diff --git a/.github/workflows/build_test_invoke.yml b/.github/workflows/build_test_invoke.yml index 594557cc9..14b501b39 100644 --- a/.github/workflows/build_test_invoke.yml +++ b/.github/workflows/build_test_invoke.yml @@ -208,6 +208,12 @@ jobs: - version: '20' type: 'Invoke' file: 'tests/integration/build_invoke/node/test_node_20.py' + - version: '22' + type: 'Test' + file: 'tests/integration/unit_test/test_unit_test_nodejs22_x.py' + - version: '22' + type: 'Invoke' + file: 'tests/integration/build_invoke/node/test_node_22.py' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/manifest-v2.json b/manifest-v2.json index 5e5f1100f..2db0c4e3c 100644 --- a/manifest-v2.json +++ b/manifest-v2.json @@ -904,6 +904,122 @@ "useCaseName": "Hello World Example" } ], + "nodejs22.x": [ + { + "directory": "nodejs22.x/hello", + "displayName": "Hello World Example", + "dependencyManager": "npm", + "appTemplate": "hello-world", + "packageType": "Zip", + "useCaseName": "Hello World Example" + }, + { + "directory": "nodejs22.x/hello-gql", + "displayName": "GraphQLApi Hello World Example", + "dependencyManager": "npm", + "appTemplate": "graphql-api-sample-app", + "packageType": "Zip", + "useCaseName": "GraphQLApi Hello World Example" + }, + { + "directory": "nodejs22.x/hello-ts", + "displayName": "Hello World Example TypeScript", + "dependencyManager": "npm", + "appTemplate": "hello-world-typescript", + "packageType": "Zip", + "useCaseName": "Hello World Example" + }, + { + "directory": "nodejs22.x/hello-ts-pt", + "displayName": "Hello World Example TypeScript With Powertools for AWS Lambda", + "dependencyManager": "npm", + "appTemplate": "hello-world-powertools-typescript", + "packageType": "Zip", + "useCaseName": "Hello World Example with Powertools for AWS Lambda" + }, + { + "directory": "nodejs22.x/step-func", + "displayName": "Step Functions Sample App (Stock Trader)", + "dependencyManager": "npm", + "appTemplate": "step-functions-sample-app", + "packageType": "Zip", + "useCaseName": "Multi-step workflow" + }, + { + "directory": "nodejs22.x/scratch", + "displayName": "Quick Start: From Scratch", + "dependencyManager": "npm", + "appTemplate": "quick-start-from-scratch", + "packageType": "Zip", + "useCaseName": "Standalone function" + }, + { + "directory": "nodejs22.x/cw-event", + "displayName": "Quick Start: Scheduled Events", + "dependencyManager": "npm", + "appTemplate": "quick-start-cloudwatch-events", + "packageType": "Zip", + "useCaseName": "Scheduled task" + }, + { + "directory": "nodejs22.x/s3", + "displayName": "Quick Start: S3", + "dependencyManager": "npm", + "appTemplate": "quick-start-s3", + "packageType": "Zip", + "useCaseName": "Data processing" + }, + { + "directory": "nodejs22.x/sns", + "displayName": "Quick Start: SNS", + "dependencyManager": "npm", + "appTemplate": "quick-start-sns", + "packageType": "Zip", + "useCaseName": "Data processing" + }, + { + "directory": "nodejs22.x/sqs", + "displayName": "Quick Start: SQS", + "dependencyManager": "npm", + "appTemplate": "quick-start-sqs", + "packageType": "Zip", + "useCaseName": "Data processing" + }, + { + "directory": "nodejs22.x/web", + "displayName": "Quick Start: Web Backend", + "dependencyManager": "npm", + "appTemplate": "quick-start-web", + "packageType": "Zip", + "useCaseName": "Serverless API" + }, + { + "directory": "nodejs22.x/full-stack", + "displayName": "Quick Start: Full Stack Application", + "dependencyManager": "npm", + "appTemplate": "quick-start-full-stack", + "packageType": "Zip", + "useCaseName": "Full Stack" + }, + { + "directory": "nodejs22.x/response-streaming", + "displayName": "Lambda function using Response Streaming", + "dependencyManager": "npm", + "appTemplate": "response-streaming", + "packageType": "Zip", + "useCaseName": "Lambda Response Streaming" + } + ], + "amazon/nodejs22.x-base": [ + { + "directory": "nodejs22.x/hello-img", + "displayName": "Hello World Image Example", + "dependencyManager": "npm", + "appTemplate": "hello-world-lambda-image", + "packageType": "Image", + "useCaseName": "Hello World Example" + } + ], "python3.8": [ { "directory": "python3.8/hello", diff --git a/nodejs22.x/cw-event/.gitignore b/nodejs22.x/cw-event/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/cw-event/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/cw-event/README.md b/nodejs22.x/cw-event/README.md new file mode 100644 index 000000000..bed29659a --- /dev/null +++ b/nodejs22.x/cw-event/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS CloudWatch Events Quick Start Application + +A cookiecutter template to create a NodeJS CloudWatch Events Quick Start Application using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x --app-template quick-start-cloudwatch-events --name cwe-app` + +> **NOTE**: ``--name`` allows you to specify a different project folder name + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/cw-event/cookiecutter.json b/nodejs22.x/cw-event/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/cw-event/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/cw-event/setup.cfg b/nodejs22.x/cw-event/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/cw-event/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/cw-event/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/.gitignore new file mode 100755 index 000000000..b512c09d4 --- /dev/null +++ b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/nodejs22.x/cw-event/{{cookiecutter.project_name}}/README.md b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..f730e0d1d --- /dev/null +++ b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,140 @@ +# {{cookiecutter.project_name}} + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) command line interface (CLI). It includes the following files and folders: + +- `src` - Code for the application's Lambda function. +- `events` - Invocation events that you can use to invoke the function. +- `__tests__` - Unit tests for the application code. +- `template.yaml` - A template that defines the application's AWS resources. + +Resources for this project are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open-source plugin for popular IDEs that uses the AWS SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds step-through debugging for Lambda function code. + +To get started, see the following: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The AWS SAM CLI is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools: + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the npm package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community). + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +## Use the AWS SAM CLI to build and test locally + +Build your application by using the `sam build` command. + +```bash +my-application$ sam build +``` + +The AWS SAM CLI installs dependencies that are defined in `package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +my-application$ sam local invoke ScheduledEventLogger --event events/event-cloudwatch-event.json +``` + +## Add a resource to your application + +The application template uses AWS SAM to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources, such as functions, triggers, and APIs. For resources that aren't included in the [AWS SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use the standard [AWS CloudFormation resource types](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html). + +Update `template.yaml` to add a dead-letter queue to your application. In the **Resources** section, add a resource named **MyQueue** with the type **AWS::SQS::Queue**. Then add a property to the **AWS::Serverless::Function** resource named **DeadLetterQueue** that targets the queue's Amazon Resource Name (ARN), and a policy that grants the function permission to access the queue. + +``` +Resources: + MyQueue: + Type: AWS::SQS::Queue + ScheduledEventLogger: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/scheduled-event-logger.scheduledEventLogger + Runtime: nodejs22.x + DeadLetterQueue: + Type: SQS + TargetArn: !GetAtt MyQueue.Arn + Policies: + - SQSSendMessagePolicy: + QueueName: !GetAtt MyQueue.QueueName +``` + +The dead-letter queue is a location for Lambda to send events that could not be processed. It's only used if you invoke your function asynchronously, but it's useful here to show how you can modify your application's resources and function configuration. + +Deploy the updated application. + +```bash +my-application$ sam deploy +``` + +Open the [**Applications**](https://console.aws.amazon.com/lambda/home#/applications) page of the Lambda console, and choose your application. When the deployment completes, view the application resources on the **Overview** tab to see the new resource. Then, choose the function to see the updated configuration that specifies the dead-letter queue. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, the AWS SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs that are generated by your Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +**NOTE:** This command works for all Lambda functions, not just the ones you deploy using AWS SAM. + +```bash +my-application$ sam logs -n ScheduledEventLogger --stack-name sam-app --tail +``` + +**NOTE:** This uses the logical name of the function within the stack. This is the correct name to use when searching logs inside an AWS Lambda function within a CloudFormation stack, even if the deployed function name varies due to CloudFormation's unique resource name generation. + +You can find more information and examples about filtering Lambda function logs in the [AWS SAM CLI documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `__tests__` folder in this project. Use `npm` to install the [Jest test framework](https://jestjs.io/) and run unit tests. + +```bash +my-application$ npm install +my-application$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +For an introduction to the AWS SAM specification, the AWS SAM CLI, and serverless application concepts, see the [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). + +Next, you can use the AWS Serverless Application Repository to deploy ready-to-use apps that go beyond Hello World samples and learn how authors developed their applications. For more information, see the [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) and the [AWS Serverless Application Repository Developer Guide](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/what-is-serverlessrepo.html). diff --git a/nodejs22.x/cw-event/{{cookiecutter.project_name}}/__tests__/unit/handlers/scheduled-event-logger.test.mjs b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/__tests__/unit/handlers/scheduled-event-logger.test.mjs new file mode 100644 index 000000000..a51fba2c1 --- /dev/null +++ b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/__tests__/unit/handlers/scheduled-event-logger.test.mjs @@ -0,0 +1,31 @@ +// Import scheduledEventLoggerHandler function from scheduled-event-logger.mjs +import { scheduledEventLoggerHandler } from '../../../src/handlers/scheduled-event-logger.mjs'; +import { jest } from '@jest/globals'; + +describe('Test for sqs-payload-logger', function () { + // This test invokes the scheduled-event-logger Lambda function and verifies that the received payload is logged + it('Verifies the payload is logged', async () => { + // Mock console.log statements so we can verify them. For more information, see + // https://jestjs.io/docs/en/mock-functions.html + console.info = jest.fn() + + // Create a sample payload with CloudWatch scheduled event message format + var payload = { + "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c", + "detail-type": "Scheduled Event", + "source": "aws.events", + "account": "", + "time": "1970-01-01T00:00:00Z", + "region": "us-west-2", + "resources": [ + "arn:aws:events:us-west-2:123456789012:rule/ExampleRule" + ], + "detail": {} + } + + await scheduledEventLoggerHandler(payload, null) + + // Verify that console.info has been called with the expected payload + expect(console.info).toHaveBeenCalledWith(JSON.stringify(payload)) + }); +}); diff --git a/nodejs22.x/cw-event/{{cookiecutter.project_name}}/buildspec.yml b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/buildspec.yml new file mode 100755 index 000000000..adb0a1019 --- /dev/null +++ b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/buildspec.yml @@ -0,0 +1,23 @@ +version: 0.2 + +phases: + install: + commands: + # Install all dependencies (including dependencies for running tests) + - npm install + pre_build: + commands: + # Discover and run unit tests in the '__tests__' directory + - npm run test + # Remove all unit tests to reduce the size of the package that will be ultimately uploaded to Lambda + - rm -rf ./__tests__ + # Remove all dependencies not needed for the Lambda deployment package (the packages from devDependencies in package.json) + - npm prune --production + build: + commands: + # Use AWS SAM to package the application by using AWS CloudFormation + - aws cloudformation package --template template.yaml --s3-bucket $S3_BUCKET --output-template template-export.yml +artifacts: + type: zip + files: + - template-export.yml diff --git a/nodejs22.x/cw-event/{{cookiecutter.project_name}}/events/event-cloudwatch-event.json b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/events/event-cloudwatch-event.json new file mode 100644 index 000000000..6c3cb7e3c --- /dev/null +++ b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/events/event-cloudwatch-event.json @@ -0,0 +1,12 @@ +{ + "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c", + "detail-type": "Scheduled Event", + "source": "aws.events", + "account": "", + "time": "1970-01-01T00:00:00Z", + "region": "us-west-2", + "resources": [ + "arn:aws:events:us-west-2:123456789012:rule/ExampleRule" + ], + "detail": {} +} diff --git a/nodejs22.x/cw-event/{{cookiecutter.project_name}}/package.json b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/package.json new file mode 100755 index 000000000..64d1cf5eb --- /dev/null +++ b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/package.json @@ -0,0 +1,24 @@ +{ + "name": "replaced-by-user-input", + "description": "replaced-by-user-input", + "version": "0.0.1", + "private": true, + "devDependencies": { + "jest": "^29.2.1" + }, + "scripts": { + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" + }, + "jest": { + "testMatch": [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[jt]s?(x)", + "**/__tests__/**/*.mjs?(x)", + "**/?(*.)+(spec|test).mjs?(x)" + ], + "moduleFileExtensions": [ + "mjs", + "js" + ] + } +} diff --git a/nodejs22.x/cw-event/{{cookiecutter.project_name}}/src/handlers/scheduled-event-logger.mjs b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/src/handlers/scheduled-event-logger.mjs new file mode 100755 index 000000000..af6443ecc --- /dev/null +++ b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/src/handlers/scheduled-event-logger.mjs @@ -0,0 +1,8 @@ +/** + * A Lambda function that logs the payload received from a CloudWatch scheduled event. + */ +export const scheduledEventLoggerHandler = async (event, context) => { + // All log statements are written to CloudWatch by default. For more information, see + // https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-logging.html + console.info(JSON.stringify(event)); +} diff --git a/nodejs22.x/cw-event/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/template.yaml new file mode 100755 index 000000000..612d6305b --- /dev/null +++ b/nodejs22.x/cw-event/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,41 @@ +# This is the SAM template that represents the architecture of your serverless application +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html + +# The AWSTemplateFormatVersion identifies the capabilities of the template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html +AWSTemplateFormatVersion: 2010-09-09 +Description: >- + {{cookiecutter.project_name}} + +# Transform section specifies one or more macros that AWS CloudFormation uses to process your template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html +Transform: +- AWS::Serverless-2016-10-31 + +# Resources declares the AWS resources that you want to include in the stack +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html +Resources: + # This is the Lambda function definition associated with the source code: sqs-payload-logger.js. For all available properties, see + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + ScheduledEventLogger: + Type: AWS::Serverless::Function + Properties: + Description: A Lambda function that logs the payload of messages sent to an associated SQS queue. + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + Handler: src/handlers/scheduled-event-logger.scheduledEventLoggerHandler + # This property associates this Lambda function with a scheduled CloudWatch Event. For all available properties, see + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#schedule + # This example runs every hour. + Events: + CloudWatchEvent: + Type: Schedule + Properties: + Schedule: cron(0 * * * ? *) + MemorySize: 128 + Timeout: 100 diff --git a/nodejs22.x/full-stack/.gitignore b/nodejs22.x/full-stack/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/full-stack/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/full-stack/README.md b/nodejs22.x/full-stack/README.md new file mode 100644 index 000000000..62e42badd --- /dev/null +++ b/nodejs22.x/full-stack/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS Quick Start Web Application + +A cookiecutter template to create a NodeJS Quick Start Web Application using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x --app-template quick-start-full-stack --name full-stack-app` + +> **NOTE**: ``--name`` allows you to specify a different project folder name + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/full-stack/cookiecutter.json b/nodejs22.x/full-stack/cookiecutter.json new file mode 100644 index 000000000..c77e45adf --- /dev/null +++ b/nodejs22.x/full-stack/cookiecutter.json @@ -0,0 +1,11 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore", + "*.vue" + ] +} \ No newline at end of file diff --git a/nodejs22.x/full-stack/setup.cfg b/nodejs22.x/full-stack/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/full-stack/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..e65dcb1ff --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,390 @@ +# Created by https://www.toptal.com/developers/gitignore/api/osx,sam,linux,python,windows,node +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,sam,linux,python,windows,node + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,sam,linux,python,windows,node \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/12 - Full Stack.png b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/12 - Full Stack.png new file mode 100644 index 000000000..b2ab61429 Binary files /dev/null and b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/12 - Full Stack.png differ diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/3-Serverless-API.png b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/3-Serverless-API.png new file mode 100644 index 000000000..0e4313c06 Binary files /dev/null and b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/3-Serverless-API.png differ diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/README.md b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..17c6d2267 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,246 @@ +# {{cookiecutter.project_name}} + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) command line interface (CLI). It includes the following files and folders: + +- `src` - Code for the application's Lambda function. +- `events` - Invocation events that you can use to invoke the function. +- `__tests__` - Unit tests for the application code. +- `template.yaml` - A template that defines the application's AWS resources. + +The application uses several AWS resources, including Lambda functions, an API Gateway API, an S3 Bucket with a CloudFront Distribution and Amazon DynamoDB tables. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +This template addresses the challenge developers can face when adding a front-end to a standard serverless backend. See below for a diagram that represents a standard serverless backend: + +![Standard](./3-Serverless-API.png) + +This template adds the following components to the standard serverless backend which provides the foundation for further development of a single page app frontend to allow your users to interact with your api: + +![Full-Stack](./12%20-%20Full%20Stack.png) + +How does it work? When the full stack is deployed, the end result is a single page web application hosted on S3/CloudFront that allows users to interact with the serverless API that is created by this project. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open-source plugin for popular IDEs that uses the AWS SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds step-through debugging for Lambda function code. + +To get started, see the following: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The AWS SAM CLI is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools: + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the npm package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community). + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +The following outputs will be displayed in the outputs when the deployment is complete: +* API Gateway endpoint API +* CloudFront Distribution ID +* CloudFront domain name +* S3 Bucket for Front End source files + +## Deploy the Front End +For convenience, the included deploy_frontend.sh bash script can be run to automatically deploy your front end website to your AWS account. Run using the following command: +```bash +./deploy_frontend.sh +``` + +## Regarding CORS +For security, it is recommended to restrict the Allowed Origin value to restrict HTTP requests that are initiated from scripts running in the browser. See here for more information: +* Configuring CORS for an HTTP API - [Configuring CORS for an HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-cors.html). + +## Use the AWS SAM CLI to build and test locally + +Build your application by using the `sam build` command. + +```bash +my-application$ sam build +``` + +The AWS SAM CLI installs dependencies that are defined in `package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +my-application$ sam local invoke putItemFunction --event events/event-post-item.json +my-application$ sam local invoke getAllItemsFunction --event events/event-get-all-items.json +``` + +The AWS SAM CLI can also emulate your application's API. Use the `sam local start-api` command to run the API locally on port 3000. + +```bash +my-application$ sam local start-api +my-application$ curl http://localhost:3000/ +``` + +The AWS SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. It also includes a reference to the API Gateway that is also deployed as part of this application. + +```yaml + Events: + Api: + Type: Api + Properties: + Path: / + Method: GET + RestApiId: + Ref: ApiGatewayApi +``` + +## Test locally with dynamodb: +1. Start DynamoDB Local in a Docker container (this example works on codespace) +``` +docker run --rm -p 8000:8000 -v /tmp:/data amazon/dynamodb-local +``` +2. Create the DynamoDB table (sample command below): +``` +aws dynamodb create-table --table-name SampleTable --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --billing-mode PAY_PER_REQUEST --endpoint-url http://127.0.0.1:8000 +``` +3. Retrieve the ip address of your docker container running dynamodb local: +``` +docker inspect -f {% raw %} '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' {% endraw %} +``` +4. Update env.json with the IP of your docker container for the endpoint override - see here for example: +``` +{ + "getByIdFunction": { + "ENDPOINT_OVERRIDE": "http://172.17.0.2:8000", + "SAMPLE_TABLE": "SampleTable" + }, + "putItemFunction": { + "ENDPOINT_OVERRIDE": "http://172.17.0.2:8000", + "SAMPLE_TABLE": "SampleTable" + } +} +``` +5. run the following commands to start the sam local api: +``` +sam local start-api --env-vars env.json --host 0.0.0.0 --debug +``` +6. For testing - you can put an item into dynamodb local +``` +aws dynamodb put-item \ + --table-name SampleTable \ + --item '{"id": {"S": "A1234"}, "name": {"S": "randeepx"}}' \ + --endpoint-url http://127.0.0.1:8000 +``` +7. How to scan your table for items +``` +aws dynamodb scan --table-name SampleTable --endpoint-url http://127.0.0.1:8000 +``` +8. To run frontend application locally: +Go to your `frontend` code directory + ``` +cd frontend +``` +Make backend API endpoint accessible as an environment variable. For local, create a `.env` file, Here is an example: +``` +VUE_APP_API_ENDPOINT=http://127.0.0.1:3000/ +``` +9. run following command to compile and run (with hot-reloads) for development +``` +npm run serve +``` +10. to execute frontend unit test +``` +npm run test +``` + + +## Add a resource to your application +The application template uses AWS SAM to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources, such as functions, triggers, and APIs. For resources that aren't included in the [AWS SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use the standard [AWS CloudFormation resource types](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html). + +Update `template.yaml` to add a dead-letter queue to your application. In the **Resources** section, add a resource named **MyQueue** with the type **AWS::SQS::Queue**. Then add a property to the **AWS::Serverless::Function** resource named **DeadLetterQueue** that targets the queue's Amazon Resource Name (ARN), and a policy that grants the function permission to access the queue. + +``` +Resources: + MyQueue: + Type: AWS::SQS::Queue + getAllItemsFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/get-all-items.getAllItemsHandler + Runtime: nodejs22.x + DeadLetterQueue: + Type: SQS + TargetArn: !GetAtt MyQueue.Arn + Policies: + - SQSSendMessagePolicy: + QueueName: !GetAtt MyQueue.QueueName +``` + +The dead-letter queue is a location for Lambda to send events that could not be processed. It's only used if you invoke your function asynchronously, but it's useful here to show how you can modify your application's resources and function configuration. + +Deploy the updated application. + +```bash +my-application$ sam deploy +``` + +Open the [**Applications**](https://console.aws.amazon.com/lambda/home#/applications) page of the Lambda console, and choose your application. When the deployment completes, view the application resources on the **Overview** tab to see the new resource. Then, choose the function to see the updated configuration that specifies the dead-letter queue. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, the AWS SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs that are generated by your Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +**NOTE:** This command works for all Lambda functions, not just the ones you deploy using AWS SAM. + +```bash +my-application$ sam logs -n putItemFunction --stack-name sam-app --tail +``` + +**NOTE:** This uses the logical name of the function within the stack. This is the correct name to use when searching logs inside an AWS Lambda function within a CloudFormation stack, even if the deployed function name varies due to CloudFormation's unique resource name generation. + +You can find more information and examples about filtering Lambda function logs in the [AWS SAM CLI documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `__tests__` folder in this project. Use `npm` to install the [Jest test framework](https://jestjs.io/) and run unit tests. + +```bash +my-application$ npm install +my-application$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +For an introduction to the AWS SAM specification, the AWS SAM CLI, and serverless application concepts, see the [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). + +Next, you can use the AWS Serverless Application Repository to deploy ready-to-use apps that go beyond Hello World samples and learn how authors developed their applications. For more information, see the [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) and the [AWS Serverless Application Repository Developer Guide](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/what-is-serverlessrepo.html). diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/get-all-items.test.mjs b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/get-all-items.test.mjs new file mode 100644 index 000000000..063c71b71 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/get-all-items.test.mjs @@ -0,0 +1,43 @@ +// Import getAllItemsHandler function from get-all-items.mjs +import { getAllItemsHandler } from '../../../src/handlers/get-all-items.mjs'; +// Import dynamodb from aws-sdk +import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb'; +import { mockClient } from "aws-sdk-client-mock"; + +// This includes all tests for getAllItemsHandler() +describe('Test getAllItemsHandler', () => { + const ddbMock = mockClient(DynamoDBDocumentClient); + + beforeEach(() => { + ddbMock.reset(); + }); + + it('should return ids', async () => { + const items = [{ id: 'id1' }, { id: 'id2' }]; + + // Return the specified value whenever the spied scan function is called + ddbMock.on(ScanCommand).resolves({ + Items: items, + }); + + const event = { + httpMethod: 'GET' + }; + + // Invoke helloFromLambdaHandler() + const result = await getAllItemsHandler(event); + + const expectedResult = { + statusCode: 200, + headers: { + "Access-Control-Allow-Headers" : "Content-Type", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "OPTIONS,POST,GET" + }, + body: JSON.stringify(items) + }; + + // Compare the result with the expected result + expect(result).toEqual(expectedResult); + }); +}); diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/get-by-id.test.mjs b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/get-by-id.test.mjs new file mode 100644 index 000000000..1b2d1e4b8 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/get-by-id.test.mjs @@ -0,0 +1,48 @@ +// Import getByIdHandler function from get-by-id.mjs +import { getByIdHandler } from '../../../src/handlers/get-by-id.mjs'; +// Import dynamodb from aws-sdk +import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'; +import { mockClient } from "aws-sdk-client-mock"; + +// This includes all tests for getByIdHandler() +describe('Test getByIdHandler', () => { + const ddbMock = mockClient(DynamoDBDocumentClient); + + beforeEach(() => { + ddbMock.reset(); + }); + + // This test invokes getByIdHandler() and compare the result + it('should get item by id', async () => { + const item = { id: 'id1' }; + + // Return the specified value whenever the spied get function is called + ddbMock.on(GetCommand).resolves({ + Item: item, + }); + + const event = { + httpMethod: 'GET', + pathParameters: { + id: 'id1' + } + }; + + // Invoke getByIdHandler() + const result = await getByIdHandler(event); + + const expectedResult = { + statusCode: 200, + headers: { + "Access-Control-Allow-Headers" : "Content-Type", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "OPTIONS,POST,GET" + }, + body: JSON.stringify(item) + }; + + // Compare the result with the expected result + expect(result).toEqual(expectedResult); + }); +}); + \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/put-item.test.mjs b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/put-item.test.mjs new file mode 100644 index 000000000..03acd5288 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/__tests__/unit/handlers/put-item.test.mjs @@ -0,0 +1,45 @@ +// Import putItemHandler function from put-item.mjs +import { putItemHandler } from '../../../src/handlers/put-item.mjs'; +// Import dynamodb from aws-sdk +import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; +import { mockClient } from "aws-sdk-client-mock"; +// This includes all tests for putItemHandler() +describe('Test putItemHandler', function () { + const ddbMock = mockClient(DynamoDBDocumentClient); + + beforeEach(() => { + ddbMock.reset(); + }); + + // This test invokes putItemHandler() and compare the result + it('should add id to the table', async () => { + const returnedItem = { id: 'id1', name: 'name1' }; + + // Return the specified value whenever the spied put function is called + ddbMock.on(PutCommand).resolves({ + returnedItem + }); + + const event = { + httpMethod: 'POST', + body: '{"id": "id1","name": "name1"}' + }; + + // Invoke putItemHandler() + const result = await putItemHandler(event); + + const expectedResult = { + statusCode: 200, + headers: { + "Access-Control-Allow-Headers" : "Content-Type", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "OPTIONS,POST,GET" + }, + body: JSON.stringify(returnedItem) + }; + + // Compare the result with the expected result + expect(result).toEqual(expectedResult); + }); +}); + \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/package.json b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/package.json new file mode 100644 index 000000000..11e7ea689 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/package.json @@ -0,0 +1,30 @@ +{ + "name": "delete-test-01", + "description": "delete-test-01-description", + "version": "0.0.1", + "private": true, + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.398.0", + "@aws-sdk/lib-dynamodb": "^3.398.0" + }, + "devDependencies": { + "aws-sdk-client-mock": "^2.0.0", + "jest": "^29.2.1" + }, + "scripts": { + "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js" + }, + "jest": { + "testMatch": [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[jt]s?(x)", + "**/__tests__/**/*.mjs?(x)", + "**/?(*.)+(spec|test).mjs?(x)" + ], + "moduleFileExtensions": [ + "mjs", + "js" + ] + } +} + diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/get-all-items.mjs b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/get-all-items.mjs new file mode 100644 index 000000000..3ff565ce9 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/get-all-items.mjs @@ -0,0 +1,66 @@ +// Create clients and set shared const values outside of the handler. + +// Create a DocumentClient that represents the query to add an item +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb'; + +//DynamoDB Endpoint +const ENDPOINT_OVERRIDE = process.env.ENDPOINT_OVERRIDE; +let ddbClient = undefined; + +if (ENDPOINT_OVERRIDE) { + ddbClient = new DynamoDBClient({ endpoint: ENDPOINT_OVERRIDE }); +} +else { + ddbClient = new DynamoDBClient({}); // Use default values for DynamoDB endpoint + console.warn("No value for ENDPOINT_OVERRIDE provided for DynamoDB, using default"); +} + +const ddbDocClient = DynamoDBDocumentClient.from(ddbClient); + +// Get the DynamoDB table name from environment variables +const tableName = process.env.SAMPLE_TABLE; + +/** + * A simple example includes a HTTP get method to get all items from a DynamoDB table. + */ +export const getAllItemsHandler = async (event) => { + if (event.httpMethod !== 'GET') { + throw new Error(`getAllItems only accept GET method, you tried: ${event.httpMethod}`); + } + // All log statements are written to CloudWatch + console.info('received:', event); + + // get all items from the table (only first 1MB data, you can use `LastEvaluatedKey` to get the rest of data) + // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property + // https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html + var params = { + TableName : tableName + }; + + try { + const data = await ddbDocClient.send(new ScanCommand(params)); + var items = data.Items; + } catch (err) { + console.error("Error retrieving all items:", err.message); + console.error("Error code:", err.code); + console.error("Error name:", err.name); + console.error("Error stack:", err.stack); + + throw err; + } + + const response = { + statusCode: 200, + headers: { + "Access-Control-Allow-Headers" : "Content-Type", + "Access-Control-Allow-Origin": "*", //DO NOT USE THIS VALUE IN PRODUCTION - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-cors.html + "Access-Control-Allow-Methods": "OPTIONS,POST,GET" + }, + body: JSON.stringify(items) + }; + + // All log statements are written to CloudWatch + console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); + return response; +} diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/get-by-id.mjs b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/get-by-id.mjs new file mode 100644 index 000000000..8c97ccfbf --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/get-by-id.mjs @@ -0,0 +1,69 @@ +// Create clients and set shared const values outside of the handler. + +// Create a DocumentClient that represents the query to add an item +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'; + +//DynamoDB Endpoint +const ENDPOINT_OVERRIDE = process.env.ENDPOINT_OVERRIDE; +let ddbClient = undefined; + +if (ENDPOINT_OVERRIDE) { + ddbClient = new DynamoDBClient({ endpoint: ENDPOINT_OVERRIDE }); +} +else{ + ddbClient = new DynamoDBClient({}); // Use default values for DynamoDB endpoint + console.warn("No value for ENDPOINT_OVERRIDE provided for DynamoDB, using default"); +} + +const ddbDocClient = DynamoDBDocumentClient.from(ddbClient); + +// Get the DynamoDB table name from environment variables +const tableName = process.env.SAMPLE_TABLE; + +/** + * A simple example includes a HTTP get method to get one item by id from a DynamoDB table. + */ +export const getByIdHandler = async (event) => { + if (event.httpMethod !== 'GET') { + throw new Error(`getMethod only accept GET method, you tried: ${event.httpMethod}`); + } + // All log statements are written to CloudWatch + console.info('received:', event); + + // Get id from pathParameters from APIGateway because of `/{id}` at template.yaml + const id = event.pathParameters.id; + + // Get the item from the table + // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#get-property + var params = { + TableName : tableName, + Key: { id: id }, + }; + + try { + const data = await ddbDocClient.send(new GetCommand(params)); + var item = data.Item; + } catch (err) { + console.error("Error retrieving item:", err.message); + console.error("Error code:", err.code); + console.error("Error name:", err.name); + console.error("Error stack:", err.stack); + + throw err; + } + + const response = { + statusCode: 200, + headers: { + "Access-Control-Allow-Headers" : "Content-Type", + "Access-Control-Allow-Origin": "*", //DO NOT USE THIS VALUE IN PRODUCTION - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-cors.html + "Access-Control-Allow-Methods": "OPTIONS,POST,GET" + }, + body: JSON.stringify(item) + }; + + // All log statements are written to CloudWatch + console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); + return response; +} diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/put-item.mjs b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/put-item.mjs new file mode 100644 index 000000000..f92d38950 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/backend/src/handlers/put-item.mjs @@ -0,0 +1,71 @@ +// Create clients and set shared const values outside of the handler. + +// Create a DocumentClient that represents the query to add an item +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; + +//DynamoDB Endpoint +const ENDPOINT_OVERRIDE = process.env.ENDPOINT_OVERRIDE; +let ddbClient = undefined; + +if (ENDPOINT_OVERRIDE) { + ddbClient = new DynamoDBClient({ endpoint: ENDPOINT_OVERRIDE }); +} +else { + ddbClient = new DynamoDBClient({}); // Use default values for DynamoDB endpoint + console.warn("No value for ENDPOINT_OVERRIDE provided for DynamoDB, using default"); +} + +const ddbDocClient = DynamoDBDocumentClient.from(ddbClient); + +// Get the DynamoDB table name from environment variables +const tableName = process.env.SAMPLE_TABLE; + +/** + * A simple example includes a HTTP post method to add one item to a DynamoDB table. + */ +export const putItemHandler = async (event) => { + if (event.httpMethod !== 'POST') { + throw new Error(`postMethod only accepts POST method, you tried: ${event.httpMethod} method.`); + } + // All log statements are written to CloudWatch + console.info('received:', event); + + // Get id and name from the body of the request + const body = JSON.parse(event.body); + const id = body.id; + const name = body.name; + + // Creates a new item, or replaces an old item with a new item + // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#put-property + var params = { + TableName : tableName, + Item: { id : id, name: name } + }; + + try { + const data = await ddbDocClient.send(new PutCommand(params)); + console.log("Success - item added or updated", data); + } catch (err) { + console.error("Error adding or updating item:", err.message); + console.error("Error code:", err.code); + console.error("Error name:", err.name); + console.error("Error stack:", err.stack); + + throw err; + } + + const response = { + statusCode: 200, + headers: { + "Access-Control-Allow-Headers" : "Content-Type", + "Access-Control-Allow-Origin": "*", //DO NOT USE THIS VALUE IN PRODUCTION - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-cors.html + "Access-Control-Allow-Methods": "OPTIONS,POST,GET" + }, + body: JSON.stringify(body) + }; + + // All log statements are written to CloudWatch + console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); + return response; +}; diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/deploy_frontend.ps1 b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/deploy_frontend.ps1 new file mode 100644 index 000000000..52d39cdff --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/deploy_frontend.ps1 @@ -0,0 +1,49 @@ +# Get user input for stack name +$stack_name = Read-Host "Enter the name of the CloudFormation stack:" + +# Get the API Gateway URL from the stack +$api_gateway_endpoint = aws cloudformation describe-stacks --stack-name $stack_name --query "Stacks[0].Outputs[?OutputKey=='APIGatewayEndpoint'].OutputValue" --output text + +# Get the CloudFront Distribution ID from the stack +$cloudfront_distribution_id = aws cloudformation describe-stacks --stack-name $stack_name --query "Stacks[0].Outputs[?OutputKey=='CloudFrontDistributionId'].OutputValue" --output text + +# Get the S3 Bucket Name from the stack +$s3_bucket_name = aws cloudformation describe-stacks --stack-name $stack_name --query "Stacks[0].Outputs[?OutputKey=='WebS3BucketName'].OutputValue" --output text + +# Output the results +Write-Host "API Gateway URL: $api_gateway_endpoint" +Write-Host "CloudFront Distribution ID: $cloudfront_distribution_id" +Write-Host "S3 Bucket Name: $s3_bucket_name" + +# Move to frontend and install +cd frontend/ +npm install + +# Create .env file for building distribution with API Gateway Endpoint defined +New-Item -ItemType File -Path ".env" + +# Add the API Gateway endpoint to the .env file +"`nVUE_APP_API_ENDPOINT=$api_gateway_endpoint" | Out-File -Append ".env" + +# Confirm that the endpoint has been added to the .env file +Write-Host "The API Gateway endpoint has been added to the .env file:" +Get-Content ".env" + +# Create distribution for deployment +npm run build +cd dist/ + +# Sync distribution with S3 +aws s3 sync . "s3://$s3_bucket_name/" + +# Create cloudfront invalidation and capture id for next step +$invalidation_output = aws cloudfront create-invalidation --distribution-id $cloudfront_distribution_id --paths "/*" +$invalidation_id = $invalidation_output | Select-String -Pattern '(?<=Id": ")[^"]+' | ForEach-Object { $_.Matches.Value } + +# Wait for cloudfront invalidation to complete +aws cloudfront wait invalidation-completed --distribution-id $cloudfront_distribution_id --id $invalidation_id + +# Get cloudfront domain name and validate +$cloudfront_domain_name = aws cloudfront list-distributions --query "DistributionList.Items[?Id=='$cloudfront_distribution_id'].DomainName" --output text + +Write-Host "The invalidation is now complete - please visit your cloudfront URL to test: $cloudfront_domain_name" \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/deploy_frontend.sh b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/deploy_frontend.sh new file mode 100755 index 000000000..e50c37941 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/deploy_frontend.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Get user input for stack name +read -p "Enter the name of the CloudFormation stack: " stack_name + +# Get the API Gateway URL from the stack +api_gateway_endpoint=$(aws cloudformation describe-stacks --stack-name "$stack_name" --query "Stacks[0].Outputs[?OutputKey=='APIGatewayEndpoint'].OutputValue" --output text) + +# Get the CloudFront Distribution ID from the stack +cloudfront_distribution_id=$(aws cloudformation describe-stacks --stack-name "$stack_name" --query "Stacks[0].Outputs[?OutputKey=='CloudFrontDistributionId'].OutputValue" --output text) + +# Get the S3 Bucket Name from the stack +s3_bucket_name=$(aws cloudformation describe-stacks --stack-name "$stack_name" --query "Stacks[0].Outputs[?OutputKey=='WebS3BucketName'].OutputValue" --output text) + +# Output the results +echo "API Gateway URL: $api_gateway_endpoint" +echo "CloudFront Distribution ID: $cloudfront_distribution_id" +echo "S3 Bucket Name: $s3_bucket_name" + +# Move to frontend and install +cd frontend/ && npm install + +# Create .env file for building distribtuion with API Gateway Endpoint defined +touch .env + +# Add the API Gateway endpoint to the .env file +echo "VUE_APP_API_ENDPOINT=$api_gateway_endpoint" >> .env + +# Confirm that the endpoint has been added to the .env file +echo "The API Gateway endpoint has been added to the .env file:" +cat .env + +# Create distribution for deployment +npm run build && cd dist/ + +# Sync distribution with S3 +aws s3 sync . s3://$s3_bucket_name/ + +# Create cloudfront invalidation and capture id for next step +invalidation_output=$(aws cloudfront create-invalidation --distribution-id $cloudfront_distribution_id --paths "/*") +invalidation_id=$(echo "$invalidation_output" | grep -oP '(?<="Id": ")[^"]+') + +# Wait for cloudfront invalidation to complete +aws cloudfront wait invalidation-completed --distribution-id $cloudfront_distribution_id --id $invalidation_id + +# Get cloudfront domain name and validate +cloudfront_domain_name=$(aws cloudfront list-distributions --query "DistributionList.Items[?Id=='$cloudfront_distribution_id'].DomainName" --output text) + +echo "The invalidation is now complete - please visit your cloudfront URL to test: $cloudfront_domain_name" diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/env.json b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/env.json new file mode 100644 index 000000000..947de1f76 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/env.json @@ -0,0 +1,14 @@ +{ + "getAllItemsFunction": { + "ENDPOINT_OVERRIDE": "", + "SAMPLE_TABLE": "" + }, + "getByIdFunction": { + "ENDPOINT_OVERRIDE": "", + "SAMPLE_TABLE": "" + }, + "putItemFunction": { + "ENDPOINT_OVERRIDE": "", + "SAMPLE_TABLE": "" + } + } \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-get-all-items.json b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-get-all-items.json new file mode 100644 index 000000000..3a0cb5f77 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-get-all-items.json @@ -0,0 +1,3 @@ +{ + "httpMethod": "GET" +} \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-get-by-id.json b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-get-by-id.json new file mode 100644 index 000000000..63a64fb45 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-get-by-id.json @@ -0,0 +1,6 @@ +{ + "httpMethod": "GET", + "pathParameters": { + "id": "id1" + } +} \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-post-item.json b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-post-item.json new file mode 100644 index 000000000..6367003e5 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/events/event-post-item.json @@ -0,0 +1,4 @@ +{ + "httpMethod": "POST", + "body": "{\"id\": \"id1\",\"name\": \"name1\"}" +} \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/.gitignore b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/.gitignore new file mode 100644 index 000000000..20cffc7ca --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/.gitignore @@ -0,0 +1,24 @@ +.DS_Store +node_modules +/dist +/test/__snapshots__ +package-lock.json + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/LICENSE b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/LICENSE new file mode 100644 index 000000000..7c41feeec --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/LICENSE @@ -0,0 +1,14 @@ +Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/README.md b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/README.md new file mode 100644 index 000000000..de3b2cf12 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/README.md @@ -0,0 +1,36 @@ +# SAM starter Front End Application + +## Project setup +``` +npm install +``` + +### Compiles and hot-reloads for development +``` +npm run serve +``` + +### Compiles and minifies for production +``` +npm run build +``` + +### Lints and fixes files +``` +npm run lint +``` + +### Runs unit tests +``` +npm run test +``` + +### Connect to Backend + +Make backend API endpoint accessible as an environment variable. For local, create a `.env` file, Here is an example: +``` +VUE_APP_API_ENDPOINT=http://127.0.0.1:3000/ +``` + +### Customize configuration +See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/babel.config.js b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/babel.config.js new file mode 100644 index 000000000..e9558405f --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ] +} diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/package.json b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/package.json new file mode 100644 index 000000000..165afa073 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/package.json @@ -0,0 +1,55 @@ +{ + "name": "sam-starter-fontend", + "version": "1.0.0", + "description": "Frontend app for the Innovator Island workshop.", + "author": "Randeep Singh , Ben Peterson ", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint", + "test": "vitest" + }, + "dependencies": { + "axios": "^1.6.0", + "core-js": "^3.18.1", + "vue": "^3.0.0" + }, + "devDependencies": { + "@testing-library/vue": "^7.0.0", + "@vue/cli-plugin-babel": "^5.0.8", + "@vue/cli-plugin-eslint": "^5.0.8", + "@vitejs/plugin-vue": "^4.2.3", + "@vue/cli-service": "^5.0.8", + "@vue/compiler-sfc": "^3.0.0", + "babel-eslint": "^10.1.0", + "eslint": "^7.32.0", + "eslint-plugin-vue": "^7.18.0", + "happy-dom": "^15.10.2", + "vitest": "^0.31.1", + "vue-template-compiler": "^2.6.14" + }, + "eslintConfig": { + "root": true, + "env": { + "node": true + }, + "extends": [ + "plugin:vue/vue3-essential", + "eslint:recommended" + ], + "parserOptions": { + "parser": "babel-eslint" + }, + "rules": {} + }, + "prettier": { + "semi": false, + "singleQuote": true + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead" + ] +} diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/favicon.ico b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/favicon.ico new file mode 100644 index 000000000..b7fdc77dc Binary files /dev/null and b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/favicon.ico differ diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/index.html b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/index.html new file mode 100644 index 000000000..3e5a13962 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + <%= htmlWebpackPlugin.options.title %> + + + +
+ + + diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/logo.png b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/logo.png new file mode 100644 index 000000000..56a3af614 Binary files /dev/null and b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/public/logo.png differ diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/App.vue b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/App.vue new file mode 100644 index 000000000..b94b48055 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/App.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/CreateItem.vue b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/CreateItem.vue new file mode 100644 index 000000000..17507de2b --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/CreateItem.vue @@ -0,0 +1,54 @@ + + + + + \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/GetItemById.vue b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/GetItemById.vue new file mode 100644 index 000000000..6f67b7c72 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/GetItemById.vue @@ -0,0 +1,54 @@ + + + + + \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/GetItems.vue b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/GetItems.vue new file mode 100644 index 000000000..bdc9f11e6 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/components/GetItems.vue @@ -0,0 +1,42 @@ + + + + + \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/main.js b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/main.js new file mode 100644 index 000000000..01433bca2 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/src/main.js @@ -0,0 +1,4 @@ +import { createApp } from 'vue' +import App from './App.vue' + +createApp(App).mount('#app') diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/CreateItem.test.js b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/CreateItem.test.js new file mode 100644 index 000000000..f5bbbfa4b --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/CreateItem.test.js @@ -0,0 +1,36 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import CreateItem from '../src/components/CreateItem.vue' + +describe('CreateItem', () => { + + it('has proper input fileds', () => { + const wrapper = mount(CreateItem) + + const userId = wrapper.find('#userId') + expect(userId.element.id).toBe('userId') + + const userName = wrapper.find('#userName') + expect(userName.element.id).toBe('userName') + + const button = wrapper.find('button') + expect(button.exists()).toBe(true) + }) + + it('accepts input values', () => { + const wrapper = mount(CreateItem) + + const userId = wrapper.find('#userId') + userId.setValue('A123') + + const userName = wrapper.find('#userName') + userName.setValue('John Doe') + + + expect(userId.element.value).toBe('A123') + expect(userName.element.value).toBe('John Doe') + + // const button = wrapper.find('button') + // button.trigger('click') + }) +}) \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/GetItemById.test.js b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/GetItemById.test.js new file mode 100644 index 000000000..bb101c96b --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/GetItemById.test.js @@ -0,0 +1,28 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import GetItemById from '../src/components/GetItemById.vue' + +describe('GetItemById', () => { + + it('has proper input fileds', () => { + const wrapper = mount(GetItemById) + + const userId = wrapper.find('#userId') + expect(userId.element.id).toBe('userId') + + const button = wrapper.find('button') + expect(button.exists()).toBe(true) + }) + + it('accepts input values', () => { + const wrapper = mount(GetItemById) + + const userId = wrapper.find('#userId') + userId.setValue('A123') + + expect(userId.element.value).toBe('A123') + + // const button = wrapper.find('button') + // button.trigger('click') + }) +}) \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/GetItems.test.js b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/GetItems.test.js new file mode 100644 index 000000000..1005e4252 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/test/GetItems.test.js @@ -0,0 +1,20 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import GetItems from '../src/components/GetItems.vue' + +describe('GetItems', () => { + + it('has proper input fileds', () => { + const wrapper = mount(GetItems) + + const button = wrapper.find('button') + expect(button.exists()).toBe(true) + }) + + it('accepts input values', () => { + const wrapper = mount(GetItems) + + // const button = wrapper.find('button') + // button.trigger('click') + }) +}) \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/vite.config.js b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/vite.config.js new file mode 100644 index 000000000..c74213279 --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/vite.config.js @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig ({ + plugins: [ + vue(), + ], + // ... + test: { + // enable jest-like global test APIs + globals: true, + // simulate DOM with happy-dom + environment: 'happy-dom' + } +}) diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/vue.config.js b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/vue.config.js new file mode 100644 index 000000000..1c5377cfe --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/frontend/vue.config.js @@ -0,0 +1,3 @@ +module.exports = { + runtimeCompiler: true +} \ No newline at end of file diff --git a/nodejs22.x/full-stack/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..3a711301b --- /dev/null +++ b/nodejs22.x/full-stack/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,244 @@ +# This is the SAM template that represents the architecture of your serverless application +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html + +# The AWSTemplateFormatVersion identifies the capabilities of the template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html +AWSTemplateFormatVersion: 2010-09-09 +Description: >- + {{cookiecutter.project_name}} +# Transform section specifies one or more macros that AWS CloudFormation uses to process your template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html +Transform: +- AWS::Serverless-2016-10-31 + +# Resources declares the AWS resources that you want to include in the stack +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html +Resources: + # Each Lambda function is defined by properties: + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + + # This is an API gateway associated with the getByIdFunction and putItemFunctions + ApiGatewayApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + Cors: + AllowMethods: "'OPTIONS, POST, GET'" + AllowHeaders: "'Content-Type'" + AllowOrigin: "'*'" #DO NOT USE THIS VALUE IN PRODUCTION - https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-cors.html + + # This is a Lambda function config associated with the source code: get-by-id.js + getAllItemsFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: backend/ + Handler: src/handlers/get-all-items.getAllItemsHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + MemorySize: 128 + Timeout: 100 + Description: A simple example includes a HTTP get method to get all items by id from a DynamoDB table. + Policies: + # Give Create/Read/Update/Delete Permissions to the SampleTable + - DynamoDBCrudPolicy: + TableName: !Ref SampleTable + Environment: + Variables: + # Make table name accessible as environment variable from function code during execution + SAMPLE_TABLE: !Ref SampleTable + # Make DynamoDB endpoint accessible as environment variable from function code during execution + ENDPOINT_OVERRIDE: "" + Events: + Api: + Type: Api + Properties: + Path: / + Method: GET + RestApiId: + Ref: ApiGatewayApi + # Each Lambda function is defined by properties: + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + + # This is a Lambda function config associated with the source code: get-by-id.js + getByIdFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: backend/ + Handler: src/handlers/get-by-id.getByIdHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + MemorySize: 128 + Timeout: 100 + Description: A simple example includes a HTTP get method to get one item by id from a DynamoDB table. + Policies: + # Give Create/Read/Update/Delete Permissions to the SampleTable + - DynamoDBCrudPolicy: + TableName: !Ref SampleTable + Environment: + Variables: + # Make table name accessible as environment variable from function code during execution + SAMPLE_TABLE: !Ref SampleTable + # Make DynamoDB endpoint accessible as environment variable from function code during execution + ENDPOINT_OVERRIDE: "" + Events: + Api: + Type: Api + Properties: + Path: /{id} + Method: GET + RestApiId: + Ref: ApiGatewayApi + # Each Lambda function is defined by properties: + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + + # This is a Lambda function config associated with the source code: put-item.js + putItemFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: backend/ + Handler: src/handlers/put-item.putItemHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + MemorySize: 128 + Timeout: 100 + Description: A simple example includes a HTTP post method to add one item to a DynamoDB table. + Policies: + # Give Create/Read/Update/Delete Permissions to the SampleTable + - DynamoDBCrudPolicy: + TableName: !Ref SampleTable + Environment: + Variables: + SAMPLE_TABLE: !Ref SampleTable + # Make DynamoDB endpoint accessible as environment variable from function code during execution + ENDPOINT_OVERRIDE: "" + Events: + Api: + Type: Api + Properties: + Path: / + Method: POST + RestApiId: + Ref: ApiGatewayApi + # Simple syntax to create a DynamoDB table with a single attribute primary key, more in + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlesssimpletable + + # DynamoDB table to store item: {id: <ID>, name: <NAME>} + SampleTable: + Type: AWS::Serverless::SimpleTable + Properties: + PrimaryKey: + Name: id + Type: String + ProvisionedThroughput: + ReadCapacityUnits: 2 + WriteCapacityUnits: 2 + + # S3 Bucket to host single page app website + WebSiteBucket: + Type: "AWS::S3::Bucket" + Properties: + BucketEncryption: + ServerSideEncryptionConfiguration: + - BucketKeyEnabled: true + VersioningConfiguration: + Status: Enabled + WebSiteBucketPolicy: + Type: "AWS::S3::BucketPolicy" + Properties: + Bucket: !Ref WebSiteBucket + PolicyDocument: + Version: "2012-10-17" + Id: "PolicyForCloudFrontPrivateContent" + Statement: + - Sid: "AllowCloudFrontServicePrincipal" + Effect: "Allow" + Principal: + Service: "cloudfront.amazonaws.com" + Action: "s3:GetObject" + Resource: !Join [ "", [ "arn:aws:s3:::", !Ref WebSiteBucket, "/*" ] ] + Condition: + StringEquals: + "AWS:SourceArn": !Join [ "", [ "arn:aws:cloudfront::", !Ref "AWS::AccountId", ":distribution/", !Ref CloudFrontDistribution ] ] + + # CloudFront Distribution for hosting the single page app website + CloudFrontDistribution: + Type: "AWS::CloudFront::Distribution" + Properties: + DistributionConfig: + Origins: + - DomainName: !GetAtt WebSiteBucket.RegionalDomainName + Id: "myS3Origin" + OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id + S3OriginConfig: + OriginAccessIdentity: "" + Enabled: true + DefaultRootObject: "index.html" + HttpVersion: "http2" + DefaultCacheBehavior: + AllowedMethods: + - "DELETE" + - "GET" + - "HEAD" + - "OPTIONS" + - "PATCH" + - "POST" + - "PUT" + CachedMethods: + - "GET" + - "HEAD" + TargetOriginId: "myS3Origin" + ForwardedValues: + QueryString: false + Cookies: + Forward: "none" + ViewerProtocolPolicy: "allow-all" + MinTTL: 0 + DefaultTTL: 3600 + MaxTTL: 86400 + PriceClass: "PriceClass_200" + Restrictions: + GeoRestriction: + RestrictionType: "whitelist" + Locations: + - "US" + - "CA" + - "GB" + - "DE" + ViewerCertificate: + CloudFrontDefaultCertificate: true + CloudFrontOriginAccessControl: + Type: "AWS::CloudFront::OriginAccessControl" + Properties: + OriginAccessControlConfig: + Name: !Sub "${WebSiteBucket} OAC" + OriginAccessControlOriginType: "s3" + SigningBehavior: "always" + SigningProtocol: "sigv4" +Outputs: + APIGatewayEndpoint: + Description: "API Gateway endpoint URL for Prod stage" + Value: !Sub "https://${ApiGatewayApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + CloudFrontDistributionId: + Description: "CloudFront Distribution ID for hosting web front end" + Value: !Ref CloudFrontDistribution + CloudFrontDistributionDomainName: + Description: "CloudFront Distribution Domain Name for accessing web front end" + Value: !GetAtt CloudFrontDistribution.DomainName + WebS3BucketName: + Description: "S3 Bucket for hosting web frontend" + Value: !Ref WebSiteBucket diff --git a/nodejs22.x/hello-gql/.gitignore b/nodejs22.x/hello-gql/.gitignore new file mode 100644 index 000000000..9fb28e9f2 --- /dev/null +++ b/nodejs22.x/hello-gql/.gitignore @@ -0,0 +1,208 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +**/node_modules/ +**/jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/hello-gql/README.md b/nodejs22.x/hello-gql/README.md new file mode 100644 index 000000000..e7c3f12d8 --- /dev/null +++ b/nodejs22.x/hello-gql/README.md @@ -0,0 +1,69 @@ +# {{ cookiecutter.project_name }} + +This is a sample template for {{ cookiecutter.project_name }} - Below is a brief explanation of what we have generated for you: + +```bash +. +├── README.md <-- This instructions file +├── gql <-- Source code for schema and pipeline functions +│ ├── createPostItem.js <-- Pipeline function code +│ ├── getPostFromTable.js <-- Pipeline function code +│ ├── greet.js <-- Pipeline function code +│ ├── preprocessPostItem.js <-- Pipeline function code +│ └── schema.graphql <-- Schema definition +├── greeter <-- Source code for lambda function +│ ├── tests <-- Tests for lambda function +│ │ ├──events <-- Event stubs +│ │ │ └── appsync.json <-- Sample event for tests +│ │ └──unit <-- Unit tests +│ │ └── test-handler.mjs <-- Source file with tests +│ ├── app.mjs <-- Lambda function source code +│ └── package.json <-- List of Lambda dependencies and npm scripts +└── template.yaml <-- SAM template +``` + +## Requirements + +* AWS CLI already configured with Administrator permission +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) + +## Deploy the sample application + +To deploy your application for the first time, run the following in your shell: + +```bash +sam deploy --guided +``` + +This command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your AppSync GraphQL Endpoint URL and API key, which is required to access your API, in the output values displayed after deployment. + +## Add a resource to your application +The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name "{{ cookiecutter.__stack_name }}" +``` + +## Test lambda function locally + +1. Open terminal in `{{ cookiecutter.__stack_name }}/greeter` directory +2. Run `npm i` +3. Run `npm run test` + +To modify the lambda function event, edit `{{ cookiecutter.__stack_name }}/greeter/tests/events/appsync.json` file. + +## Resources + +See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. diff --git a/nodejs22.x/hello-gql/cookiecutter.json b/nodejs22.x/hello-gql/cookiecutter.json new file mode 100644 index 000000000..aa29c9e4f --- /dev/null +++ b/nodejs22.x/hello-gql/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} diff --git a/nodejs22.x/hello-gql/setup.cfg b/nodejs22.x/hello-gql/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/hello-gql/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..9fb28e9f2 --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,208 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +**/node_modules/ +**/jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/createPostItem.js b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/createPostItem.js new file mode 100644 index 000000000..97f20d313 --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/createPostItem.js @@ -0,0 +1,14 @@ +import { util } from "@aws-appsync/utils"; + +export function request(ctx) { + const { key, values } = ctx.prev.result; + return { + operation: "PutItem", + key: util.dynamodb.toMapValues(key), + attributeValues: util.dynamodb.toMapValues(values), + }; +} + +export function response(ctx) { + return ctx.result; +} diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/getPostFromTable.js b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/getPostFromTable.js new file mode 100644 index 000000000..ca55c8bc7 --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/getPostFromTable.js @@ -0,0 +1,19 @@ +import { util } from "@aws-appsync/utils"; + +export function request(ctx) { + return dynamoDBGetItemRequest({ id: ctx.args.id }); +} + +export function response(ctx) { + return ctx.result; +} + +/** + * A helper function to get a DynamoDB item + */ +function dynamoDBGetItemRequest(key) { + return { + operation: "GetItem", + key: util.dynamodb.toMapValues(key), + }; +} diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/greet.js b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/greet.js new file mode 100644 index 000000000..05fb0dcca --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/greet.js @@ -0,0 +1,11 @@ +export function request(ctx) { + const { source, args } = ctx; + return { + operation: "Invoke", + payload: { field: ctx.info.fieldName, arguments: args, source }, + }; +} + +export function response(ctx) { + return ctx.result; +} diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/preprocessPostItem.js b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/preprocessPostItem.js new file mode 100644 index 000000000..a1b4c839d --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/preprocessPostItem.js @@ -0,0 +1,14 @@ +import { util } from "@aws-appsync/utils"; + +export function request(ctx) { + const id = util.autoId(); + const { ...values } = ctx.args; + values.ups = 1; + values.downs = 0; + values.version = 1; + return { payload: { key: { id }, values: values } }; +} + +export function response(ctx) { + return ctx.result; +} diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/schema.graphql b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/schema.graphql new file mode 100644 index 000000000..d6a62ddee --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/gql/schema.graphql @@ -0,0 +1,24 @@ +schema { + query: Query + mutation: Mutation +} + +type Query { + getPost(id: String!): Post + sayHello(name: String): String + sayGoodbye(name: String): String +} + +type Mutation { + addPost(author: String!, title: String!, content: String!): Post! +} + +type Post { + id: String! + author: String + title: String + content: String + ups: Int! + downs: Int! + version: Int! +} diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/.npmignore b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/.npmignore new file mode 100644 index 000000000..e7e1fb04f --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/app.mjs b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/app.mjs new file mode 100644 index 000000000..11b4e6124 --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/app.mjs @@ -0,0 +1,15 @@ +export const lambdaHandler = async (event, _) => { + console.log("Got an Invoke Request."); + console.log(`EVENT: ${JSON.stringify(event, 2)}`); + + const name = !event.arguments?.name ? "world" : event.arguments.name; + + switch (event.field) { + case "sayHello": + return `Hello, ${name}`; + case "sayGoodbye": + return `Bye, ${name}`; + default: + throw new Error("Unknown field, unable to resolve " + event.field); + } +}; diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/package.json b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/package.json new file mode 100644 index 000000000..d48887bd1 --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/package.json @@ -0,0 +1,16 @@ +{ + "name": "hello_world", + "version": "1.0.0", + "description": "hello world sample for NodeJS", + "main": "app.js", + "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", + "author": "SAM CLI", + "license": "MIT", + "scripts": { + "test": "mocha tests/unit/" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.1.0" + } +} diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/tests/events/appsync.json b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/tests/events/appsync.json new file mode 100644 index 000000000..95a0a0d0e --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/tests/events/appsync.json @@ -0,0 +1,69 @@ +{ + "field": "sayHello", + "arguments": { + "name": "User" + }, + "identity": { + "claims": { + "sub": "192879fc-a240-4bf1-ab5a-d6a00f3063f9", + "email_verified": true, + "iss": "https://cognito-idp.us-west-2.amazonaws.com/us-west-xxxxxxxxxxx", + "phone_number_verified": false, + "cognito:username": "jdoe", + "aud": "7471s60os7h0uu77i1tk27sp9n", + "event_id": "bc334ed8-a938-4474-b644-9547e304e606", + "token_use": "id", + "auth_time": 1599154213, + "phone_number": "+19999999999", + "exp": 1599157813, + "iat": 1599154213, + "email": "jdoe@email.com" + }, + "defaultAuthStrategy": "ALLOW", + "groups": null, + "issuer": "https://cognito-idp.us-west-2.amazonaws.com/us-west-xxxxxxxxxxx", + "sourceIp": ["1.1.1.1"], + "sub": "192879fc-a240-4bf1-ab5a-d6a00f3063f9", + "username": "jdoe" + }, + "source": null, + "request": { + "headers": { + "x-forwarded-for": "1.1.1.1, 2.2.2.2", + "cloudfront-viewer-country": "US", + "cloudfront-is-tablet-viewer": "false", + "via": "2.0 xxxxxxxxxxxxxxxx.cloudfront.net (CloudFront)", + "cloudfront-forwarded-proto": "https", + "origin": "https://us-west-1.console.aws.amazon.com", + "content-length": "217", + "accept-language": "en-US,en;q=0.9", + "host": "xxxxxxxxxxxxxxxx.appsync-api.us-west-1.amazonaws.com", + "x-forwarded-proto": "https", + "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36", + "accept": "*/*", + "cloudfront-is-mobile-viewer": "false", + "cloudfront-is-smarttv-viewer": "false", + "accept-encoding": "gzip, deflate, br", + "referer": "https://us-west-1.console.aws.amazon.com/appsync/home?region=us-west-1", + "content-type": "application/json", + "sec-fetch-mode": "cors", + "x-amz-cf-id": "3aykhqlUwQeANU-HGY7E_guV5EkNeMMtwyOgiA==", + "x-amzn-trace-id": "Root=1-5f512f51-fac632066c5e848ae714", + "authorization": "eyJraWQiOiJScWFCSlJqYVJlM0hrSnBTUFpIcVRXazNOW...", + "sec-fetch-dest": "empty", + "x-amz-user-agent": "AWS-Console-AppSync/", + "cloudfront-is-desktop-viewer": "true", + "sec-fetch-site": "cross-site", + "x-forwarded-port": "443" + } + }, + "prev": null, + "info": { + "selectionSetList": ["id", "field1", "field2"], + "selectionSetGraphQL": "{\n id\n field1\n field2\n}", + "parentTypeName": "Mutation", + "fieldName": "createSomething", + "variables": {} + }, + "stash": {} +} diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/tests/unit/test-handler.mjs b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/tests/unit/test-handler.mjs new file mode 100644 index 000000000..6a53f78b4 --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/greeter/tests/unit/test-handler.mjs @@ -0,0 +1,19 @@ +"use strict"; + +import { lambdaHandler } from "../../app.mjs"; +import { expect } from "chai"; +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); +const event = require('../events/appsync.json'); + +const context = {}; + +describe("Tests Lambda handler", function () { + it("verifies successful response", async () => { + const result = await lambdaHandler(event, context); + + expect(result).to.be.an("string"); + expect(result).to.be.equal("Hello, User"); + }); +}); diff --git a/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..3f91172a9 --- /dev/null +++ b/nodejs22.x/hello-gql/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,105 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + {{ cookiecutter.project_name }} + + Sample SAM Template for {{ cookiecutter.project_name }} + + +Resources: + PostsTable: + Type: AWS::Serverless::SimpleTable + + Greeter: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: greeter/ + Handler: app.lambdaHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + + HelloWorldGraphQLApi: + Type: AWS::Serverless::GraphQLApi + Properties: + SchemaUri: ./gql/schema.graphql + Auth: + Type: API_KEY + ApiKeys: + MyApiKey: + Description: my api key + DataSources: + DynamoDb: + Posts: + TableName: !Ref PostsTable + TableArn: !GetAtt PostsTable.Arn + Lambda: + Greeter: + FunctionArn: !GetAtt Greeter.Arn + Functions: + preprocessPostItem: + Runtime: + Name: APPSYNC_JS + Version: 1.0.0 + DataSource: NONE + CodeUri: ./gql/preprocessPostItem.js + createPostItem: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + DataSource: Posts + CodeUri: ./gql/createPostItem.js + getPostFromTable: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + DataSource: Posts + CodeUri: ./gql/getPostFromTable.js + greet: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + DataSource: Greeter + CodeUri: ./gql/greet.js + Resolvers: + Mutation: + addPost: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + Pipeline: + - preprocessPostItem + - createPostItem + Query: + getPost: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + Pipeline: + - getPostFromTable + sayHello: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + Pipeline: + - greet + sayGoodbye: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + Pipeline: + - greet + + +Outputs: + HelloWorldGraphQLApi: + Description: HelloWorldGraphQLApi endpoint URL for Prod environment + Value: !GetAtt HelloWorldGraphQLApi.GraphQLUrl + HelloWorldGraphQLApiMyApiKey: + Description: API Key for HelloWorldGraphQLApi + Value: !GetAtt HelloWorldGraphQLApiMyApiKey.ApiKey + diff --git a/nodejs22.x/hello-img/.gitignore b/nodejs22.x/hello-img/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/hello-img/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/hello-img/README.md b/nodejs22.x/hello-img/README.md new file mode 100644 index 000000000..58ce0733c --- /dev/null +++ b/nodejs22.x/hello-img/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS Hello-world for SAM based Serverless App + +A cookiecutter template to create a NodeJS Hello world boilerplate using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x` + +> **NOTE**: ``--name`` allows you to specify a different project folder name (`sam-app` is the default) + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/hello-img/cookiecutter.json b/nodejs22.x/hello-img/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/hello-img/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/hello-img/setup.cfg b/nodejs22.x/hello-img/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/hello-img/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..5854f05ec --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,207 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/README.md b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..e51c65a5c --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,116 @@ +# {{ cookiecutter.project_name }} + +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders. + +- hello-world - Code for the application's Lambda function and Project Dockerfile. +- events - Invocation events that you can use to invoke the function. +- hello-world/tests - Unit tests for the application code. +- template.yaml - A template that defines the application's AWS resources. + +The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +## Deploy the sample application + +The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the SAM CLI, you need the following tools. + +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) + +You may need the following for local testing. + +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the NPM package management tool. + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build a docker image from a Dockerfile and then the source of your application inside the Docker image. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your API Gateway Endpoint URL in the output values displayed after deployment. + +## Use the SAM CLI to build and test locally + +Build your application with the `sam build` command. + +```bash +{{ cookiecutter.project_name }}$ sam build +``` + +The SAM CLI builds a docker image from a Dockerfile and then installs dependencies defined in `hello-world/package.json` inside the docker image. The processed template file is saved in the `.aws-sam/build` folder. +* **Note**: The Dockerfile included in this sample application uses `npm install` by default. If you are building your code for production, you can modify it to use `npm ci` instead. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +{{ cookiecutter.project_name }}$ sam local invoke HelloWorldFunction --event events/event.json +``` + +The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. + +```bash +{{ cookiecutter.project_name }}$ sam local start-api +{{ cookiecutter.project_name }}$ curl http://localhost:3000/ +``` + +The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. + +```yaml + Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: get +``` + +## Add a resource to your application +The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. + +```bash +{{ cookiecutter.project_name }}$ sam logs -n HelloWorldFunction --stack-name {{ cookiecutter.project_name }} --tail +``` + +You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `hello-world/tests` folder in this project. Use NPM to install the [Mocha test framework](https://mochajs.org/) and run unit tests from your local machine. + +```bash +{{ cookiecutter.project_name }}$ cd hello-world +hello-world$ npm install +hello-world$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. + +Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/events/event.json b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/events/event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"hello world\"}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/.npmignore b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/.npmignore new file mode 100644 index 000000000..e7e1fb04f --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/Dockerfile b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/Dockerfile new file mode 100644 index 000000000..f03a62a5f --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/Dockerfile @@ -0,0 +1,10 @@ +FROM public.ecr.aws/lambda/nodejs:22 + +COPY app.mjs package*.json ./ + +RUN npm install +# If you are building your code for production, instead include a package-lock.json file on this directory and use: +# RUN npm ci --production + +# Command can be overwritten by providing a different command in the template directly. +CMD ["app.lambdaHandler"] diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/app.mjs b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/app.mjs new file mode 100644 index 000000000..9cef2aca5 --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/app.mjs @@ -0,0 +1,23 @@ +/** + * + * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format + * @param {Object} event - API Gateway Lambda Proxy Input Format + * + * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html + * @param {Object} context + * + * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html + * @returns {Object} object - API Gateway Lambda Proxy Output Format + * + */ + +export const lambdaHandler = async (event, context) => { + const response = { + statusCode: 200, + body: JSON.stringify({ + message: 'hello world', + }) + }; + + return response; + }; diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/package.json b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/package.json new file mode 100644 index 000000000..0164adcf5 --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/package.json @@ -0,0 +1,19 @@ +{ + "name": "hello_world", + "version": "1.0.0", + "description": "hello world sample for NodeJS", + "main": "app.js", + "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", + "author": "SAM CLI", + "license": "MIT", + "dependencies": { + "axios": ">=1.6.0" + }, + "scripts": { + "test": "mocha tests/unit/" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.2.0" + } +} diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.mjs b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.mjs new file mode 100644 index 000000000..02a66db27 --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.mjs @@ -0,0 +1,20 @@ +'use strict'; + +import { lambdaHandler } from '../../app.mjs'; +import { expect } from 'chai'; +var event, context; + +describe('Tests index', function () { + it('verifies successful response', async () => { + const result = await lambdaHandler(event, context) + + expect(result).to.be.an('object'); + expect(result.statusCode).to.equal(200); + expect(result.body).to.be.an('string'); + + let response = JSON.parse(result.body); + + expect(response).to.be.an('object'); + expect(response.message).to.be.equal("hello world"); + }); +}); diff --git a/nodejs22.x/hello-img/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..2e2bb0ee3 --- /dev/null +++ b/nodejs22.x/hello-img/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,47 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + {{ cookiecutter.project_name }} + + Sample SAM Template for {{ cookiecutter.project_name }} + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 3 + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function + Properties: + PackageType: Image + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: get + Metadata: + DockerTag: {{cookiecutter.runtime}}-v1 + DockerContext: ./hello-world + Dockerfile: Dockerfile + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn diff --git a/nodejs22.x/hello-ts-pt/.gitignore b/nodejs22.x/hello-ts-pt/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/hello-ts-pt/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/hello-ts-pt/README.md b/nodejs22.x/hello-ts-pt/README.md new file mode 100644 index 000000000..5f5436fcd --- /dev/null +++ b/nodejs22.x/hello-ts-pt/README.md @@ -0,0 +1,37 @@ +# AWS SAM cookiecutter for NodeJS TypeScript Lambda functions with Powertools for AWS Lambda (TypeScript) + +**Please note, you should not try to `git clone` this project.** Instead, use `cookiecutter` CLI instead as ``{{cookiecutter.project_name}}`` will be rendered based on your input and therefore all variables and files will be rendered properly. + +## Cookiecutter requirements + +Install `cookiecutter` command line: + +**Pip users**: + +* `pip install cookiecutter` + +**Homebrew users**: + +* `brew install cookiecutter` + +**Windows or Pipenv users**: + +* `pipenv install cookiecutter` + +**NOTE**: [`Pipenv`](https://github.com/pypa/pipenv) is the new and recommended Python packaging tool that works across multiple platforms and makes Windows a first-class citizen. + +### Usage + +Generate a new SAM based Serverless App: `sam init --runtime nodejs22.x`. + +You'll be prompted a few questions to help this cookiecutter template to scaffold this project and after its completed you should see a new folder at your current path with the name of the project you gave as input. + +**NOTE**: After you understand how cookiecutter works (cookiecutter.json, mainly), you can fork this repo and apply your own mechanisms to accelerate your development process and this can be followed for any programming language and OS. + +### Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + +### License + +This project is licensed under the terms of the [MIT License with no attribution](/LICENSE) diff --git a/nodejs22.x/hello-ts-pt/cookiecutter.json b/nodejs22.x/hello-ts-pt/cookiecutter.json new file mode 100644 index 000000000..7dfa5e4fb --- /dev/null +++ b/nodejs22.x/hello-ts-pt/cookiecutter.json @@ -0,0 +1,14 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "Powertools for AWS Lambda (TypeScript) Tracing": ["enabled","disabled"], + "Powertools for AWS Lambda (TypeScript) Metrics": ["enabled","disabled"], + "Powertools for AWS Lambda (TypeScript) Logging": ["enabled","disabled"], + "_copy_without_render": [ + ".gitignore" + ] + +} \ No newline at end of file diff --git a/nodejs22.x/hello-ts-pt/setup.cfg b/nodejs22.x/hello-ts-pt/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/hello-ts-pt/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..5854f05ec --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,207 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/README.md b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..f6482f29e --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,182 @@ +# {{ cookiecutter.project_name }} + +Congratulations, you have just created a Serverless "Hello World" application using the AWS Serverless Application Model (AWS SAM) for the `nodejs22.x` runtime, and options to bootstrap it with [**Powertools for AWS Lambda (TypeScript)**](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/) (Lambda Powertools) utilities for Logging, Tracing and Metrics. + +Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serverless best practices and increase developer velocity. + +## Powertools for AWS Lambda (TypeScript) features + +Powertools for AWS Lambda (TypeScript) provides three core utilities: + +* **[Tracer](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer/)** - Utilities to trace Lambda function handlers, and both synchronous and asynchronous functions +* **[Logger](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/logger/)** - Structured logging made easier, and a middleware to enrich log items with key details of the Lambda context +* **[Metrics](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) + +Find the complete project's [documentation here](https://awslabs.github.io/aws-lambda-powertools-typescript). + +### Installing Powertools for AWS Lambda (TypeScript) + +You have 2 ways of consuming those utilities: + +* NPM modules +* Lambda Layer + +#### Lambda layers + +The Powertools for AWS Lambda (TypeScript) utilities is packaged as a single [AWS Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-concepts.html#gettingstarted-concepts-layer) + +👉 [Installation guide for the **Powertools for AWS Lambda (TypeScript)** layer](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/#lambda-layer) + +#### NPM modules + +The Powertools for AWS Lambda (TypeScript) utilities follow a modular approach, similar to the official [AWS SDK v3 for JavaScript](https://github.com/aws/aws-sdk-js-v3). + +Each TypeScript utility is installed as standalone NPM package. + +Install all three core utilities at once with this single command: + +```shell +npm install @aws-lambda-powertools/logger @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics +``` + +Or refer to the installation guide of each utility: + +👉 [Installation guide for the **Tracer** utility](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer#getting-started) + +👉 [Installation guide for the **Logger** utility](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/logger#getting-started) + +👉 [Installation guide for the **Metrics** utility](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/metrics#getting-started) + +### Powertools for AWS Lambda (TypeScript) Examples + +* [CDK](https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/examples/cdk) +* [SAM](https://github.com/awslabs/aws-lambda-powertools-typescript/tree/main/examples/sam) + +## Working with this project + +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders. + +* hello-world - Code for the application's Lambda function written in TypeScript. +* events - Invocation events that you can use to invoke the function. +* hello-world/tests - Unit tests for the application code. +* template.yaml - A template that defines the application's AWS resources. + +The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started. + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +### Deploy the sample application + +The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the NPM package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your API Gateway Endpoint URL in the output values displayed after deployment. + +### Use the SAM CLI to build and test locally + +Build your application with the `sam build` command. + +```bash +{{ cookiecutter.project_name }}$ sam build +``` + +The SAM CLI installs dependencies defined in `hello-world/package.json`, compiles TypeScript with esbuild, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +{{ cookiecutter.project_name }}$ sam local invoke HelloWorldFunction --event events/event.json +``` + +The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. + +```bash +{{ cookiecutter.project_name }}$ sam local start-api +{{ cookiecutter.project_name }}$ curl http://localhost:3000/ +``` + +The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. + +```yaml + Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: get +``` + +### Add a resource to your application + +The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. + +### Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. + +```bash +{{ cookiecutter.project_name }}$ sam logs -n HelloWorldFunction --stack-name {{ cookiecutter.project_name }} --tail +``` + +You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +### Unit tests + +Tests are defined in the `test` folder in this project. + +```bash +{{ cookiecutter.project_name }}$ cd hello-world +hello-world$ npm install +hello-world$ npm run test +``` + +### Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. + +Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/events/event.json b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/events/event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"hello world\"}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.eslintignore b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.eslintignore new file mode 100644 index 000000000..512d4cb8b --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.eslintignore @@ -0,0 +1,2 @@ +node_modules +.aws-sam \ No newline at end of file diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.eslintrc.js b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.eslintrc.js new file mode 100644 index 000000000..5da871fc4 --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features + sourceType: "module" + }, + extends: [ + "plugin:@typescript-eslint/recommended", // recommended rules from the @typescript-eslint/eslint-plugin + "plugin:prettier/recommended" // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. + ], + rules: { + // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs + // e.g. "@typescript-eslint/explicit-function-return-type": "off", + } + }; \ No newline at end of file diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.npmignore b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.npmignore new file mode 100644 index 000000000..e7e1fb04f --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.prettierrc.js b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.prettierrc.js new file mode 100644 index 000000000..4c2c6c78b --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/.prettierrc.js @@ -0,0 +1,7 @@ +module.exports = { + semi: true, + trailingComma: "all", + singleQuote: true, + printWidth: 120, + tabWidth: 4 + }; \ No newline at end of file diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/app.ts b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/app.ts new file mode 100644 index 000000000..b6976fecc --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/app.ts @@ -0,0 +1,135 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +{%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Metrics"] == "enabled"%} +import { Metrics } from '@aws-lambda-powertools/metrics'; +{%- endif %} +{%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Logging"] == "enabled"%} +import { Logger } from '@aws-lambda-powertools/logger'; +{%- endif %} +{%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled"%} +import { Tracer } from '@aws-lambda-powertools/tracer'; +{%- endif %} + + +{%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Metrics"] == "enabled"%} +const metrics = new Metrics(); +{%- endif %} +{%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Logging"] == "enabled"%} +const logger = new Logger(); +{%- endif %} +{%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled"%} +const tracer = new Tracer(); +{%- endif %} + +/** + * + * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format + * @param {APIGatewayProxyEvent} event - API Gateway Lambda Proxy Input Format + * @param {Context} object - API Gateway Lambda $context variable + * + * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html + * @returns {APIGatewayProxyResult} object - API Gateway Lambda Proxy Output Format + * + */ + +export const lambdaHandler = async (event: APIGatewayProxyEvent, context: Context): Promise => { + let response: APIGatewayProxyResult; + + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Logging"] == "enabled"%} + + // Log the incoming event + logger.info('Lambda invocation event', { event }); + + // Append awsRequestId to each log statement + logger.appendKeys({ + awsRequestId: context.awsRequestId, + }); + + {%- endif %} + + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled"%} + // Get facade segment created by AWS Lambda + const segment = tracer.getSegment(); + + if (!segment) { + response = { + statusCode: 500, + body: "Failed to get segment" + } + return response; + } + + // Create subsegment for the function & set it as active + const handlerSegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`); + tracer.setSegment(handlerSegment); + + // Annotate the subsegment with the cold start & serviceName + tracer.annotateColdStart(); + tracer.addServiceNameAnnotation(); + + // Add annotation for the awsRequestId + tracer.putAnnotation('awsRequestId', context.awsRequestId); + + {%- endif %} + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Metrics"] == "enabled" %} + // Capture cold start metrics + metrics.captureColdStartMetric(); + + {%- endif %} + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled"%} + // Create another subsegment & set it as active + const subsegment = handlerSegment.addNewSubsegment('### MySubSegment'); + tracer.setSegment(subsegment); + {%- endif %} + + try { + // hello world code + response = { + statusCode: 200, + body: JSON.stringify({ + message: 'hello world', + }), + }; + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Logging"] == "enabled"%} + logger.info(`Successful response from API enpoint: ${event.path}`, response.body); + {%- else %} + console.log('sending HTTP 200 - hello world response') + {%- endif %} + } catch (err) { + // Error handling + response = { + statusCode: 500, + body: JSON.stringify({ + message: 'some error happened', + }), + }; + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled"%} + tracer.addErrorAsMetadata(err as Error); + {%- endif %} + + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Logging"] == "enabled"%} + logger.error(`Error response from API enpoint: ${err}`, response.body); + {%- else %} + console.log('sending HTTP 500 - some error happened response') + {%- endif %} + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Metrics"] == "enabled" or cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled"%} + } finally { + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled"%} + // Close subsegments (the AWS Lambda one is closed automatically) + subsegment.close(); // (### MySubSegment) + handlerSegment.close(); // (## index.handler) + + // Set the facade segment as active again (the one created by AWS Lambda) + tracer.setSegment(segment); + + {%- endif %} + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Metrics"] == "enabled"%} + // Publish all stored metrics + metrics.publishStoredMetrics(); + + {%- endif %} + {%- endif %} + } + + return response; + +}; \ No newline at end of file diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/jest.config.ts b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/jest.config.ts new file mode 100644 index 000000000..3f8eb55bd --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/jest.config.ts @@ -0,0 +1,15 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +export default { + transform: { + '^.+\\.ts?$': 'ts-jest', + }, + clearMocks: true, + collectCoverage: true, + coverageDirectory: 'coverage', + coverageProvider: 'v8', + testMatch: ['**/tests/unit/*.test.ts'], +}; diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/package.json b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/package.json new file mode 100644 index 000000000..f1e2400bd --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/package.json @@ -0,0 +1,37 @@ +{ + "name": "hello_world", + "version": "1.0.0", + "description": "hello world sample for NodeJS", + "main": "app.js", + "repository": "https://github.com/aws/aws-sam-cli-app-templates/tree/master/nodejs22.x/hello-ts-pt", + "author": "SAM CLI", + "license": "MIT", + "dependencies": { + "@aws-lambda-powertools/logger": "^2.0.3", + "@aws-lambda-powertools/metrics": "^2.0.3", + "@aws-lambda-powertools/tracer": "^2.0.3", + "esbuild": "^0.17.6" + }, + "scripts": { + "unit": "jest", + "lint": "eslint '*.ts' --quiet --fix", + "compile": "tsc", + "test": "npm run compile && npm run unit" + }, + "devDependencies": { + "@types/aws-lambda": "^8.10.109", + "@types/jest": "^29.4.0", + "@jest/globals": "^29.4.0", + "@types/node": "^20.5.7", + "@typescript-eslint/eslint-plugin": "^5.46.1", + "@typescript-eslint/parser": "^5.46.1", + "eslint": "^8.30.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "jest": "^29.3.1", + "prettier": "^2.5.1", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "typescript": "^4.9.4" + } +} diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.test.ts b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.test.ts new file mode 100644 index 000000000..da70ae4da --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.test.ts @@ -0,0 +1,87 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { lambdaHandler } from '../../app'; +import { expect, describe, it } from '@jest/globals'; + +describe('Unit test for app handler', function () { + it('verifies successful response', async () => { + const event: APIGatewayProxyEvent = { + httpMethod: 'get', + body: '', + headers: {}, + isBase64Encoded: false, + multiValueHeaders: {}, + multiValueQueryStringParameters: {}, + path: '/hello', + pathParameters: {}, + queryStringParameters: {}, + requestContext: { + accountId: '123456789012', + apiId: '1234', + authorizer: {}, + httpMethod: 'get', + identity: { + accessKey: '', + accountId: '', + apiKey: '', + apiKeyId: '', + caller: '', + clientCert: { + clientCertPem: '', + issuerDN: '', + serialNumber: '', + subjectDN: '', + validity: { notAfter: '', notBefore: '' }, + }, + cognitoAuthenticationProvider: '', + cognitoAuthenticationType: '', + cognitoIdentityId: '', + cognitoIdentityPoolId: '', + principalOrgId: '', + sourceIp: '', + user: '', + userAgent: '', + userArn: '', + }, + path: '/hello', + protocol: 'HTTP/1.1', + requestId: 'c6af9ac6-7b61-11e6-9a41-93e8deadbeef', + requestTimeEpoch: 1428582896000, + resourceId: '123456', + resourcePath: '/hello', + stage: 'dev', + }, + resource: '', + stageVariables: {}, + }; + const context: Context = { + callbackWaitsForEmptyEventLoop: false, + functionName: 'lambdaHandler', + functionVersion: '1.0', + invokedFunctionArn: 'arn:1234567890:lambda:lambdaHandler', + memoryLimitInMB: '128', + awsRequestId: '1234567890', + logGroupName: 'lambdaHandlerLogGroup', + logStreamName: 'c6a789dff9326bc178', + getRemainingTimeInMillis: function (): number { + throw new Error('Function not implemented.'); + }, + done: function (error?: Error, result?: any): void { + throw new Error('Function not implemented.'); + }, + fail: function (error: string | Error): void { + throw new Error('Function not implemented.'); + }, + succeed: function (messageOrObject: any): void { + throw new Error('Function not implemented.'); + } + }; + const result: APIGatewayProxyResult = await lambdaHandler(event,context); + + expect(result.statusCode).toEqual(200); + expect(result.body).toEqual( + JSON.stringify({ + message: 'hello world', + }), + ); + }); +}); diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/tsconfig.json b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/tsconfig.json new file mode 100644 index 000000000..93fab7255 --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/hello-world/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2020", + "strict": true, + "preserveConstEnums": true, + "noEmit": true, + "sourceMap": false, + "module": "es2015", + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "exclude": ["node_modules"], +} \ No newline at end of file diff --git a/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..324c53fb9 --- /dev/null +++ b/nodejs22.x/hello-ts-pt/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,70 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + {{ cookiecutter.project_name }} + + Sample SAM Template for {{ cookiecutter.project_name }} + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 3 + MemorySize: 128 + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: hello-world/ + Handler: app.lambdaHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled"%} + Tracing: Active + {%- endif %} + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled" or cookiecutter["Powertools for AWS Lambda (TypeScript) Metrics"] == "enabled" or cookiecutter["Powertools for AWS Lambda (TypeScript) Logging"] == "enabled" %} + Environment: + Variables: + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Tracing"] == "enabled" or cookiecutter["Powertools for AWS Lambda (TypeScript) Metrics"] == "enabled"%} + POWERTOOLS_SERVICE_NAME: helloWorld + {%- endif %} + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Metrics"] == "enabled"%} + POWERTOOLS_METRICS_NAMESPACE: {{ cookiecutter.project_name|lower|replace(' ', '-') }} + {%- endif %} + {%- if cookiecutter["Powertools for AWS Lambda (TypeScript) Logging"] == "enabled"%} + LOG_LEVEL: INFO + {%- endif %} + {%- endif %} + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + Metadata: # Manage esbuild properties + BuildMethod: esbuild + BuildProperties: + Minify: true + Target: "es2020" + # Sourcemap: true # Enabling source maps will create the required NODE_OPTIONS environment variables on your lambda function during sam build + EntryPoints: + - app.ts + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn diff --git a/nodejs22.x/hello-ts/.gitignore b/nodejs22.x/hello-ts/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/hello-ts/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/hello-ts/README.md b/nodejs22.x/hello-ts/README.md new file mode 100644 index 000000000..21aad6c88 --- /dev/null +++ b/nodejs22.x/hello-ts/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS TypeScript Hello-world for SAM based Serverless App + +A cookiecutter template to create a NodeJS TypeScript Hello world boilerplate using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x` + +> **NOTE**: ``--name`` allows you to specify a different project folder name (`sam-app` is the default) + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/hello-ts/cookiecutter.json b/nodejs22.x/hello-ts/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/hello-ts/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/hello-ts/setup.cfg b/nodejs22.x/hello-ts/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/hello-ts/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..5854f05ec --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,207 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/README.md b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..be35eb981 --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,127 @@ +# {{ cookiecutter.project_name }} + +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders. + +- hello-world - Code for the application's Lambda function written in TypeScript. +- events - Invocation events that you can use to invoke the function. +- hello-world/tests - Unit tests for the application code. +- template.yaml - A template that defines the application's AWS resources. + +The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started. + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the NPM package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your API Gateway Endpoint URL in the output values displayed after deployment. + +## Use the SAM CLI to build and test locally + +Build your application with the `sam build` command. + +```bash +{{ cookiecutter.project_name }}$ sam build +``` + +The SAM CLI installs dependencies defined in `hello-world/package.json`, compiles TypeScript with esbuild, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +{{ cookiecutter.project_name }}$ sam local invoke HelloWorldFunction --event events/event.json +``` + +The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. + +```bash +{{ cookiecutter.project_name }}$ sam local start-api +{{ cookiecutter.project_name }}$ curl http://localhost:3000/ +``` + +The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. + +```yaml + Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: get +``` + +## Add a resource to your application +The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. + +```bash +{{ cookiecutter.project_name }}$ sam logs -n HelloWorldFunction --stack-name {{ cookiecutter.project_name }} --tail +``` + +You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `hello-world/tests` folder in this project. Use NPM to install the [Jest test framework](https://jestjs.io/) and run unit tests. + +```bash +{{ cookiecutter.project_name }}$ cd hello-world +hello-world$ npm install +hello-world$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. + +Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/events/event.json b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/events/event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"hello world\"}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.eslintignore b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.eslintignore new file mode 100644 index 000000000..512d4cb8b --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.eslintignore @@ -0,0 +1,2 @@ +node_modules +.aws-sam \ No newline at end of file diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.eslintrc.js b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.eslintrc.js new file mode 100644 index 000000000..5da871fc4 --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.eslintrc.js @@ -0,0 +1,15 @@ +module.exports = { + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features + sourceType: "module" + }, + extends: [ + "plugin:@typescript-eslint/recommended", // recommended rules from the @typescript-eslint/eslint-plugin + "plugin:prettier/recommended" // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. + ], + rules: { + // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs + // e.g. "@typescript-eslint/explicit-function-return-type": "off", + } + }; \ No newline at end of file diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.npmignore b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.npmignore new file mode 100644 index 000000000..e7e1fb04f --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.prettierrc.js b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.prettierrc.js new file mode 100644 index 000000000..4c2c6c78b --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/.prettierrc.js @@ -0,0 +1,7 @@ +module.exports = { + semi: true, + trailingComma: "all", + singleQuote: true, + printWidth: 120, + tabWidth: 4 + }; \ No newline at end of file diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/app.ts b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/app.ts new file mode 100644 index 000000000..09939f747 --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/app.ts @@ -0,0 +1,30 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; + +/** + * + * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format + * @param {Object} event - API Gateway Lambda Proxy Input Format + * + * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html + * @returns {Object} object - API Gateway Lambda Proxy Output Format + * + */ + +export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise => { + try { + return { + statusCode: 200, + body: JSON.stringify({ + message: 'hello world', + }), + }; + } catch (err) { + console.log(err); + return { + statusCode: 500, + body: JSON.stringify({ + message: 'some error happened', + }), + }; + } +}; diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/jest.config.ts b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/jest.config.ts new file mode 100644 index 000000000..3f8eb55bd --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/jest.config.ts @@ -0,0 +1,15 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +export default { + transform: { + '^.+\\.ts?$': 'ts-jest', + }, + clearMocks: true, + collectCoverage: true, + coverageDirectory: 'coverage', + coverageProvider: 'v8', + testMatch: ['**/tests/unit/*.test.ts'], +}; diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/package.json b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/package.json new file mode 100644 index 000000000..85f260ee5 --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/package.json @@ -0,0 +1,34 @@ +{ + "name": "hello_world", + "version": "1.0.0", + "description": "hello world sample for NodeJS", + "main": "app.js", + "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", + "author": "SAM CLI", + "license": "MIT", + "scripts": { + "unit": "jest", + "lint": "eslint '*.ts' --quiet --fix", + "compile": "tsc", + "test": "npm run compile && npm run unit" + }, + "dependencies": { + "esbuild": "^0.14.14" + }, + "devDependencies": { + "@types/aws-lambda": "^8.10.92", + "@types/jest": "^29.2.0", + "@jest/globals": "^29.2.0", + "@types/node": "^20.5.7", + "@typescript-eslint/eslint-plugin": "^5.10.2", + "@typescript-eslint/parser": "^5.10.2", + "eslint": "^8.8.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-prettier": "^4.0.0", + "jest": "^29.2.1", + "prettier": "^2.5.1", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "typescript": "^4.8.4" + } +} diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.test.ts b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.test.ts new file mode 100644 index 000000000..f4501a905 --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.test.ts @@ -0,0 +1,65 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; +import { lambdaHandler } from '../../app'; +import { expect, describe, it } from '@jest/globals'; + +describe('Unit test for app handler', function () { + it('verifies successful response', async () => { + const event: APIGatewayProxyEvent = { + httpMethod: 'get', + body: '', + headers: {}, + isBase64Encoded: false, + multiValueHeaders: {}, + multiValueQueryStringParameters: {}, + path: '/hello', + pathParameters: {}, + queryStringParameters: {}, + requestContext: { + accountId: '123456789012', + apiId: '1234', + authorizer: {}, + httpMethod: 'get', + identity: { + accessKey: '', + accountId: '', + apiKey: '', + apiKeyId: '', + caller: '', + clientCert: { + clientCertPem: '', + issuerDN: '', + serialNumber: '', + subjectDN: '', + validity: { notAfter: '', notBefore: '' }, + }, + cognitoAuthenticationProvider: '', + cognitoAuthenticationType: '', + cognitoIdentityId: '', + cognitoIdentityPoolId: '', + principalOrgId: '', + sourceIp: '', + user: '', + userAgent: '', + userArn: '', + }, + path: '/hello', + protocol: 'HTTP/1.1', + requestId: 'c6af9ac6-7b61-11e6-9a41-93e8deadbeef', + requestTimeEpoch: 1428582896000, + resourceId: '123456', + resourcePath: '/hello', + stage: 'dev', + }, + resource: '', + stageVariables: {}, + }; + const result: APIGatewayProxyResult = await lambdaHandler(event); + + expect(result.statusCode).toEqual(200); + expect(result.body).toEqual( + JSON.stringify({ + message: 'hello world', + }), + ); + }); +}); diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/tsconfig.json b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/tsconfig.json new file mode 100644 index 000000000..ffaf193ea --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/hello-world/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2020", + "strict": true, + "preserveConstEnums": true, + "noEmit": true, + "sourceMap": false, + "module":"es2015", + "moduleResolution":"node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + }, + "exclude": ["node_modules", "**/*.test.ts"] + } \ No newline at end of file diff --git a/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..447d4e83a --- /dev/null +++ b/nodejs22.x/hello-ts/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,53 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + {{ cookiecutter.project_name }} + + Sample SAM Template for {{ cookiecutter.project_name }} + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 3 + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: hello-world/ + Handler: app.lambdaHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + Metadata: # Manage esbuild properties + BuildMethod: esbuild + BuildProperties: + Minify: true + Target: "es2020" + Sourcemap: true + EntryPoints: + - app.ts + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn diff --git a/nodejs22.x/hello/.gitignore b/nodejs22.x/hello/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/hello/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/hello/README.md b/nodejs22.x/hello/README.md new file mode 100644 index 000000000..58ce0733c --- /dev/null +++ b/nodejs22.x/hello/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS Hello-world for SAM based Serverless App + +A cookiecutter template to create a NodeJS Hello world boilerplate using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x` + +> **NOTE**: ``--name`` allows you to specify a different project folder name (`sam-app` is the default) + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/hello/cookiecutter.json b/nodejs22.x/hello/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/hello/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/hello/setup.cfg b/nodejs22.x/hello/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/hello/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/hello/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/hello/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..5854f05ec --- /dev/null +++ b/nodejs22.x/hello/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,207 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/hello/{{cookiecutter.project_name}}/README.md b/nodejs22.x/hello/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..b4f741cae --- /dev/null +++ b/nodejs22.x/hello/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,127 @@ +# {{ cookiecutter.project_name }} + +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders. + +- hello-world - Code for the application's Lambda function. +- events - Invocation events that you can use to invoke the function. +- hello-world/tests - Unit tests for the application code. +- template.yaml - A template that defines the application's AWS resources. + +The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started. + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the SAM CLI, you need the following tools. + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the NPM package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your API Gateway Endpoint URL in the output values displayed after deployment. + +## Use the SAM CLI to build and test locally + +Build your application with the `sam build` command. + +```bash +{{ cookiecutter.project_name }}$ sam build +``` + +The SAM CLI installs dependencies defined in `hello-world/package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +{{ cookiecutter.project_name }}$ sam local invoke HelloWorldFunction --event events/event.json +``` + +The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. + +```bash +{{ cookiecutter.project_name }}$ sam local start-api +{{ cookiecutter.project_name }}$ curl http://localhost:3000/ +``` + +The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. + +```yaml + Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: get +``` + +## Add a resource to your application +The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. + +```bash +{{ cookiecutter.project_name }}$ sam logs -n HelloWorldFunction --stack-name {{ cookiecutter.project_name }} --tail +``` + +You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `hello-world/tests` folder in this project. Use NPM to install the [Mocha test framework](https://mochajs.org/) and run unit tests. + +```bash +{{ cookiecutter.project_name }}$ cd hello-world +hello-world$ npm install +hello-world$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. + +Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/nodejs22.x/hello/{{cookiecutter.project_name}}/events/event.json b/nodejs22.x/hello/{{cookiecutter.project_name}}/events/event.json new file mode 100644 index 000000000..070ad8e01 --- /dev/null +++ b/nodejs22.x/hello/{{cookiecutter.project_name}}/events/event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"hello world\"}", + "resource": "/{proxy+}", + "path": "/path/to/resource", + "httpMethod": "POST", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/path/to/resource", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/.npmignore b/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/.npmignore new file mode 100644 index 000000000..e7e1fb04f --- /dev/null +++ b/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/app.mjs b/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/app.mjs new file mode 100644 index 000000000..a9de03111 --- /dev/null +++ b/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/app.mjs @@ -0,0 +1,24 @@ +/** + * + * Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format + * @param {Object} event - API Gateway Lambda Proxy Input Format + * + * Context doc: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html + * @param {Object} context + * + * Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html + * @returns {Object} object - API Gateway Lambda Proxy Output Format + * + */ + +export const lambdaHandler = async (event, context) => { + const response = { + statusCode: 200, + body: JSON.stringify({ + message: 'hello world', + }) + }; + + return response; + }; + \ No newline at end of file diff --git a/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/package.json b/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/package.json new file mode 100644 index 000000000..0164adcf5 --- /dev/null +++ b/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/package.json @@ -0,0 +1,19 @@ +{ + "name": "hello_world", + "version": "1.0.0", + "description": "hello world sample for NodeJS", + "main": "app.js", + "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs", + "author": "SAM CLI", + "license": "MIT", + "dependencies": { + "axios": ">=1.6.0" + }, + "scripts": { + "test": "mocha tests/unit/" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.2.0" + } +} diff --git a/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.mjs b/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.mjs new file mode 100644 index 000000000..02a66db27 --- /dev/null +++ b/nodejs22.x/hello/{{cookiecutter.project_name}}/hello-world/tests/unit/test-handler.mjs @@ -0,0 +1,20 @@ +'use strict'; + +import { lambdaHandler } from '../../app.mjs'; +import { expect } from 'chai'; +var event, context; + +describe('Tests index', function () { + it('verifies successful response', async () => { + const result = await lambdaHandler(event, context) + + expect(result).to.be.an('object'); + expect(result.statusCode).to.equal(200); + expect(result.body).to.be.an('string'); + + let response = JSON.parse(result.body); + + expect(response).to.be.an('object'); + expect(response.message).to.be.equal("hello world"); + }); +}); diff --git a/nodejs22.x/hello/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/hello/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..3cbd2fc7b --- /dev/null +++ b/nodejs22.x/hello/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,45 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + {{ cookiecutter.project_name }} + + Sample SAM Template for {{ cookiecutter.project_name }} + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 3 + +Resources: + HelloWorldFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: hello-world/ + Handler: app.lambdaHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + Events: + HelloWorld: + Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api + Properties: + Path: /hello + Method: get + +Outputs: + # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function + # Find out more about other implicit resources you can reference within SAM + # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api + HelloWorldApi: + Description: "API Gateway endpoint URL for Prod stage for Hello World function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" + HelloWorldFunction: + Description: "Hello World Lambda Function ARN" + Value: !GetAtt HelloWorldFunction.Arn + HelloWorldFunctionIamRole: + Description: "Implicit IAM Role created for Hello World function" + Value: !GetAtt HelloWorldFunctionRole.Arn diff --git a/nodejs22.x/response-streaming/.gitignore b/nodejs22.x/response-streaming/.gitignore new file mode 100644 index 000000000..41b1156d3 --- /dev/null +++ b/nodejs22.x/response-streaming/.gitignore @@ -0,0 +1,208 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/response-streaming/README.md b/nodejs22.x/response-streaming/README.md new file mode 100644 index 000000000..58ce0733c --- /dev/null +++ b/nodejs22.x/response-streaming/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS Hello-world for SAM based Serverless App + +A cookiecutter template to create a NodeJS Hello world boilerplate using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x` + +> **NOTE**: ``--name`` allows you to specify a different project folder name (`sam-app` is the default) + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/response-streaming/cookiecutter.json b/nodejs22.x/response-streaming/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/response-streaming/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/response-streaming/setup.cfg b/nodejs22.x/response-streaming/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/response-streaming/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..41b1156d3 --- /dev/null +++ b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,208 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/README.md b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..479e6e861 --- /dev/null +++ b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,102 @@ +# {{cookiecutter.project_name}} + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) command line interface (CLI). It includes the following files and folders: + +- `src` - Code for the application's Lambda function. +- `template.yaml` - A template that defines the application's AWS resources. +- `__tests__` - Unit tests for the application code. + +Resources for this project are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open-source plugin for popular IDEs that uses the AWS SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds step-through debugging for Lambda function code. + +To get started, see the following: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The AWS SAM CLI is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools: + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the npm package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community). + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +## Building and testing + +Build your application by using the `sam build` command. + +```bash +my-application$ sam build +``` + +The AWS SAM CLI installs dependencies that are defined in `package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +my-application$ sam local invoke StreamingFunction --no-event +``` + +The streaming capabilities of the function are present only when invoked through a Function URL, so the response won't be streamed when invoking locally. To learn more, see the [Lambda response streaming documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html) + +There are also unit tests inside the `__tests__` directory, that can be run using `npm run test`. + + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, the AWS SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs that are generated by your Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +**NOTE:** This command works for all Lambda functions, not just the ones you deploy using AWS SAM. + +```bash +my-application$ sam logs -n StreamingFunction --stack-name sam-app --tail +``` + +**NOTE:** This uses the logical name of the function within the stack. This is the correct name to use when searching logs inside an AWS Lambda function within a CloudFormation stack, even if the deployed function name varies due to CloudFormation's unique resource name generation. + +You can find more information and examples about filtering Lambda function logs in the [AWS SAM CLI documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete +``` + +## Resources + +For an introduction to the AWS SAM specification, the AWS SAM CLI, and serverless application concepts, see the [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). + +Next, you can use the AWS Serverless Application Repository to deploy ready-to-use apps that go beyond Hello World samples and learn how authors developed their applications. For more information, see the [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) and the [AWS Serverless Application Repository Developer Guide](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/what-is-serverlessrepo.html). diff --git a/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/__tests__/unit/index.test.js b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/__tests__/unit/index.test.js new file mode 100644 index 000000000..134a63ca0 --- /dev/null +++ b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/__tests__/unit/index.test.js @@ -0,0 +1,41 @@ +// `awslambda` object doesn't exist outside of the Lambda runtime, so we mock its definitions here +awslambda = { + streamifyResponse: _streamifyResponseMock, + HttpResponseStream: { from: _responseStreamMock }, +}; + +const { handler } = require('../../src/index.js'); + +// This includes all tests for handler() +describe('Test for streaming handler', function () { + // This test invokes handler() and compare the values streamed + it('Verifies successful response', async () => { + const responseStream = { + setContentType: jest.fn(), + write: jest.fn(), + end: jest.fn(), + } + // Invoke handler() + await handler({}, responseStream); + + // Check some of the text that was streamed from your Lambda function. + const expectedResults = ['', '

First write!

', '']; + // Compare the result with the expected result + expect(responseStream.write).toHaveBeenCalledWith(expectedResults[0]); + expect(responseStream.write).toHaveBeenCalledWith(expectedResults[1]); + expect(responseStream.write).toHaveBeenLastCalledWith(expectedResults[2]); + expect(responseStream.end).toHaveBeenCalledTimes(1); + }); +}); + +function _streamifyResponseMock(lambdaHandler) { + return lambdaHandler; +} + +function _responseStreamMock(responseStream, httpResponseMetadata){ + responseStream.setContentType("vnd.awslambda.http-integration-response"); + responseStream.write(httpResponseMetadata); + // Separator between metadata and the body + responseStream.write("\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"); + return responseStream; +} \ No newline at end of file diff --git a/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/package.json b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/package.json new file mode 100644 index 000000000..e8ce5fb99 --- /dev/null +++ b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/package.json @@ -0,0 +1,24 @@ +{ + "name": "streaming-function", + "description": "Sample project for Lambda streaming invoke", + "version": "0.0.1", + "private": true, + "devDependencies": { + "jest": "^29.2.1" + }, + "scripts": { + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" + }, + "jest": { + "testMatch": [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[jt]s?(x)", + "**/__tests__/**/*.mjs?(x)", + "**/?(*.)+(spec|test).mjs?(x)" + ], + "moduleFileExtensions": [ + "mjs", + "js" + ] + } +} diff --git a/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/src/index.js b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/src/index.js new file mode 100644 index 000000000..71ae27051 --- /dev/null +++ b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/src/index.js @@ -0,0 +1,33 @@ +exports.handler = awslambda.streamifyResponse( + async (event, responseStream, context) => { + const httpResponseMetadata = { + statusCode: 200, + headers: { + "Content-Type": "text/html", + "X-Custom-Header": "Example-Custom-Header" + } + }; + + responseStream = awslambda.HttpResponseStream.from(responseStream, httpResponseMetadata); + // It's recommended to use a `pipeline` over the `write` method for more complex use cases. + // Learn more: https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html + responseStream.write(""); + responseStream.write("

First write!

"); + + responseStream.write("

Streaming h1

"); + await new Promise(r => setTimeout(r, 1000)); + responseStream.write("

Streaming h2

"); + await new Promise(r => setTimeout(r, 1000)); + responseStream.write("

Streaming h3

"); + await new Promise(r => setTimeout(r, 1000)); + + // Long strings will be streamed + const loremIpsum1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vitae mi tincidunt tellus ultricies dignissim id et diam. Morbi pharetra eu nisi et finibus. Vivamus diam nulla, vulputate et nisl cursus, pellentesque vehicula libero. Cras imperdiet lorem ante, non posuere dolor sollicitudin a. Vestibulum ipsum lacus, blandit nec augue id, lobortis dictum urna. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Morbi auctor orci eget tellus aliquam, non maximus massa porta. In diam ante, pulvinar aliquam nisl non, elementum hendrerit sapien. Vestibulum massa nunc, mattis non congue vitae, placerat in quam. Nam vulputate lectus metus, et dignissim erat varius a."; + responseStream.write(`

${loremIpsum1}

`); + await new Promise(r => setTimeout(r, 1000)); + + responseStream.write("

DONE!

"); + responseStream.write(""); + responseStream.end(); + } +); diff --git a/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..2da419c02 --- /dev/null +++ b/nodejs22.x/response-streaming/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,33 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 + +Description: > + Sample SAM Template for {{ cookiecutter.project_name }} + +Resources: + StreamingFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/ + Handler: index.handler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + Timeout: 10 + FunctionUrlConfig: + AuthType: AWS_IAM + InvokeMode: RESPONSE_STREAM + +Outputs: + StreamingFunction: + Description: "Streaming Lambda Function ARN" + Value: !GetAtt StreamingFunction.Arn + StreamingFunctionURL: + Description: "Streaming Lambda Function URL" + Value: !GetAtt StreamingFunctionUrl.FunctionUrl + + diff --git a/nodejs22.x/s3/.gitignore b/nodejs22.x/s3/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/s3/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/s3/README.md b/nodejs22.x/s3/README.md new file mode 100644 index 000000000..0627aa6ab --- /dev/null +++ b/nodejs22.x/s3/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS S3 Quick Start Application + +A cookiecutter template to create a NodeJS S3 Quick Start Application using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x --app-template quick-start-s3 --name s3-app` + +> **NOTE**: ``--name`` allows you to specify a different project folder name + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/s3/cookiecutter.json b/nodejs22.x/s3/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/s3/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/s3/setup.cfg b/nodejs22.x/s3/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/s3/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/s3/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/s3/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..2d980704f --- /dev/null +++ b/nodejs22.x/s3/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,2 @@ +.aws-sam +node_modules \ No newline at end of file diff --git a/nodejs22.x/s3/{{cookiecutter.project_name}}/README.md b/nodejs22.x/s3/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..0020be236 --- /dev/null +++ b/nodejs22.x/s3/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,151 @@ +# {{cookiecutter.project_name}} + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) command line interface (CLI). It includes the following files and folders: + +- `src` - Code for the application's Lambda function. +- `events` - Invocation events that you can use to invoke the function. +- `__tests__` - Unit tests for the application code. +- `template.yaml` - A template that defines the application's AWS resources. + +Resources for this project are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open-source plugin for popular IDEs that uses the AWS SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds step-through debugging for Lambda function code. + +To get started, see the following: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The AWS SAM CLI is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools: + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the npm package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community). + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Parameter AppBucketName**: This template includes a parameter to name the S3 bucket you will create as a part of the new application. This name needs to be globally unique. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +## Use the AWS SAM CLI to build and test locally + +Build your application by using the `sam build` command. + +```bash +my-application$ sam build +``` + +The AWS SAM CLI installs dependencies that are defined in `package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +my-application$ sam local invoke S3JsonLoggerFunction --event events/event-s3.json +``` + +## Add a resource to your application + +The application template uses AWS SAM to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources, such as functions, triggers, and APIs. For resources that aren't included in the [AWS SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use the standard [AWS CloudFormation resource types](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html). + +Update `template.yaml` to add a dead-letter queue to your application. In the **Resources** section, add a resource named **MyQueue** with the type **AWS::SQS::Queue**. Then add a property to the **AWS::Serverless::Function** resource named **DeadLetterQueue** that targets the queue's Amazon Resource Name (ARN), and a policy that grants the function permission to access the queue. + +``` +Resources: + MyQueue: + Type: AWS::SQS::Queue + S3JsonLoggerFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/s3-json-logger.s3JsonLoggerHandler + Runtime: nodejs22.x + DeadLetterQueue: + Type: SQS + TargetArn: !GetAtt MyQueue.Arn + Policies: + - SQSSendMessagePolicy: + QueueName: !GetAtt MyQueue.QueueName + - S3NewObjectEvent: + Type: S3 + Properties: + Bucket: !Ref AppBucket + Events: s3:ObjectCreated:* + Filter: + S3Key: + Rules: + - Name: suffix + Value: ".json" +``` + +The dead-letter queue is a location for Lambda to send events that could not be processed. It's only used if you invoke your function asynchronously, but it's useful here to show how you can modify your application's resources and function configuration. + +Deploy the updated application. + +```bash +my-application$ sam deploy +``` + +Open the [**Applications**](https://console.aws.amazon.com/lambda/home#/applications) page of the Lambda console, and choose your application. When the deployment completes, view the application resources on the **Overview** tab to see the new resource. Then, choose the function to see the updated configuration that specifies the dead-letter queue. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, the AWS SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs that are generated by your Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +**NOTE:** This command works for all Lambda functions, not just the ones you deploy using AWS SAM. + +```bash +my-application$ sam logs -n S3JsonLoggerFunction --stack-name sam-app --tail +``` + +**NOTE:** This uses the logical name of the function within the stack. This is the correct name to use when searching logs inside an AWS Lambda function within a CloudFormation stack, even if the deployed function name varies due to CloudFormation's unique resource name generation. + +You can find more information and examples about filtering Lambda function logs in the [AWS SAM CLI documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `__tests__` folder in this project. Use `npm` to install the [Jest test framework](https://jestjs.io/) and run unit tests. + +```bash +my-application$ npm install +my-application$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +For an introduction to the AWS SAM specification, the AWS SAM CLI, and serverless application concepts, see the [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). + +Next, you can use the AWS Serverless Application Repository to deploy ready-to-use apps that go beyond Hello World samples and learn how authors developed their applications. For more information, see the [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) and the [AWS Serverless Application Repository Developer Guide](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/what-is-serverlessrepo.html). diff --git a/nodejs22.x/s3/{{cookiecutter.project_name}}/__tests__/unit/handlers/s3-json-logger-handler.test.mjs b/nodejs22.x/s3/{{cookiecutter.project_name}}/__tests__/unit/handlers/s3-json-logger-handler.test.mjs new file mode 100644 index 000000000..4590eaf34 --- /dev/null +++ b/nodejs22.x/s3/{{cookiecutter.project_name}}/__tests__/unit/handlers/s3-json-logger-handler.test.mjs @@ -0,0 +1,52 @@ +import { mockClient } from 'aws-sdk-client-mock'; +import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; +import { s3JsonLoggerHandler } from '../../../src/handlers/s3-json-logger.mjs'; +import { jest } from '@jest/globals'; +import { Readable } from 'stream'; + +describe('Test s3JsonLoggerHandler', () => { + const s3Mock = mockClient(S3Client); + + beforeEach(() => { + s3Mock.reset(); + }); + + it('should read and log S3 objects', async () => { + const objectBody = '{"Test": "PASS"}'; + const getObjectResp = { + data: objectBody + }; + + const s3ResponseStream = new Readable({ + read() {} + }); + + s3ResponseStream.push(objectBody); + s3ResponseStream.push(null); + + const event = { + Records: [ + { + s3: { + bucket: { + name: "test-bucket" + }, + object: { + key: "test-key" + } + } + } + ] + } + + s3Mock.on(GetObjectCommand).resolves({ + Body: s3ResponseStream + }); + + console.info = jest.fn() + + await s3JsonLoggerHandler(event, null); + + expect(console.info).toHaveBeenCalledWith(objectBody); + }); +}); diff --git a/nodejs22.x/s3/{{cookiecutter.project_name}}/buildspec.yml b/nodejs22.x/s3/{{cookiecutter.project_name}}/buildspec.yml new file mode 100644 index 000000000..7c1dae85e --- /dev/null +++ b/nodejs22.x/s3/{{cookiecutter.project_name}}/buildspec.yml @@ -0,0 +1,22 @@ +version: 0.2 +phases: + install: + commands: + # Install all dependencies (including dependencies for running tests) + - npm install + pre_build: + commands: + # Discover and run unit tests in the '__tests__' directory + - npm run test + # Remove all unit tests to reduce the size of the package that will be ultimately uploaded to Lambda + - rm -rf ./__tests__ + # Remove all dependencies not needed for the Lambda deployment package (the packages from devDependencies in package.json) + - npm prune --production + build: + commands: + # Use AWS SAM to package the application by using AWS CloudFormation + - aws cloudformation package --template template.yaml --s3-bucket $S3_BUCKET --output-template template-export.yml +artifacts: + type: zip + files: + - template-export.yml diff --git a/nodejs22.x/s3/{{cookiecutter.project_name}}/events/event-s3.json b/nodejs22.x/s3/{{cookiecutter.project_name}}/events/event-s3.json new file mode 100644 index 000000000..b13b216d2 --- /dev/null +++ b/nodejs22.x/s3/{{cookiecutter.project_name}}/events/event-s3.json @@ -0,0 +1,38 @@ +{ + "Records": [ + { + "eventVersion": "2.0", + "eventSource": "aws:s3", + "awsRegion": "us-east-1", + "eventTime": "1970-01-01T00:00:00.000Z", + "eventName": "ObjectCreated:Put", + "userIdentity": { + "principalId": "EXAMPLE" + }, + "requestParameters": { + "sourceIPAddress": "127.0.0.1" + }, + "responseElements": { + "x-amz-request-id": "EXAMPLE123456789", + "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH" + }, + "s3": { + "s3SchemaVersion": "1.0", + "configurationId": "testConfigRule", + "bucket": { + "name": "example-bucket", + "ownerIdentity": { + "principalId": "EXAMPLE" + }, + "arn": "arn:aws:s3:::example-bucket" + }, + "object": { + "key": "test/key", + "size": 1024, + "eTag": "0123456789abcdef0123456789abcdef", + "sequencer": "0A1B2C3D4E5F678901" + } + } + } + ] +} diff --git a/nodejs22.x/s3/{{cookiecutter.project_name}}/package.json b/nodejs22.x/s3/{{cookiecutter.project_name}}/package.json new file mode 100644 index 000000000..52d84b4e0 --- /dev/null +++ b/nodejs22.x/s3/{{cookiecutter.project_name}}/package.json @@ -0,0 +1,29 @@ +{ + "name": "replaced-by-user-input", + "description": "replaced-by-user-input", + "version": "0.0.1", + "jest": { + "testMatch": [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[jt]s?(x)", + "**/__tests__/**/*.mjs?(x)", + "**/?(*.)+(spec|test).mjs?(x)" + ], + "moduleFileExtensions": [ + "mjs", + "js" + ] + }, + "private": true, + "dependencies": { + "@aws-sdk/client-s3": "^3.400.0", + "@aws-sdk/util-stream-node": "^3.374.0" + }, + "devDependencies": { + "aws-sdk-client-mock": "^2.0.0", + "jest": "^29.2.1" + }, + "scripts": { + "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js" + } +} diff --git a/nodejs22.x/s3/{{cookiecutter.project_name}}/src/handlers/s3-json-logger.mjs b/nodejs22.x/s3/{{cookiecutter.project_name}}/src/handlers/s3-json-logger.mjs new file mode 100644 index 000000000..a30815631 --- /dev/null +++ b/nodejs22.x/s3/{{cookiecutter.project_name}}/src/handlers/s3-json-logger.mjs @@ -0,0 +1,39 @@ +import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; +import { sdkStreamMixin } from '@aws-sdk/util-stream-node'; + +const s3 = new S3Client({ }); + +/** + * A Lambda function that logs the payload received from S3. + */ +export const s3JsonLoggerHandler = async (event, context) => { + // All log statements are written to CloudWatch by default. For more information, see + // https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-logging.html + const getObjectRequests = event.Records.map(record => { + const params = { + Bucket: record.s3.bucket.name, + Key: record.s3.object.key + }; + + const getObject = new GetObjectCommand(params); + + return s3.send(getObject).then((data) => + sdkStreamMixin(data.Body).transformToString().then((objectString) => { + console.info(objectString); + return Promise.resolve(objectString); + }) + .catch((err) => { + console.error("Error consuming response stream:", err); + return Promise.reject(err); + }) + ) + .catch((err) => { + console.error("Error calling S3 getObject:", err); + return Promise.reject(err); + }) + }); + + return Promise.all(getObjectRequests).then(() => { + console.debug('Complete!'); + }); +}; \ No newline at end of file diff --git a/nodejs22.x/s3/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/s3/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..bef1b9f84 --- /dev/null +++ b/nodejs22.x/s3/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,47 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + {{cookiecutter.project_name}} + +Parameters: + AppBucketName: + Type: String + Description: "REQUIRED: Unique S3 bucket name to use for the app." + +Resources: + S3JsonLoggerFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/s3-json-logger.s3JsonLoggerHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + MemorySize: 128 + Timeout: 60 + Policies: + S3ReadPolicy: + BucketName: !Ref AppBucketName + Events: + S3NewObjectEvent: + Type: S3 + Properties: + Bucket: !Ref AppBucket + Events: s3:ObjectCreated:* + Filter: + S3Key: + Rules: + - Name: suffix + Value: ".json" + AppBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: !Ref AppBucketName + BucketEncryption: + ServerSideEncryptionConfiguration: + - BucketKeyEnabled: true + VersioningConfiguration: + Status: Enabled diff --git a/nodejs22.x/scratch/.gitignore b/nodejs22.x/scratch/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/scratch/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/scratch/README.md b/nodejs22.x/scratch/README.md new file mode 100644 index 000000000..71e0f421d --- /dev/null +++ b/nodejs22.x/scratch/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS "From Scratch" Quick Start Application + +A cookiecutter template to create a NodeJS Basic Quick Start Application using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x --app-template quick-start-from-scratch --name sam-app` + +> **NOTE**: ``--name`` allows you to specify a different project folder name + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/scratch/cookiecutter.json b/nodejs22.x/scratch/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/scratch/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/scratch/setup.cfg b/nodejs22.x/scratch/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/scratch/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/scratch/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/scratch/{{cookiecutter.project_name}}/.gitignore new file mode 100755 index 000000000..b512c09d4 --- /dev/null +++ b/nodejs22.x/scratch/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/nodejs22.x/scratch/{{cookiecutter.project_name}}/README.md b/nodejs22.x/scratch/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..fe8764ad1 --- /dev/null +++ b/nodejs22.x/scratch/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,140 @@ +# {{cookiecutter.project_name}} + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) command line interface (CLI). It includes the following files and folders: + +- `src` - Code for the application's Lambda function. +- `events` - Invocation events that you can use to invoke the function. +- `__tests__` - Unit tests for the application code. +- `template.yaml` - A template that defines the application's AWS resources. + +Resources for this project are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open-source plugin for popular IDEs that uses the AWS SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds step-through debugging for Lambda function code. + +To get started, see the following: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The AWS SAM CLI is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools: + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the npm package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community). + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +## Use the AWS SAM CLI to build and test locally + +Build your application by using the `sam build` command. + +```bash +my-application$ sam build +``` + +The AWS SAM CLI installs dependencies that are defined in `package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +my-application$ sam local invoke helloFromLambdaFunction --no-event +``` + +## Add a resource to your application + +The application template uses AWS SAM to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources, such as functions, triggers, and APIs. For resources that aren't included in the [AWS SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use the standard [AWS CloudFormation resource types](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html). + +Update `template.yaml` to add a dead-letter queue to your application. In the **Resources** section, add a resource named **MyQueue** with the type **AWS::SQS::Queue**. Then add a property to the **AWS::Serverless::Function** resource named **DeadLetterQueue** that targets the queue's Amazon Resource Name (ARN), and a policy that grants the function permission to access the queue. + +``` +Resources: + MyQueue: + Type: AWS::SQS::Queue + helloFromLambdaFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/hello-from-lambda.helloFromLambdaHandler + Runtime: nodejs22.x + DeadLetterQueue: + Type: SQS + TargetArn: !GetAtt MyQueue.Arn + Policies: + - SQSSendMessagePolicy: + QueueName: !GetAtt MyQueue.QueueName +``` + +The dead-letter queue is a location for Lambda to send events that could not be processed. It's only used if you invoke your function asynchronously, but it's useful here to show how you can modify your application's resources and function configuration. + +Deploy the updated application. + +```bash +my-application$ sam deploy +``` + +Open the [**Applications**](https://console.aws.amazon.com/lambda/home#/applications) page of the Lambda console, and choose your application. When the deployment completes, view the application resources on the **Overview** tab to see the new resource. Then, choose the function to see the updated configuration that specifies the dead-letter queue. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, the AWS SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs that are generated by your Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +**NOTE:** This command works for all Lambda functions, not just the ones you deploy using AWS SAM. + +```bash +my-application$ sam logs -n helloFromLambdaFunction --stack-name sam-app --tail +``` + +**NOTE:** This uses the logical name of the function within the stack. This is the correct name to use when searching logs inside an AWS Lambda function within a CloudFormation stack, even if the deployed function name varies due to CloudFormation's unique resource name generation. + +You can find more information and examples about filtering Lambda function logs in the [AWS SAM CLI documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `__tests__` folder in this project. Use `npm` to install the [Jest test framework](https://jestjs.io/) and run unit tests. + +```bash +my-application$ npm install +my-application$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +For an introduction to the AWS SAM specification, the AWS SAM CLI, and serverless application concepts, see the [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). + +Next, you can use the AWS Serverless Application Repository to deploy ready-to-use apps that go beyond Hello World samples and learn how authors developed their applications. For more information, see the [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) and the [AWS Serverless Application Repository Developer Guide](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/what-is-serverlessrepo.html). diff --git a/nodejs22.x/scratch/{{cookiecutter.project_name}}/__tests__/unit/handlers/hello-from-lambda.test.mjs b/nodejs22.x/scratch/{{cookiecutter.project_name}}/__tests__/unit/handlers/hello-from-lambda.test.mjs new file mode 100644 index 000000000..76d12cc51 --- /dev/null +++ b/nodejs22.x/scratch/{{cookiecutter.project_name}}/__tests__/unit/handlers/hello-from-lambda.test.mjs @@ -0,0 +1,20 @@ +// Import helloFromLambdaHandler function from hello-from-lambda.mjs +import { helloFromLambdaHandler } from '../../../src/handlers/hello-from-lambda.mjs'; + +// This includes all tests for helloFromLambdaHandler() +describe('Test for hello-from-lambda', function () { + // This test invokes helloFromLambdaHandler() and compare the result + it('Verifies successful response', async () => { + // Invoke helloFromLambdaHandler() + const result = await helloFromLambdaHandler(); + /* + The expected result should match the return from your Lambda function. + e.g. + if you change from `const message = 'Hello from Lambda!';` to `const message = 'Hello World!';` in hello-from-lambda.mjs + you should change the following line to `const expectedResult = 'Hello World!';` + */ + const expectedResult = 'Hello from Lambda!'; + // Compare the result with the expected result + expect(result).toEqual(expectedResult); + }); +}); diff --git a/nodejs22.x/scratch/{{cookiecutter.project_name}}/buildspec.yml b/nodejs22.x/scratch/{{cookiecutter.project_name}}/buildspec.yml new file mode 100755 index 000000000..86e7a9ebf --- /dev/null +++ b/nodejs22.x/scratch/{{cookiecutter.project_name}}/buildspec.yml @@ -0,0 +1,25 @@ +version: 0.2 + +phases: + install: + commands: + # Install all dependencies (including dependencies for running tests) + - npm install + pre_build: + commands: + # Discover and run unit tests in the '__tests__' directory + - npm run test + # Remove all unit tests to reduce the size of the package that will be ultimately uploaded to Lambda + - rm -rf ./__tests__ + # Remove all dependencies not needed for the Lambda deployment package (the packages from devDependencies in package.json) + - npm prune --production + build: + commands: + # Use AWS SAM to package the application by using AWS CloudFormation + - aws cloudformation package --template template.yaml --s3-bucket $S3_BUCKET --output-template template-export.yml +artifacts: + type: zip + files: + - template-export.yml + + diff --git a/nodejs22.x/scratch/{{cookiecutter.project_name}}/package.json b/nodejs22.x/scratch/{{cookiecutter.project_name}}/package.json new file mode 100755 index 000000000..64d1cf5eb --- /dev/null +++ b/nodejs22.x/scratch/{{cookiecutter.project_name}}/package.json @@ -0,0 +1,24 @@ +{ + "name": "replaced-by-user-input", + "description": "replaced-by-user-input", + "version": "0.0.1", + "private": true, + "devDependencies": { + "jest": "^29.2.1" + }, + "scripts": { + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" + }, + "jest": { + "testMatch": [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[jt]s?(x)", + "**/__tests__/**/*.mjs?(x)", + "**/?(*.)+(spec|test).mjs?(x)" + ], + "moduleFileExtensions": [ + "mjs", + "js" + ] + } +} diff --git a/nodejs22.x/scratch/{{cookiecutter.project_name}}/src/handlers/hello-from-lambda.mjs b/nodejs22.x/scratch/{{cookiecutter.project_name}}/src/handlers/hello-from-lambda.mjs new file mode 100755 index 000000000..0fb1c6b40 --- /dev/null +++ b/nodejs22.x/scratch/{{cookiecutter.project_name}}/src/handlers/hello-from-lambda.mjs @@ -0,0 +1,12 @@ +/** + * A Lambda function that returns a static string + */ +export const helloFromLambdaHandler = async () => { + // If you change this message, you will need to change hello-from-lambda.test.mjs + const message = 'Hello from Lambda!'; + + // All log statements are written to CloudWatch + console.info(`${message}`); + + return message; +} diff --git a/nodejs22.x/scratch/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/scratch/{{cookiecutter.project_name}}/template.yaml new file mode 100755 index 000000000..d12efbca5 --- /dev/null +++ b/nodejs22.x/scratch/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,38 @@ +# This is the SAM template that represents the architecture of your serverless application +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html + +# The AWSTemplateFormatVersion identifies the capabilities of the template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html +AWSTemplateFormatVersion: 2010-09-09 +Description: >- + {{cookiecutter.project_name}} + +# Transform section specifies one or more macros that AWS CloudFormation uses to process your template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html +Transform: +- AWS::Serverless-2016-10-31 + +# Resources declares the AWS resources that you want to include in the stack +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html +Resources: + # Each Lambda function is defined by properties: + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + + # This is a Lambda function config associated with the source code: hello-from-lambda.js + helloFromLambdaFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/hello-from-lambda.helloFromLambdaHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + MemorySize: 128 + Timeout: 100 + Description: A Lambda function that returns a static string. + Policies: + # Give Lambda basic execution Permission to the helloFromLambda + - AWSLambdaBasicExecutionRole diff --git a/nodejs22.x/sns/.gitignore b/nodejs22.x/sns/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/sns/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/sns/README.md b/nodejs22.x/sns/README.md new file mode 100644 index 000000000..0369c4ee7 --- /dev/null +++ b/nodejs22.x/sns/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS SNS Quick Start Application + +A cookiecutter template to create a NodeJS SNS Quick Start Application using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x --app-template quick-start-sns --name sns-app` + +> **NOTE**: ``--name`` allows you to specify a different project folder name + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/sns/cookiecutter.json b/nodejs22.x/sns/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/sns/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/sns/setup.cfg b/nodejs22.x/sns/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/sns/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/sns/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/sns/{{cookiecutter.project_name}}/.gitignore new file mode 100755 index 000000000..b512c09d4 --- /dev/null +++ b/nodejs22.x/sns/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/nodejs22.x/sns/{{cookiecutter.project_name}}/README.md b/nodejs22.x/sns/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..425871001 --- /dev/null +++ b/nodejs22.x/sns/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,146 @@ +# {{cookiecutter.project_name}} + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) command line interface (CLI). It includes the following files and folders: + +- `src` - Code for the application's Lambda function. +- `events` - Invocation events that you can use to invoke the function. +- `__tests__` - Unit tests for the application code. +- `template.yaml` - A template that defines the application's AWS resources. + +Resources for this project are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open-source plugin for popular IDEs that uses the AWS SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds step-through debugging for Lambda function code. + +To get started, see the following: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The AWS SAM CLI is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools: + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the npm package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community). + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +## Use the AWS SAM CLI to build and test locally + +Build your application by using the `sam build` command. + +```bash +my-application$ sam build +``` + +The AWS SAM CLI installs dependencies that are defined in `package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +my-application$ sam local invoke SNSPayloadLogger --event events/event-sns.json +``` + +## Add a resource to your application + +The application template uses AWS SAM to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources, such as functions, triggers, and APIs. For resources that aren't included in the [AWS SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use the standard [AWS CloudFormation resource types](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html). + +Update `template.yaml` to add a dead-letter queue to your application. In the **Resources** section, add a resource named **MyQueue** with the type **AWS::SQS::Queue**. Then add a property to the **AWS::Serverless::Function** resource named **DeadLetterQueue** that targets the queue's Amazon Resource Name (ARN), and a policy that grants the function permission to access the queue. + +``` +Resources: + MyQueue: + Type: AWS::SQS::Queue + SNSPayloadLogger: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/sns-payload-logger.snsPayloadLoggerHandler + Runtime: nodejs22.x + DeadLetterQueue: + Type: SQS + TargetArn: !GetAtt MyQueue.Arn + Policies: + - SQSSendMessagePolicy: + QueueName: !GetAtt MyQueue.QueueName +``` + +The dead-letter queue is a location for Lambda to send events that could not be processed. It's only used if you invoke your function asynchronously, but it's useful here to show how you can modify your application's resources and function configuration. + +Deploy the updated application. + +```bash +my-application$ sam package \ + --output-template-file packaged.yaml \ + --s3-bucket BUCKET_NAME +my-application$ sam deploy \ + --template-file packaged.yaml \ + --stack-name sam-app \ + --capabilities CAPABILITY_IAM +``` + +Open the [**Applications**](https://console.aws.amazon.com/lambda/home#/applications) page of the Lambda console, and choose your application. When the deployment completes, view the application resources on the **Overview** tab to see the new resource. Then, choose the function to see the updated configuration that specifies the dead-letter queue. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, the AWS SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs that are generated by your Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +**NOTE:** This command works for all Lambda functions, not just the ones you deploy using AWS SAM. + +```bash +my-application$ sam logs -n SNSPayloadLogger --stack-name sam-app --tail +``` + +**NOTE:** This uses the logical name of the function within the stack. This is the correct name to use when searching logs inside an AWS Lambda function within a CloudFormation stack, even if the deployed function name varies due to CloudFormation's unique resource name generation. + +You can find more information and examples about filtering Lambda function logs in the [AWS SAM CLI documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `__tests__` folder in this project. Use `npm` to install the [Jest test framework](https://jestjs.io/) and run unit tests. + +```bash +my-application$ npm install +my-application$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +For an introduction to the AWS SAM specification, the AWS SAM CLI, and serverless application concepts, see the [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). + +Next, you can use the AWS Serverless Application Repository to deploy ready-to-use apps that go beyond Hello World samples and learn how authors developed their applications. For more information, see the [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) and the [AWS Serverless Application Repository Developer Guide](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/what-is-serverlessrepo.html). diff --git a/nodejs22.x/sns/{{cookiecutter.project_name}}/__tests__/unit/handlers/sns-payload-logger.test.mjs b/nodejs22.x/sns/{{cookiecutter.project_name}}/__tests__/unit/handlers/sns-payload-logger.test.mjs new file mode 100644 index 000000000..2d864ae5d --- /dev/null +++ b/nodejs22.x/sns/{{cookiecutter.project_name}}/__tests__/unit/handlers/sns-payload-logger.test.mjs @@ -0,0 +1,24 @@ +// Import snsPayloadLoggerHandler function from sns-payload-logger.mjs +import { snsPayloadLoggerHandler } from '../../../src/handlers/sns-payload-logger.mjs'; +import { jest } from '@jest/globals' + +describe('Test for sns-payload-logger', function () { + // This test invokes the sns-payload-logger Lambda function and verifies that the received payload is logged + it('Verifies the payload is logged', async() => { + // Mock console.log statements so we can verify them. For more information, see + // https://jestjs.io/docs/en/mock-functions.html + console.info = jest.fn() + + // Create a sample payload with SNS message format + var payload = { + TopicArn: "arn:aws:sns:us-west-2:123456789012:SimpleTopic", + Message: "This is a notification from SNS", + Subject: "SNS Notification" + } + + await snsPayloadLoggerHandler(payload, null) + + // Verify that console.info has been called with the expected payload + expect(console.info).toHaveBeenCalledWith(payload) + }); +}); diff --git a/nodejs22.x/sns/{{cookiecutter.project_name}}/buildspec.yml b/nodejs22.x/sns/{{cookiecutter.project_name}}/buildspec.yml new file mode 100755 index 000000000..86e7a9ebf --- /dev/null +++ b/nodejs22.x/sns/{{cookiecutter.project_name}}/buildspec.yml @@ -0,0 +1,25 @@ +version: 0.2 + +phases: + install: + commands: + # Install all dependencies (including dependencies for running tests) + - npm install + pre_build: + commands: + # Discover and run unit tests in the '__tests__' directory + - npm run test + # Remove all unit tests to reduce the size of the package that will be ultimately uploaded to Lambda + - rm -rf ./__tests__ + # Remove all dependencies not needed for the Lambda deployment package (the packages from devDependencies in package.json) + - npm prune --production + build: + commands: + # Use AWS SAM to package the application by using AWS CloudFormation + - aws cloudformation package --template template.yaml --s3-bucket $S3_BUCKET --output-template template-export.yml +artifacts: + type: zip + files: + - template-export.yml + + diff --git a/nodejs22.x/sns/{{cookiecutter.project_name}}/events/event-sns.json b/nodejs22.x/sns/{{cookiecutter.project_name}}/events/event-sns.json new file mode 100644 index 000000000..baa02e78b --- /dev/null +++ b/nodejs22.x/sns/{{cookiecutter.project_name}}/events/event-sns.json @@ -0,0 +1,5 @@ +{ + "TopicArn": "arn:aws:sns:us-west-2:123456789012:SimpleTopic", + "Message": "This is a notification from SNS", + "Subject": "SNS Notification" +} \ No newline at end of file diff --git a/nodejs22.x/sns/{{cookiecutter.project_name}}/package.json b/nodejs22.x/sns/{{cookiecutter.project_name}}/package.json new file mode 100755 index 000000000..64d1cf5eb --- /dev/null +++ b/nodejs22.x/sns/{{cookiecutter.project_name}}/package.json @@ -0,0 +1,24 @@ +{ + "name": "replaced-by-user-input", + "description": "replaced-by-user-input", + "version": "0.0.1", + "private": true, + "devDependencies": { + "jest": "^29.2.1" + }, + "scripts": { + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" + }, + "jest": { + "testMatch": [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[jt]s?(x)", + "**/__tests__/**/*.mjs?(x)", + "**/?(*.)+(spec|test).mjs?(x)" + ], + "moduleFileExtensions": [ + "mjs", + "js" + ] + } +} diff --git a/nodejs22.x/sns/{{cookiecutter.project_name}}/src/handlers/sns-payload-logger.mjs b/nodejs22.x/sns/{{cookiecutter.project_name}}/src/handlers/sns-payload-logger.mjs new file mode 100755 index 000000000..fe1bfbb55 --- /dev/null +++ b/nodejs22.x/sns/{{cookiecutter.project_name}}/src/handlers/sns-payload-logger.mjs @@ -0,0 +1,8 @@ +/** + * A Lambda function that logs the payload received from SNS. + */ +export const snsPayloadLoggerHandler = async (event, context) => { + // All log statements are written to CloudWatch by default. For more information, see + // https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-logging.html + console.info(event); +} diff --git a/nodejs22.x/sns/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/sns/{{cookiecutter.project_name}}/template.yaml new file mode 100755 index 000000000..210103834 --- /dev/null +++ b/nodejs22.x/sns/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,48 @@ +# This is the SAM template that represents the architecture of your serverless application +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html + +# The AWSTemplateFormatVersion identifies the capabilities of the template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html +AWSTemplateFormatVersion: 2010-09-09 +Description: >- + {{cookiecutter.project_name}} + +# Transform section specifies one or more macros that AWS CloudFormation uses to process your template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html +Transform: +- AWS::Serverless-2016-10-31 + +# Resources declares the AWS resources that you want to include in the stack +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html +Resources: + # This is an SNS Topic with all default configuration properties. To learn more about the available options, see + # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sns-topic.html + SimpleTopic: + Type: AWS::SNS::Topic + + # This is the Lambda function definition associated with the source code: sns-payload-logger.js. For all available properties, see + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + SNSPayloadLogger: + Type: AWS::Serverless::Function + Properties: + Description: A Lambda function that logs the payload of messages sent to an associated SNS topic. + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + Handler: src/handlers/sns-payload-logger.snsPayloadLoggerHandler + # This property associates this Lambda function with the SNS topic defined above, so that whenever the topic + # receives a message, the Lambda function is invoked + Events: + SNSTopicEvent: + Type: SNS + Properties: + Topic: !Ref SimpleTopic + MemorySize: 128 + Timeout: 100 + Policies: + # Give Lambda basic execution Permission to the helloFromLambda + - AWSLambdaBasicExecutionRole diff --git a/nodejs22.x/sqs/.gitignore b/nodejs22.x/sqs/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/sqs/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/sqs/README.md b/nodejs22.x/sqs/README.md new file mode 100644 index 000000000..e8143e99a --- /dev/null +++ b/nodejs22.x/sqs/README.md @@ -0,0 +1,20 @@ +# {{cookiecutter.project_name}} + +A cookiecutter template to create a NodeJS SQS Quick Start Application using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x --app-template quick-start-sqs --name sqs-app` + +> **NOTE**: ``--name`` allows you to specify a different project folder name + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/sqs/cookiecutter.json b/nodejs22.x/sqs/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/sqs/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/sqs/setup.cfg b/nodejs22.x/sqs/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/sqs/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/sqs/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/sqs/{{cookiecutter.project_name}}/.gitignore new file mode 100755 index 000000000..b512c09d4 --- /dev/null +++ b/nodejs22.x/sqs/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/nodejs22.x/sqs/{{cookiecutter.project_name}}/README.md b/nodejs22.x/sqs/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..5f0fef20c --- /dev/null +++ b/nodejs22.x/sqs/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,141 @@ +# {{cookiecutter.project_name}} + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) command line interface (CLI). It includes the following files and folders: + +- `src` - Code for the application's Lambda function. +- `events` - Invocation events that you can use to invoke the function. +- `__tests__` - Unit tests for the application code. +- `template.yaml` - A template that defines the application's AWS resources. + +Resources for this project are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open-source plugin for popular IDEs that uses the AWS SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds step-through debugging for Lambda function code. + +To get started, see the following: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The AWS SAM CLI is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools: + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the npm package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community). + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +## Use the AWS SAM CLI to build and test locally + +Build your application by using the `sam build` command. + +```bash +my-application$ sam build +``` + +The AWS SAM CLI installs dependencies that are defined in `package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +my-application$ sam local invoke SQSPayloadLogger --event events/event-sqs.json +``` + +## Add a resource to your application + +The application template uses AWS SAM to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources, such as functions, triggers, and APIs. For resources that aren't included in the [AWS SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use the standard [AWS CloudFormation resource types](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html). + +Update `template.yaml` to add a dead-letter queue to your application. In the **Resources** section, add a resource named **MyQueue** with the type **AWS::SQS::Queue**. Then add a property to the **AWS::Serverless::Function** resource named **DeadLetterQueue** that targets the queue's Amazon Resource Name (ARN), and a policy that grants the function permission to access the queue. + +``` +Resources: + MyQueue: + Type: AWS::SQS::Queue + SQSPayloadLogger: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/sqs-payload-logger.sqsPayloadLoggerHandler + Runtime: nodejs22.x + DeadLetterQueue: + Type: SQS + TargetArn: !GetAtt MyQueue.Arn + Policies: + - SQSSendMessagePolicy: + QueueName: !GetAtt MyQueue.QueueName +``` + +The dead-letter queue is a location for Lambda to send events that could not be processed. It's only used if you invoke your function asynchronously, but it's useful here to show how you can modify your application's resources and function configuration. + +Deploy the updated application. + +```bash +my-application$ sam deploy +``` + +Open the [**Applications**](https://console.aws.amazon.com/lambda/home#/applications) page of the Lambda console, and choose your application. When the deployment completes, view the application resources on the **Overview** tab to see the new resource. Then, choose the function to see the updated configuration that specifies the dead-letter queue. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, the AWS SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs that are generated by your Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +**NOTE:** This command works for all Lambda functions, not just the ones you deploy using AWS SAM. + +```bash +my-application$ sam logs -n SQSPayloadLogger --stack-name sam-app --tail +``` + +**NOTE:** This uses the logical name of the function within the stack. This is the correct name to use when searching logs inside an AWS Lambda function within a CloudFormation stack, even if the deployed function name varies due to CloudFormation's unique resource name generation. + +You can find more information and examples about filtering Lambda function logs in the [AWS SAM CLI documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `__tests__` folder in this project. Use `npm` to install the [Jest test framework](https://jestjs.io/) and run unit tests. + +```bash +my-application$ npm install +my-application$ npm run test +``` + +## Cleanup + +To delete the sample application and the bucket that you created, use the AWS CLI. + +```bash +my-application$ sam delete --stack-name sam-app +my-application$ aws s3 rb s3://BUCKET_NAME +``` + +## Resources + +For an introduction to the AWS SAM specification, the AWS SAM CLI, and serverless application concepts, see the [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). + +Next, you can use the AWS Serverless Application Repository to deploy ready-to-use apps that go beyond Hello World samples and learn how authors developed their applications. For more information, see the [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) and the [AWS Serverless Application Repository Developer Guide](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/what-is-serverlessrepo.html). diff --git a/nodejs22.x/sqs/{{cookiecutter.project_name}}/__tests__/unit/handlers/sqs-payload-logger.test.mjs b/nodejs22.x/sqs/{{cookiecutter.project_name}}/__tests__/unit/handlers/sqs-payload-logger.test.mjs new file mode 100644 index 000000000..de00cb6c6 --- /dev/null +++ b/nodejs22.x/sqs/{{cookiecutter.project_name}}/__tests__/unit/handlers/sqs-payload-logger.test.mjs @@ -0,0 +1,30 @@ +// Import sqsPayloadLoggerHandler function from sqs-payload-logger.mjs +import { sqsPayloadLoggerHandler } from '../../../src/handlers/sqs-payload-logger.mjs'; +import { jest } from '@jest/globals'; + +describe('Test for sqs-payload-logger', function () { + // This test invokes the sqs-payload-logger Lambda function and verifies that the received payload is logged + it('Verifies the payload is logged', async () => { + // Mock console.log statements so we can verify them. For more information, see + // https://jestjs.io/docs/en/mock-functions.html + console.info = jest.fn() + + // Create a sample payload with SQS message format + var payload = { + DelaySeconds: 10, + MessageAttributes: { + "Sender": { + DataType: "String", + StringValue: "sqs-payload-logger" + } + }, + MessageBody: "This message was sent by the sqs-payload-logger Lambda function", + QueueUrl: "SQS_QUEUE_URL" + } + + await sqsPayloadLoggerHandler(payload, null) + + // Verify that console.info has been called with the expected payload + expect(console.info).toHaveBeenCalledWith(JSON.stringify(payload)) + }); +}); diff --git a/nodejs22.x/sqs/{{cookiecutter.project_name}}/buildspec.yml b/nodejs22.x/sqs/{{cookiecutter.project_name}}/buildspec.yml new file mode 100755 index 000000000..adb0a1019 --- /dev/null +++ b/nodejs22.x/sqs/{{cookiecutter.project_name}}/buildspec.yml @@ -0,0 +1,23 @@ +version: 0.2 + +phases: + install: + commands: + # Install all dependencies (including dependencies for running tests) + - npm install + pre_build: + commands: + # Discover and run unit tests in the '__tests__' directory + - npm run test + # Remove all unit tests to reduce the size of the package that will be ultimately uploaded to Lambda + - rm -rf ./__tests__ + # Remove all dependencies not needed for the Lambda deployment package (the packages from devDependencies in package.json) + - npm prune --production + build: + commands: + # Use AWS SAM to package the application by using AWS CloudFormation + - aws cloudformation package --template template.yaml --s3-bucket $S3_BUCKET --output-template template-export.yml +artifacts: + type: zip + files: + - template-export.yml diff --git a/nodejs22.x/sqs/{{cookiecutter.project_name}}/events/event-sqs.json b/nodejs22.x/sqs/{{cookiecutter.project_name}}/events/event-sqs.json new file mode 100644 index 000000000..f691c8780 --- /dev/null +++ b/nodejs22.x/sqs/{{cookiecutter.project_name}}/events/event-sqs.json @@ -0,0 +1,11 @@ +{ + "DelaySeconds": 10, + "MessageAttributes": { + "Sender": { + "DataType": "String", + "StringValue": "sqs-payload-logger" + } + }, + "MessageBody": "This message was sent by the sqs-payload-logger Lambda function", + "QueueUrl": "SQS_QUEUE_URL" +} diff --git a/nodejs22.x/sqs/{{cookiecutter.project_name}}/package.json b/nodejs22.x/sqs/{{cookiecutter.project_name}}/package.json new file mode 100755 index 000000000..64d1cf5eb --- /dev/null +++ b/nodejs22.x/sqs/{{cookiecutter.project_name}}/package.json @@ -0,0 +1,24 @@ +{ + "name": "replaced-by-user-input", + "description": "replaced-by-user-input", + "version": "0.0.1", + "private": true, + "devDependencies": { + "jest": "^29.2.1" + }, + "scripts": { + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" + }, + "jest": { + "testMatch": [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[jt]s?(x)", + "**/__tests__/**/*.mjs?(x)", + "**/?(*.)+(spec|test).mjs?(x)" + ], + "moduleFileExtensions": [ + "mjs", + "js" + ] + } +} diff --git a/nodejs22.x/sqs/{{cookiecutter.project_name}}/src/handlers/sqs-payload-logger.mjs b/nodejs22.x/sqs/{{cookiecutter.project_name}}/src/handlers/sqs-payload-logger.mjs new file mode 100755 index 000000000..1f1c4e2c5 --- /dev/null +++ b/nodejs22.x/sqs/{{cookiecutter.project_name}}/src/handlers/sqs-payload-logger.mjs @@ -0,0 +1,8 @@ +/** + * A Lambda function that logs the payload received from SQS. + */ +export const sqsPayloadLoggerHandler = async (event, context) => { + // All log statements are written to CloudWatch by default. For more information, see + // https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-logging.html + console.info(JSON.stringify(event)); +} diff --git a/nodejs22.x/sqs/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/sqs/{{cookiecutter.project_name}}/template.yaml new file mode 100755 index 000000000..6cb57492d --- /dev/null +++ b/nodejs22.x/sqs/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,48 @@ +# This is the SAM template that represents the architecture of your serverless application +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html + +# The AWSTemplateFormatVersion identifies the capabilities of the template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html +AWSTemplateFormatVersion: 2010-09-09 +Description: >- + {{cookiecutter.project_name}} + +# Transform section specifies one or more macros that AWS CloudFormation uses to process your template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html +Transform: +- AWS::Serverless-2016-10-31 + +# Resources declares the AWS resources that you want to include in the stack +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html +Resources: + # This is an SQS queue with all default configuration properties. To learn more about the available options, see + # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html + SimpleQueue: + Type: AWS::SQS::Queue + + # This is the Lambda function definition associated with the source code: sqs-payload-logger.js. For all available properties, see + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + SQSPayloadLogger: + Type: AWS::Serverless::Function + Properties: + Description: A Lambda function that logs the payload of messages sent to an associated SQS queue. + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + Handler: src/handlers/sqs-payload-logger.sqsPayloadLoggerHandler + # This property associates this Lambda function with the SQS queue defined above, so that whenever the queue + # receives a message, the Lambda function is invoked + Events: + SQSQueueEvent: + Type: SQS + Properties: + Queue: !GetAtt SimpleQueue.Arn + MemorySize: 128 + Timeout: 25 # Chosen to be less than the default SQS Visibility Timeout of 30 seconds + Policies: + # Give Lambda basic execution Permission to the helloFromLambda + - AWSLambdaBasicExecutionRole diff --git a/nodejs22.x/step-func/.gitignore b/nodejs22.x/step-func/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/step-func/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/step-func/README.md b/nodejs22.x/step-func/README.md new file mode 100644 index 000000000..a97a06707 --- /dev/null +++ b/nodejs22.x/step-func/README.md @@ -0,0 +1,23 @@ +# Cookiecutter NodeJS Step Functions Sample App (Stock Trader) for SAM based Serverless App + +A cookiecutter template to create a NodeJS Step Functions Sample App (Stock Trader) boilerplate using +[Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +This application creates a mock stock trading workflow which runs on a pre-defined schedule. It demonstrates the power of Step Functions to orchestrate Lambda functions and other AWS resources to form complex and robust workflows, coupled with event-driven development using Amazon EventBridge. + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x` + +> **NOTE**: ``--name`` allows you to specify a different project folder name (`sam-app` is the default) + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/step-func/cookiecutter.json b/nodejs22.x/step-func/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/step-func/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/step-func/setup.cfg b/nodejs22.x/step-func/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/step-func/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/step-func/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..5854f05ec --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1,207 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,node,linux,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test +.env*.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Storybook build outputs +.out +.storybook-out +storybook-static + +# rollup.js default build output +dist/ + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Temporary folders +tmp/ +temp/ + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,node,linux,windows,sam diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/README.md b/nodejs22.x/step-func/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..8ba8ea4cf --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,137 @@ +# {{ cookiecutter.project_name }} + +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders: + +- functions - Code for the application's Lambda functions to check the value of, buy, or sell shares of a stock. +- statemachines - Definition for the state machine that orchestrates the stock trading workflow. +- template.yaml - A template that defines the application's AWS resources. + +This application creates a mock stock trading workflow which runs on a pre-defined schedule (note that the schedule is disabled by default to avoid incurring charges). It demonstrates the power of Step Functions to orchestrate Lambda functions and other AWS resources to form complex and robust workflows, coupled with event-driven development using Amazon EventBridge. + +AWS Step Functions lets you coordinate multiple AWS services into serverless workflows so you can build and update apps quickly. Using Step Functions, you can design and run workflows that stitch together services, such as AWS Lambda, AWS Fargate, and Amazon SageMaker, into feature-rich applications. + +The application uses several AWS resources, including Step Functions state machines, Lambda functions and an EventBridge rule trigger. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test the Lambda functions within your application, you can use the AWS Toolkit. The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +The AWS Toolkit for VS Code includes full support for state machine visualization, enabling you to visualize your state machine in real time as you build. The AWS Toolkit for VS Code includes a language server for Amazon States Language, which lints your state machine definition to highlight common errors, provides auto-complete support, and code snippets for each state, enabling you to build state machines faster. + +## Deploy the sample application + +The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. + +To use the SAM CLI, you need the following tools: + +* SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the NPM package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your State Machine ARN in the output values displayed after deployment. + +## Use the SAM CLI to build and test locally + +Build the Lambda functions in your application with the `sam build --use-container` command. + +```bash +{{ cookiecutter.project_name }}$ sam build +``` + +The SAM CLI installs dependencies defined in `functions/*/package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +## Add a resource to your application +The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. + +```bash +{{ cookiecutter.project_name }}$ sam logs -n StockCheckerFunction --stack-name {{ cookiecutter.project_name }} --tail +``` + +You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `functions/*/tests` folder in this project. Use NPM to install the [Mocha test framework](https://mochajs.org/) and run unit tests. + +```bash +{{ cookiecutter.project_name }}$ cd functions/stock-checker +stock-checker$ npm install +stock-checker$ npm run test +``` + +## Testing Step Functions locally +Developers can test their state machines locally using Step Functions Local before deploying them to an AWS account. Often, developers want to test the control and data flows of their state machine executions in isolation, without any dependency on service integration availability. This is now possible using the [Mocked Service Integrations for Step Functions Local](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local-test-sm-exec.html). + +The `statemachine/tests/MockConfigFile.json` contains various test cases with mocked service integrations. + +- `HappyPathSellStockTest` - This test mocks the output of Check Stock Value, Sell Stock and Record Transaction using LambdaMockedSuccessValueGreaterThan50, SellStockLambdaMockedResponse and RecordTransactionDDBMockedResponse respectively. +- `HappyPathBuyStockTest` - This test mocks the output of Check Stock Value, Buy Stock and Record Transaction using LambdaMockedSuccessValueLowerThan50, BuyStockLambdaMockedResponse and RecordTransactionDDBMockedResponse respectively. +- `CheckStockRetryOnServiceExceptionTest` and `SellStockRetryOnServiceExceptionTest`- These tests mocks the failure with exponential retries. + +This application also provides a `makefile` which has all the required commands to run docker [`make run`], create state machine and execute tests [`make all`], and checking history [`make history`]. + +On a terminal window, first start with running docker: + +```bash +make run +``` + +On a different terminal window/tab, you can then run: + +```bash +make all +``` + +Finally, you can check history of each execution by running: + +```bash +make history +``` + +Check [`makefile`](./makefile) for details + + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. + +Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/.npmignore b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/.npmignore new file mode 100644 index 000000000..e7e1fb04f --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/app.mjs b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/app.mjs new file mode 100644 index 000000000..9db79a08a --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/app.mjs @@ -0,0 +1,30 @@ +import { randomBytes } from "crypto"; + +function getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)) + 1; +} + +/** + * Sample Lambda function which mocks the operation of buying a random number of shares for a stock. + * For demonstration purposes, this Lambda function does not actually perform any actual transactions. It simply returns a mocked result. + * + * @param {Object} event - Input event to the Lambda function + * @param {Object} context - Lambda Context runtime methods and attributes + * + * @returns {Object} object - Object containing details of the stock buying transaction + * + */ +export const lambdaHandler = async (event, context) => { + // Get the price of the stock provided as input + const stock_price = event["stock_price"]; + var date = new Date(); + // Mocked result of a stock buying transaction + let transaction_result = { + 'id': randomBytes(16).toString("hex"), // Unique ID for the transaction + 'price': stock_price.toString(), // Price of each share + 'type': "buy", // Type of transaction(buy/ sell) + 'qty': getRandomInt(10).toString(), // Number of shares bought / sold(We are mocking this as a random integer between 1 and 10) + 'timestamp': date.toISOString(), // Timestamp of the when the transaction was completed + } + return transaction_result +}; diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/package.json b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/package.json new file mode 100644 index 000000000..53e3571a0 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/package.json @@ -0,0 +1,17 @@ +{ + "name": "stock_checker", + "version": "1.0.0", + "description": "Mock stock checker for NodeJS", + "main": "app.js", + "repository": "TODO", + "author": "SAM CLI", + "license": "MIT", + "dependencies": {}, + "scripts": { + "test": "mocha tests/unit/" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.1.0" + } +} diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/tests/unit/test-handler.mjs b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/tests/unit/test-handler.mjs new file mode 100644 index 000000000..b581b2a15 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-buyer/tests/unit/test-handler.mjs @@ -0,0 +1,21 @@ +'use strict'; + +import { lambdaHandler } from '../../app.mjs'; +import { expect } from 'chai'; +var event, context; + +describe('Tests Stock Buyer', function () { + it('Verifies response', async () => { + event = { + 'stock_price': 25 + } + const result = await lambdaHandler(event, context) + + expect(result).to.be.an('object'); + expect(result).to.have.all.keys('id', 'price', 'type', 'timestamp', 'qty'); + expect(result.type).to.equal('buy'); + let qty = parseInt(result.qty); + expect(qty).to.be.at.least(1); + expect(qty).to.be.at.most(10); + }); +}); diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/.npmignore b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/.npmignore new file mode 100644 index 000000000..e7e1fb04f --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/app.mjs b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/app.mjs new file mode 100644 index 000000000..fd1733dd3 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/app.mjs @@ -0,0 +1,19 @@ +function getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)); +} + +/** + * Sample Lambda function which mocks the operation of checking the current price of a stock. + * For demonstration purposes this Lambda function simply returns a random integer between 0 and 100 as the stock price. + * + * @param {Object} event - Input event to the Lambda function + * @param {Object} context - Lambda Context runtime methods and attributes + * + * @returns {Object} object - Object containing the current price of the stock + * + */ +export const lambdaHandler = async (event, context) => { + // Check current price of the stock + const stock_price = getRandomInt(100) // Current stock price is mocked as a random integer between 0 and 100 + return { 'stock_price': stock_price } +}; diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/package.json b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/package.json new file mode 100644 index 000000000..53e3571a0 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/package.json @@ -0,0 +1,17 @@ +{ + "name": "stock_checker", + "version": "1.0.0", + "description": "Mock stock checker for NodeJS", + "main": "app.js", + "repository": "TODO", + "author": "SAM CLI", + "license": "MIT", + "dependencies": {}, + "scripts": { + "test": "mocha tests/unit/" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.1.0" + } +} diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/tests/unit/test-handler.mjs b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/tests/unit/test-handler.mjs new file mode 100644 index 000000000..fb5a6c614 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-checker/tests/unit/test-handler.mjs @@ -0,0 +1,16 @@ +'use strict'; + +import { lambdaHandler } from '../../app.mjs'; +import { expect } from 'chai'; +var event, context; + +describe('Tests Stock Checker', function () { + it('Verifies response', async () => { + const result = await lambdaHandler(event, context) + + expect(result).to.be.an('object'); + expect(result.stock_price).to.be.an('number'); + expect(result.stock_price).to.be.at.least(0); + expect(result.stock_price).to.be.at.most(100); + }); +}); diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/.npmignore b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/.npmignore new file mode 100644 index 000000000..e7e1fb04f --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/app.mjs b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/app.mjs new file mode 100644 index 000000000..9b3eeb7d2 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/app.mjs @@ -0,0 +1,30 @@ +import { randomBytes } from "crypto"; + +function getRandomInt(max) { + return Math.floor(Math.random() * Math.floor(max)) + 1; +} + +/** + * Sample Lambda function which mocks the operation of selling a random number of shares for a stock. + * For demonstration purposes, this Lambda function does not actually perform any actual transactions. It simply returns a mocked result. + * + * @param {Object} event - Input event to the Lambda function + * @param {Object} context - Lambda Context runtime methods and attributes + * + * @returns {Object} object - Object containing details of the stock selling transaction + * + */ +export const lambdaHandler = async (event, context) => { + // Get the price of the stock provided as input + const stock_price = event["stock_price"] + var date = new Date(); + // Mocked result of a stock selling transaction + let transaction_result = { + 'id': randomBytes(16).toString("hex"), // Unique ID for the transaction + 'price': stock_price.toString(), // Price of each share + 'type': "sell", // Type of transaction(buy/ sell) + 'qty': getRandomInt(10).toString(), // Number of shares bought / sold(We are mocking this as a random integer between 1 and 10) + 'timestamp': date.toISOString(), // Timestamp of the when the transaction was completed + } + return transaction_result +}; diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/package.json b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/package.json new file mode 100644 index 000000000..53e3571a0 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/package.json @@ -0,0 +1,17 @@ +{ + "name": "stock_checker", + "version": "1.0.0", + "description": "Mock stock checker for NodeJS", + "main": "app.js", + "repository": "TODO", + "author": "SAM CLI", + "license": "MIT", + "dependencies": {}, + "scripts": { + "test": "mocha tests/unit/" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.1.0" + } +} diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/tests/unit/test-handler.mjs b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/tests/unit/test-handler.mjs new file mode 100644 index 000000000..86a6b44bb --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/functions/stock-seller/tests/unit/test-handler.mjs @@ -0,0 +1,21 @@ +'use strict'; + +import { lambdaHandler } from '../../app.mjs'; +import { expect } from 'chai'; +var event, context; + +describe('Tests Stock Seller', function () { + it('Verifies response', async () => { + event = { + 'stock_price': 75 + } + const result = await lambdaHandler(event, context) + + expect(result).to.be.an('object'); + expect(result).to.have.all.keys('id', 'price', 'type', 'timestamp', 'qty'); + expect(result.type).to.equal('sell'); + let qty = parseInt(result.qty); + expect(qty).to.be.at.least(1); + expect(qty).to.be.at.most(10); + }); +}); diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/makefile b/nodejs22.x/step-func/{{cookiecutter.project_name}}/makefile new file mode 100644 index 000000000..3e9440fd3 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/makefile @@ -0,0 +1,71 @@ +ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) + +run: + docker run -p 8083:8083 \ + --mount type=bind,readonly,source=$(ROOT_DIR)/statemachine/test/MockConfigFile.json,destination=/home/StepFunctionsLocal/MockConfigFile.json \ + -e SFN_MOCK_CONFIG="/home/StepFunctionsLocal/MockConfigFile.json" \ + amazon/aws-stepfunctions-local + +create: + sed -E -e 's/\$$\{.+\}/arn:aws:lambda:us-east-1:123456789012:function:mock/' statemachine/stock_trader.asl.json > statemachine/test/mocked.test.asl.json + aws stepfunctions create-state-machine \ + --endpoint-url http://localhost:8083 \ + --definition file://statemachine/test/mocked.test.asl.json \ + --name "StockTradingLocalTesting" \ + --role-arn "arn:aws:iam::123456789012:role/DummyRole" \ + --no-cli-pager + rm statemachine/test/mocked.test.asl.json + +happypathsellstocktest: + aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --name HappyPathSellStockTest \ + --state-machine arn:aws:states:us-east-1:123456789012:stateMachine:StockTradingLocalTesting#HappyPathSellStockTest \ + --no-cli-pager + +happypathbuystocktest: + aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --name HappyPathBuyStockTest \ + --state-machine arn:aws:states:us-east-1:123456789012:stateMachine:StockTradingLocalTesting#HappyPathBuyStockTest \ + --no-cli-pager + +checkstockerrorwithretry: + aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --name checkStockErrorExecution \ + --state-machine arn:aws:states:us-east-1:123456789012:stateMachine:StockTradingLocalTesting#CheckStockRetryOnServiceExceptionTest \ + --no-cli-pager + +sellstockerrorwithretry: + aws stepfunctions start-execution \ + --endpoint http://localhost:8083 \ + --name sellStockErrorExecution \ + --state-machine arn:aws:states:us-east-1:123456789012:stateMachine:StockTradingLocalTesting#SellStockRetryOnServiceExceptionTest \ + --no-cli-pager + + +all: create happypathsellstocktest happypathbuystocktest checkstockerrorwithretry sellstockerrorwithretry + +happypathsellstocktest-h: + aws stepfunctions get-execution-history \ + --endpoint http://localhost:8083 \ + --execution-arn arn:aws:states:us-east-1:123456789012:execution:StockTradingLocalTesting:HappyPathSellStockTest \ + --query 'events[?type==`TaskStateExited` && stateExitedEventDetails.name==`Record Transaction`]' \ + --no-cli-pager + +checkstockerror: + aws stepfunctions get-execution-history \ + --endpoint http://localhost:8083 \ + --execution-arn arn:aws:states:us-east-1:123456789012:execution:StockTradingLocalTesting:checkStockErrorExecution \ + --query 'events[?type==`TaskStateExited` && stateExitedEventDetails.name==`Record Transaction` ]' \ + --no-cli-pager + +sellstockerror: + aws stepfunctions get-execution-history \ + --endpoint http://localhost:8083 \ + --execution-arn arn:aws:states:us-east-1:123456789012:execution:StockTradingLocalTesting:sellStockErrorExecution \ + --query 'events[?type==`TaskStateExited` && stateExitedEventDetails.name==`Record Transaction` ]' \ + --no-cli-pager + +history: happypathsellstocktest-h checkstockerror sellstockerror \ No newline at end of file diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/statemachine/stock_trader.asl.json b/nodejs22.x/step-func/{{cookiecutter.project_name}}/statemachine/stock_trader.asl.json new file mode 100644 index 000000000..c29281b34 --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/statemachine/stock_trader.asl.json @@ -0,0 +1,97 @@ +{ + "Comment": "A state machine that does mock stock trading.", + "StartAt": "Check Stock Value", + "States": { + "Check Stock Value": { + "Type": "Task", + "Resource": "${StockCheckerFunctionArn}", + "Retry": [ + { + "ErrorEquals": [ + "States.TaskFailed" + ], + "IntervalSeconds": 15, + "MaxAttempts": 5, + "BackoffRate": 1.5 + } + ], + "Next": "Buy or Sell?" + }, + "Buy or Sell?": { + "Type": "Choice", + "Choices": [ + { + "Variable": "$.stock_price", + "NumericLessThanEquals": 50, + "Next": "Buy Stock" + } + ], + "Default": "Sell Stock" + }, + "Sell Stock": { + "Type": "Task", + "Resource": "${StockSellerFunctionArn}", + "Retry": [ + { + "ErrorEquals": [ + "States.TaskFailed" + ], + "IntervalSeconds": 2, + "MaxAttempts": 3, + "BackoffRate": 1 + } + ], + "Next": "Record Transaction" + }, + "Buy Stock": { + "Type": "Task", + "Resource": "${StockBuyerFunctionArn}", + "Retry": [ + { + "ErrorEquals": [ + "States.TaskFailed" + ], + "IntervalSeconds": 2, + "MaxAttempts": 3, + "BackoffRate": 1 + } + ], + "Next": "Record Transaction" + }, + "Record Transaction": { + "Type": "Task", + "Resource": "${DDBPutItem}", + "Parameters": { + "TableName": "${DDBTable}", + "Item": { + "Id": { + "S.$": "$.id" + }, + "Type": { + "S.$": "$.type" + }, + "Price": { + "N.$": "$.price" + }, + "Quantity": { + "N.$": "$.qty" + }, + "Timestamp": { + "S.$": "$.timestamp" + } + } + }, + "Retry": [ + { + "ErrorEquals": [ + "States.TaskFailed" + ], + "IntervalSeconds": 20, + "MaxAttempts": 5, + "BackoffRate": 10 + } + ], + "End": true + } + } +} \ No newline at end of file diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/statemachine/test/MockConfigFile.json b/nodejs22.x/step-func/{{cookiecutter.project_name}}/statemachine/test/MockConfigFile.json new file mode 100644 index 000000000..20db6d24c --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/statemachine/test/MockConfigFile.json @@ -0,0 +1,142 @@ +{ + "StateMachines": { + "StockTradingLocalTesting": { + "TestCases": { + "HappyPathSellStockTest": { + "Check Stock Value": "LambdaMockedSuccessValueGreaterThan50", + "Sell Stock": "SellStockLambdaMockedResponse", + "Record Transaction": "RecordTransactionDDBMockedResponse" + }, + "HappyPathBuyStockTest": { + "Check Stock Value": "LambdaMockedSuccessValueLowerThan50", + "Buy Stock": "BuyStockLambdaMockedResponse", + "Record Transaction": "RecordTransactionDDBMockedResponse" + }, + "CheckStockRetryOnServiceExceptionTest": { + "Check Stock Value": "CheckStockLambdaMockedRetryWithSuccess", + "Sell Stock": "SellStockLambdaMockedResponse", + "Record Transaction": "RecordTransactionDDBMockedResponse" + }, + "SellStockRetryOnServiceExceptionTest": { + "Check Stock Value": "LambdaMockedSuccessValueGreaterThan50", + "Sell Stock": "SellStockLambdaMockedRetryWithSuccess", + "Record Transaction": "RecordTransactionDDBMockedResponse" + } + } + } + }, + "MockedResponses": { + "LambdaMockedSuccessValueGreaterThan50": { + "0": { + "Return": { + "stock_price": 98 + } + } + }, + "LambdaMockedSuccessValueLowerThan50": { + "0": { + "Return": { + "stock_price": 32 + } + } + }, + "SellStockLambdaMockedResponse": { + "0": { + "Return": { + "id": "8cd8e4d92fe716b7cedff5636ab9d24e", + "price": "98", + "type": "sell", + "qty": "7", + "timestamp": "2022-06-22T21:14:18.228Z" + } + } + }, + "BuyStockLambdaMockedResponse": { + "0": { + "Return": { + "id": "8cd8e4d92fe716b7cedff5636ab9d24e", + "price": "32", + "type": "buy", + "qty": "7", + "timestamp": "2022-06-22T21:14:18.228Z" + } + } + }, + "RecordTransactionDDBMockedResponse": { + "0": { + "Return": { + "resourceType": "dynamodb", + "resource": "putItem", + "output": { + "SdkHttpMetadata": { + "AllHttpHeaders": { + "Server": [ + "Server" + ], + "Connection": [ + "keep-alive" + ], + "x-amzn-RequestId": [ + "LAT641GUVNBRI0DC1HC6S4MDH3VV4KQNSO5AEMVJF66Q9ASUAAJG" + ], + "x-amz-crc32": [ + "2745614147" + ], + "Content-Length": [ + "2" + ], + "Date": [ + "Wed, 22 Jun 2022 21:14:18 GMT" + ], + "Content-Type": [ + "application/x-amz-json-1.0" + ] + }, + "HttpHeaders": { + "Connection": "keep-alive", + "Content-Length": "2", + "Content-Type": "application/x-amz-json-1.0", + "Date": "Wed, 22 Jun 2022 21:14:18 GMT", + "Server": "Server", + "x-amz-crc32": "2745614147", + "x-amzn-RequestId": "LAT641GUVNBRI0DC1HC6S4MDH3VV4KQNSO5AEMVJF66Q9ASUAAJG" + }, + "HttpStatusCode": 200 + }, + "SdkResponseMetadata": { + "RequestId": "LAT641GUVNBRI0DC1HC6S4MDH3VV4KQNSO5AEMVJF66Q9ASUAAJG" + } + } + } + } + }, + "CheckStockLambdaMockedRetryWithSuccess": { + "0-2": { + "Throw": { + "Error": "States.TaskFailed" + } + }, + "3": { + "Return": { + "stock_price": 89 + } + } + }, + "SellStockLambdaMockedRetryWithSuccess": { + "0-2": { + "Throw": { + "Error": "States.TaskFailed" + } + }, + "3": { + "Return": { + "id": "8cd8e4d92fe716b7cedff5636ab9d24e", + "price": "98", + "type": "sell", + "qty": "5", + "timestamp": "2022-06-22T21:14:18.228Z" + } + } + } + } +} \ No newline at end of file diff --git a/nodejs22.x/step-func/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/step-func/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..230774dfc --- /dev/null +++ b/nodejs22.x/step-func/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,94 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: > + {{ cookiecutter.project_name }} + + Sample SAM Template for {{ cookiecutter.project_name }} + +Resources: + StockTradingStateMachine: + Type: AWS::Serverless::StateMachine # More info about State Machine Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-statemachine.html + Properties: + DefinitionUri: statemachine/stock_trader.asl.json + DefinitionSubstitutions: + StockCheckerFunctionArn: !GetAtt StockCheckerFunction.Arn + StockSellerFunctionArn: !GetAtt StockSellerFunction.Arn + StockBuyerFunctionArn: !GetAtt StockBuyerFunction.Arn + DDBPutItem: !Sub arn:${AWS::Partition}:states:::dynamodb:putItem + DDBTable: !Ref TransactionTable + Events: + HourlyTradingSchedule: + Type: Schedule # More info about Schedule Event Source: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-statemachine-schedule.html + Properties: + Description: Schedule to run the stock trading state machine every hour + Enabled: False # This schedule is disabled by default to avoid incurring charges. + Schedule: "rate(1 hour)" + Policies: # Find out more about SAM policy templates: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html + - LambdaInvokePolicy: + FunctionName: !Ref StockCheckerFunction + - LambdaInvokePolicy: + FunctionName: !Ref StockSellerFunction + - LambdaInvokePolicy: + FunctionName: !Ref StockBuyerFunction + - DynamoDBWritePolicy: + TableName: !Ref TransactionTable + + StockCheckerFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html + Properties: + CodeUri: functions/stock-checker/ + Handler: app.lambdaHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + + StockSellerFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: functions/stock-seller/ + Handler: app.lambdaHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + + StockBuyerFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: functions/stock-buyer/ + Handler: app.lambdaHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + + TransactionTable: + Type: AWS::Serverless::SimpleTable # More info about SimpleTable Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-simpletable.html + Properties: + PrimaryKey: + Name: Id + Type: String + ProvisionedThroughput: + ReadCapacityUnits: 1 + WriteCapacityUnits: 1 + +Outputs: + # StockTradingStateMachineHourlyTradingSchedule is an implicit Schedule event rule created out of Events key under Serverless::StateMachine + # Find out more about other implicit resources you can reference within SAM + # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html + StockTradingStateMachineArn: + Description: "Stock Trading state machine ARN" + Value: !Ref StockTradingStateMachine + StockTradingStateMachineRole: + Description: "IAM Role created for Stock Trading state machine based on the specified SAM Policy Templates" + Value: !GetAtt StockTradingStateMachineRole.Arn diff --git a/nodejs22.x/web/.gitignore b/nodejs22.x/web/.gitignore new file mode 100644 index 000000000..41bcace31 --- /dev/null +++ b/nodejs22.x/web/.gitignore @@ -0,0 +1,229 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,linux,python,windows,sam + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +pytestdebug.log + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ +doc/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +# .env +.env/ +.venv/ +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# operating system-related files +# file properties cache/storage on macOS +*.DS_Store +# thumbnail cache on Windows +Thumbs.db + +# profiling data +.prof + + +### SAM ### +# Ignore build directories for the AWS Serverless Application Model (SAM) +# Info: https://aws.amazon.com/serverless/sam/ +# Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html + +**/.aws-sam + +### Windows ### +# Windows thumbnail cache files +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/osx,linux,python,windows,sam diff --git a/nodejs22.x/web/README.md b/nodejs22.x/web/README.md new file mode 100644 index 000000000..628f3d70b --- /dev/null +++ b/nodejs22.x/web/README.md @@ -0,0 +1,20 @@ +# Cookiecutter NodeJS Quick Start Web Application + +A cookiecutter template to create a NodeJS Quick Start Web Application using [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model). + +## Requirements + +* [AWS SAM CLI](https://github.com/awslabs/aws-sam-cli) + +## Usage + +Generate a boilerplate template in your current project directory using the following syntax: + +* **NodeJS 22**: `sam init --runtime nodejs22.x --app-template quick-start-web --name web-app` + +> **NOTE**: ``--name`` allows you to specify a different project folder name + +# Credits + +* This project has been generated with [Cookiecutter](https://github.com/audreyr/cookiecutter) + diff --git a/nodejs22.x/web/cookiecutter.json b/nodejs22.x/web/cookiecutter.json new file mode 100644 index 000000000..bc6045867 --- /dev/null +++ b/nodejs22.x/web/cookiecutter.json @@ -0,0 +1,10 @@ +{ + "project_name": "Name of the project", + "runtime": "nodejs22.x", + "architectures": { + "value": ["x86_64", "arm64"] + }, + "_copy_without_render": [ + ".gitignore" + ] +} \ No newline at end of file diff --git a/nodejs22.x/web/setup.cfg b/nodejs22.x/web/setup.cfg new file mode 100644 index 000000000..eee4ab11a --- /dev/null +++ b/nodejs22.x/web/setup.cfg @@ -0,0 +1,2 @@ +[install] +prefix= \ No newline at end of file diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/.gitignore b/nodejs22.x/web/{{cookiecutter.project_name}}/.gitignore new file mode 100644 index 000000000..3c3629e64 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/README.md b/nodejs22.x/web/{{cookiecutter.project_name}}/README.md new file mode 100644 index 000000000..7a386155c --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/README.md @@ -0,0 +1,160 @@ +# {{cookiecutter.project_name}} + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) command line interface (CLI). It includes the following files and folders: + +- `src` - Code for the application's Lambda function. +- `events` - Invocation events that you can use to invoke the function. +- `__tests__` - Unit tests for the application code. +- `template.yaml` - A template that defines the application's AWS resources. + +The application uses several AWS resources, including Lambda functions, an API Gateway API, and Amazon DynamoDB tables. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open-source plugin for popular IDEs that uses the AWS SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds step-through debugging for Lambda function code. + +To get started, see the following: + +* [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +* [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +* [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The AWS SAM CLI is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools: + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html). +* Node.js - [Install Node.js 22](https://nodejs.org/en/), including the npm package management tool. +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community). + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +The API Gateway endpoint API will be displayed in the outputs when the deployment is complete. + +## Use the AWS SAM CLI to build and test locally + +Build your application by using the `sam build` command. + +```bash +my-application$ sam build +``` + +The AWS SAM CLI installs dependencies that are defined in `package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +my-application$ sam local invoke putItemFunction --event events/event-post-item.json +my-application$ sam local invoke getAllItemsFunction --event events/event-get-all-items.json +``` + +The AWS SAM CLI can also emulate your application's API. Use the `sam local start-api` command to run the API locally on port 3000. + +```bash +my-application$ sam local start-api +my-application$ curl http://localhost:3000/ +``` + +The AWS SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. + +```yaml + Events: + Api: + Type: Api + Properties: + Path: / + Method: GET +``` + +## Add a resource to your application +The application template uses AWS SAM to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources, such as functions, triggers, and APIs. For resources that aren't included in the [AWS SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use the standard [AWS CloudFormation resource types](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html). + +Update `template.yaml` to add a dead-letter queue to your application. In the **Resources** section, add a resource named **MyQueue** with the type **AWS::SQS::Queue**. Then add a property to the **AWS::Serverless::Function** resource named **DeadLetterQueue** that targets the queue's Amazon Resource Name (ARN), and a policy that grants the function permission to access the queue. + +``` +Resources: + MyQueue: + Type: AWS::SQS::Queue + getAllItemsFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/get-all-items.getAllItemsHandler + Runtime: nodejs22.x + DeadLetterQueue: + Type: SQS + TargetArn: !GetAtt MyQueue.Arn + Policies: + - SQSSendMessagePolicy: + QueueName: !GetAtt MyQueue.QueueName +``` + +The dead-letter queue is a location for Lambda to send events that could not be processed. It's only used if you invoke your function asynchronously, but it's useful here to show how you can modify your application's resources and function configuration. + +Deploy the updated application. + +```bash +my-application$ sam deploy +``` + +Open the [**Applications**](https://console.aws.amazon.com/lambda/home#/applications) page of the Lambda console, and choose your application. When the deployment completes, view the application resources on the **Overview** tab to see the new resource. Then, choose the function to see the updated configuration that specifies the dead-letter queue. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, the AWS SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs that are generated by your Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +**NOTE:** This command works for all Lambda functions, not just the ones you deploy using AWS SAM. + +```bash +my-application$ sam logs -n putItemFunction --stack-name sam-app --tail +``` + +**NOTE:** This uses the logical name of the function within the stack. This is the correct name to use when searching logs inside an AWS Lambda function within a CloudFormation stack, even if the deployed function name varies due to CloudFormation's unique resource name generation. + +You can find more information and examples about filtering Lambda function logs in the [AWS SAM CLI documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Unit tests + +Tests are defined in the `__tests__` folder in this project. Use `npm` to install the [Jest test framework](https://jestjs.io/) and run unit tests. + +```bash +my-application$ npm install +my-application$ npm run test +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name {{ cookiecutter.project_name }} +``` + +## Resources + +For an introduction to the AWS SAM specification, the AWS SAM CLI, and serverless application concepts, see the [AWS SAM Developer Guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html). + +Next, you can use the AWS Serverless Application Repository to deploy ready-to-use apps that go beyond Hello World samples and learn how authors developed their applications. For more information, see the [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) and the [AWS Serverless Application Repository Developer Guide](https://docs.aws.amazon.com/serverlessrepo/latest/devguide/what-is-serverlessrepo.html). diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/get-all-items.test.mjs b/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/get-all-items.test.mjs new file mode 100644 index 000000000..f9b642f19 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/get-all-items.test.mjs @@ -0,0 +1,38 @@ +// Import getAllItemsHandler function from get-all-items.mjs +import { getAllItemsHandler } from '../../../src/handlers/get-all-items.mjs'; +// Import dynamodb from aws-sdk +import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb'; +import { mockClient } from "aws-sdk-client-mock"; + +// This includes all tests for getAllItemsHandler() +describe('Test getAllItemsHandler', () => { + const ddbMock = mockClient(DynamoDBDocumentClient); + + beforeEach(() => { + ddbMock.reset(); + }); + + it('should return ids', async () => { + const items = [{ id: 'id1' }, { id: 'id2' }]; + + // Return the specified value whenever the spied scan function is called + ddbMock.on(ScanCommand).resolves({ + Items: items, + }); + + const event = { + httpMethod: 'GET' + }; + + // Invoke helloFromLambdaHandler() + const result = await getAllItemsHandler(event); + + const expectedResult = { + statusCode: 200, + body: JSON.stringify(items) + }; + + // Compare the result with the expected result + expect(result).toEqual(expectedResult); + }); +}); diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/get-by-id.test.mjs b/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/get-by-id.test.mjs new file mode 100644 index 000000000..2d97c98d7 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/get-by-id.test.mjs @@ -0,0 +1,43 @@ +// Import getByIdHandler function from get-by-id.mjs +import { getByIdHandler } from '../../../src/handlers/get-by-id.mjs'; +// Import dynamodb from aws-sdk +import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'; +import { mockClient } from "aws-sdk-client-mock"; + +// This includes all tests for getByIdHandler() +describe('Test getByIdHandler', () => { + const ddbMock = mockClient(DynamoDBDocumentClient); + + beforeEach(() => { + ddbMock.reset(); + }); + + // This test invokes getByIdHandler() and compare the result + it('should get item by id', async () => { + const item = { id: 'id1' }; + + // Return the specified value whenever the spied get function is called + ddbMock.on(GetCommand).resolves({ + Item: item, + }); + + const event = { + httpMethod: 'GET', + pathParameters: { + id: 'id1' + } + }; + + // Invoke getByIdHandler() + const result = await getByIdHandler(event); + + const expectedResult = { + statusCode: 200, + body: JSON.stringify(item) + }; + + // Compare the result with the expected result + expect(result).toEqual(expectedResult); + }); +}); + \ No newline at end of file diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/put-item.test.mjs b/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/put-item.test.mjs new file mode 100644 index 000000000..64ea140d1 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/__tests__/unit/handlers/put-item.test.mjs @@ -0,0 +1,40 @@ +// Import putItemHandler function from put-item.mjs +import { putItemHandler } from '../../../src/handlers/put-item.mjs'; +// Import dynamodb from aws-sdk +import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; +import { mockClient } from "aws-sdk-client-mock"; +// This includes all tests for putItemHandler() +describe('Test putItemHandler', function () { + const ddbMock = mockClient(DynamoDBDocumentClient); + + beforeEach(() => { + ddbMock.reset(); + }); + + // This test invokes putItemHandler() and compare the result + it('should add id to the table', async () => { + const returnedItem = { id: 'id1', name: 'name1' }; + + // Return the specified value whenever the spied put function is called + ddbMock.on(PutCommand).resolves({ + returnedItem + }); + + const event = { + httpMethod: 'POST', + body: '{"id": "id1","name": "name1"}' + }; + + // Invoke putItemHandler() + const result = await putItemHandler(event); + + const expectedResult = { + statusCode: 200, + body: JSON.stringify(returnedItem) + }; + + // Compare the result with the expected result + expect(result).toEqual(expectedResult); + }); +}); + \ No newline at end of file diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/buildspec.yml b/nodejs22.x/web/{{cookiecutter.project_name}}/buildspec.yml new file mode 100644 index 000000000..7c1dae85e --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/buildspec.yml @@ -0,0 +1,22 @@ +version: 0.2 +phases: + install: + commands: + # Install all dependencies (including dependencies for running tests) + - npm install + pre_build: + commands: + # Discover and run unit tests in the '__tests__' directory + - npm run test + # Remove all unit tests to reduce the size of the package that will be ultimately uploaded to Lambda + - rm -rf ./__tests__ + # Remove all dependencies not needed for the Lambda deployment package (the packages from devDependencies in package.json) + - npm prune --production + build: + commands: + # Use AWS SAM to package the application by using AWS CloudFormation + - aws cloudformation package --template template.yaml --s3-bucket $S3_BUCKET --output-template template-export.yml +artifacts: + type: zip + files: + - template-export.yml diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/env.json b/nodejs22.x/web/{{cookiecutter.project_name}}/env.json new file mode 100644 index 000000000..c6073eb26 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/env.json @@ -0,0 +1,11 @@ +{ + "getAllItemsFunction": { + "SAMPLE_TABLE": "" + }, + "getByIdFunction": { + "SAMPLE_TABLE": "" + }, + "putItemFunction": { + "SAMPLE_TABLE": "" + } + } \ No newline at end of file diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-get-all-items.json b/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-get-all-items.json new file mode 100644 index 000000000..3a0cb5f77 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-get-all-items.json @@ -0,0 +1,3 @@ +{ + "httpMethod": "GET" +} \ No newline at end of file diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-get-by-id.json b/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-get-by-id.json new file mode 100644 index 000000000..63a64fb45 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-get-by-id.json @@ -0,0 +1,6 @@ +{ + "httpMethod": "GET", + "pathParameters": { + "id": "id1" + } +} \ No newline at end of file diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-post-item.json b/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-post-item.json new file mode 100644 index 000000000..6367003e5 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/events/event-post-item.json @@ -0,0 +1,4 @@ +{ + "httpMethod": "POST", + "body": "{\"id\": \"id1\",\"name\": \"name1\"}" +} \ No newline at end of file diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/package.json b/nodejs22.x/web/{{cookiecutter.project_name}}/package.json new file mode 100644 index 000000000..11e7ea689 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/package.json @@ -0,0 +1,30 @@ +{ + "name": "delete-test-01", + "description": "delete-test-01-description", + "version": "0.0.1", + "private": true, + "dependencies": { + "@aws-sdk/client-dynamodb": "^3.398.0", + "@aws-sdk/lib-dynamodb": "^3.398.0" + }, + "devDependencies": { + "aws-sdk-client-mock": "^2.0.0", + "jest": "^29.2.1" + }, + "scripts": { + "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js" + }, + "jest": { + "testMatch": [ + "**/__tests__/**/*.[jt]s?(x)", + "**/?(*.)+(spec|test).[jt]s?(x)", + "**/__tests__/**/*.mjs?(x)", + "**/?(*.)+(spec|test).mjs?(x)" + ], + "moduleFileExtensions": [ + "mjs", + "js" + ] + } +} + diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/get-all-items.mjs b/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/get-all-items.mjs new file mode 100644 index 000000000..d76ba9af9 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/get-all-items.mjs @@ -0,0 +1,44 @@ +// Create clients and set shared const values outside of the handler. + +// Create a DocumentClient that represents the query to add an item +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient, ScanCommand } from '@aws-sdk/lib-dynamodb'; +const client = new DynamoDBClient({}); +const ddbDocClient = DynamoDBDocumentClient.from(client); + +// Get the DynamoDB table name from environment variables +const tableName = process.env.SAMPLE_TABLE; + +/** + * A simple example includes a HTTP get method to get all items from a DynamoDB table. + */ +export const getAllItemsHandler = async (event) => { + if (event.httpMethod !== 'GET') { + throw new Error(`getAllItems only accept GET method, you tried: ${event.httpMethod}`); + } + // All log statements are written to CloudWatch + console.info('received:', event); + + // get all items from the table (only first 1MB data, you can use `LastEvaluatedKey` to get the rest of data) + // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property + // https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html + var params = { + TableName : tableName + }; + + try { + const data = await ddbDocClient.send(new ScanCommand(params)); + var items = data.Items; + } catch (err) { + console.log("Error", err); + } + + const response = { + statusCode: 200, + body: JSON.stringify(items) + }; + + // All log statements are written to CloudWatch + console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); + return response; +} diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/get-by-id.mjs b/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/get-by-id.mjs new file mode 100644 index 000000000..958e24969 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/get-by-id.mjs @@ -0,0 +1,47 @@ +// Create clients and set shared const values outside of the handler. + +// Create a DocumentClient that represents the query to add an item +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'; +const client = new DynamoDBClient({}); +const ddbDocClient = DynamoDBDocumentClient.from(client); + +// Get the DynamoDB table name from environment variables +const tableName = process.env.SAMPLE_TABLE; + +/** + * A simple example includes a HTTP get method to get one item by id from a DynamoDB table. + */ +export const getByIdHandler = async (event) => { + if (event.httpMethod !== 'GET') { + throw new Error(`getMethod only accept GET method, you tried: ${event.httpMethod}`); + } + // All log statements are written to CloudWatch + console.info('received:', event); + + // Get id from pathParameters from APIGateway because of `/{id}` at template.yaml + const id = event.pathParameters.id; + + // Get the item from the table + // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#get-property + var params = { + TableName : tableName, + Key: { id: id }, + }; + + try { + const data = await ddbDocClient.send(new GetCommand(params)); + var item = data.Item; + } catch (err) { + console.log("Error", err); + } + + const response = { + statusCode: 200, + body: JSON.stringify(item) + }; + + // All log statements are written to CloudWatch + console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); + return response; +} diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/put-item.mjs b/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/put-item.mjs new file mode 100644 index 000000000..fb26f652e --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/src/handlers/put-item.mjs @@ -0,0 +1,49 @@ +// Create clients and set shared const values outside of the handler. + +// Create a DocumentClient that represents the query to add an item +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; +const client = new DynamoDBClient({}); +const ddbDocClient = DynamoDBDocumentClient.from(client); + +// Get the DynamoDB table name from environment variables +const tableName = process.env.SAMPLE_TABLE; + +/** + * A simple example includes a HTTP post method to add one item to a DynamoDB table. + */ +export const putItemHandler = async (event) => { + if (event.httpMethod !== 'POST') { + throw new Error(`postMethod only accepts POST method, you tried: ${event.httpMethod} method.`); + } + // All log statements are written to CloudWatch + console.info('received:', event); + + // Get id and name from the body of the request + const body = JSON.parse(event.body); + const id = body.id; + const name = body.name; + + // Creates a new item, or replaces an old item with a new item + // https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#put-property + var params = { + TableName : tableName, + Item: { id : id, name: name } + }; + + try { + const data = await ddbDocClient.send(new PutCommand(params)); + console.log("Success - item added or updated", data); + } catch (err) { + console.log("Error", err.stack); + } + + const response = { + statusCode: 200, + body: JSON.stringify(body) + }; + + // All log statements are written to CloudWatch + console.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`); + return response; +}; diff --git a/nodejs22.x/web/{{cookiecutter.project_name}}/template.yaml b/nodejs22.x/web/{{cookiecutter.project_name}}/template.yaml new file mode 100644 index 000000000..08f3489e3 --- /dev/null +++ b/nodejs22.x/web/{{cookiecutter.project_name}}/template.yaml @@ -0,0 +1,131 @@ +# This is the SAM template that represents the architecture of your serverless application +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html + +# The AWSTemplateFormatVersion identifies the capabilities of the template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html +AWSTemplateFormatVersion: 2010-09-09 +Description: >- + {{cookiecutter.project_name}} + +# Transform section specifies one or more macros that AWS CloudFormation uses to process your template +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html +Transform: +- AWS::Serverless-2016-10-31 + +# Resources declares the AWS resources that you want to include in the stack +# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html +Resources: + # Each Lambda function is defined by properties: + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + + # This is a Lambda function config associated with the source code: get-all-items.js + getAllItemsFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/get-all-items.getAllItemsHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + MemorySize: 128 + Timeout: 100 + Description: A simple example includes a HTTP get method to get all items from a DynamoDB table. + Policies: + # Give Create/Read/Update/Delete Permissions to the SampleTable + - DynamoDBCrudPolicy: + TableName: !Ref SampleTable + Environment: + Variables: + # Make table name accessible as environment variable from function code during execution + SAMPLE_TABLE: !Ref SampleTable + Events: + Api: + Type: Api + Properties: + Path: / + Method: GET + # Each Lambda function is defined by properties: + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + + # This is a Lambda function config associated with the source code: get-by-id.js + getByIdFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/get-by-id.getByIdHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + MemorySize: 128 + Timeout: 100 + Description: A simple example includes a HTTP get method to get one item by id from a DynamoDB table. + Policies: + # Give Create/Read/Update/Delete Permissions to the SampleTable + - DynamoDBCrudPolicy: + TableName: !Ref SampleTable + Environment: + Variables: + # Make table name accessible as environment variable from function code during execution + SAMPLE_TABLE: !Ref SampleTable + Events: + Api: + Type: Api + Properties: + Path: /{id} + Method: GET + # Each Lambda function is defined by properties: + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + + # This is a Lambda function config associated with the source code: put-item.js + putItemFunction: + Type: AWS::Serverless::Function + Properties: + Handler: src/handlers/put-item.putItemHandler + Runtime: nodejs22.x + {%- if cookiecutter.architectures.value != []%} + Architectures: + {%- for arch in cookiecutter.architectures.value %} + - {{arch}} + {%- endfor %} + {%- endif %} + MemorySize: 128 + Timeout: 100 + Description: A simple example includes a HTTP post method to add one item to a DynamoDB table. + Policies: + # Give Create/Read/Update/Delete Permissions to the SampleTable + - DynamoDBCrudPolicy: + TableName: !Ref SampleTable + Environment: + Variables: + # Make table name accessible as environment variable from function code during execution + SAMPLE_TABLE: !Ref SampleTable + Events: + Api: + Type: Api + Properties: + Path: / + Method: POST + # Simple syntax to create a DynamoDB table with a single attribute primary key, more in + # https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlesssimpletable + + # DynamoDB table to store item: {id: <ID>, name: <NAME>} + SampleTable: + Type: AWS::Serverless::SimpleTable + Properties: + PrimaryKey: + Name: id + Type: String + ProvisionedThroughput: + ReadCapacityUnits: 2 + WriteCapacityUnits: 2 + +Outputs: + WebEndpoint: + Description: "API Gateway endpoint URL for Prod stage" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" diff --git a/tests/integration/build_invoke/node/test_node_22.py b/tests/integration/build_invoke/node/test_node_22.py new file mode 100644 index 000000000..62583366f --- /dev/null +++ b/tests/integration/build_invoke/node/test_node_22.py @@ -0,0 +1,80 @@ +from tests.integration.build_invoke.build_invoke_base import BuildInvokeBase + +""" +For each template, it will test the following sam commands: +1. sam init +2. sam build --use-container (if self.use_container is False, --use-container will be omitted) +3. (if there are event jsons), for each event json, check `sam local invoke` response is a valid json +""" + +class BuildInvoke_nodejs22_x_cookiecutter_aws_sam_hello_nodejs(BuildInvokeBase.SimpleHelloWorldBuildInvokeBase): + directory = "nodejs22.x/hello" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + +class BuildInvoke_nodejs22_x_cookiecutter_aws_sam_step_functions_sample_app(BuildInvokeBase.BuildInvokeBase): + directory = "nodejs22.x/step-func" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_quick_start_from_scratch(BuildInvokeBase.BuildInvokeBase): + directory = "nodejs22.x/scratch" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_quick_start_cloudwatch_events(BuildInvokeBase.BuildInvokeBase): + directory = "nodejs22.x/cw-event" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_quick_start_s3(BuildInvokeBase.BuildInvokeBase): + directory = "nodejs22.x/s3" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_quick_start_sns(BuildInvokeBase.BuildInvokeBase): + directory = "nodejs22.x/sns" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_quick_start_sqs(BuildInvokeBase.BuildInvokeBase): + directory = "nodejs22.x/sqs" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_quick_start_response_streaming(BuildInvokeBase.BuildInvokeBase): + directory = "nodejs22.x/response-streaming" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_quick_start_web(BuildInvokeBase.QuickStartWebBuildInvokeBase): + directory = "nodejs22.x/web" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_quick_start_full_stack(BuildInvokeBase.QuickStartWebBuildInvokeBase): + directory = "nodejs22.x/full-stack" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_image_nodejs22_x_cookiecutter_aws_sam_hello_nodejs_lambda_image( + BuildInvokeBase.SimpleHelloWorldBuildInvokeBase +): + directory = "nodejs22.x/hello-img" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class BuildInvoke_nodejs22_x_cookiecutter_aws_sam_gql_quick_start(BuildInvokeBase.BuildInvokeBase): + directory = "nodejs22.x/hello-gql" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False diff --git a/tests/integration/unit_test/test_unit_test_nodejs22_x.py b/tests/integration/unit_test/test_unit_test_nodejs22_x.py new file mode 100644 index 000000000..6245cecb2 --- /dev/null +++ b/tests/integration/unit_test/test_unit_test_nodejs22_x.py @@ -0,0 +1,76 @@ +from tests.integration.unit_test.unit_test_base import UnitTestBase + + +class UnitTest_nodejs22_x_cookiecutter_aws_sam_hello_nodejs(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/hello" + code_directories = ["hello-world"] + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_aws_sam_step_functions_sample_app(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/step-func" + code_directories = [ + "functions/stock-buyer", + "functions/stock-checker", + "functions/stock-seller", + ] + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_quick_start_from_scratch(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/scratch" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_quick_start_cloudwatch_events(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/cw-event" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_quick_start_response_streaming(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/response-streaming" + code_directories = ["src"] + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_quick_start_s3(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/s3" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_quick_start_sns(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/sns" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_quick_start_sqs(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/sqs" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_quick_start_web(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/web" + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_quick_start_full_stack(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/full-stack" + code_directories = ["backend", "frontend"] + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False + + +class UnitTest_nodejs22_x_cookiecutter_aws_sam_gql_quick_start(UnitTestBase.NodejsUnitTestBase): + directory = "nodejs22.x/hello-gql" + code_directories = ["greeter"] + # TODO: remove the line remove once nodejs22.x is GA + should_test_lint = False