diff --git a/apps/sandbox/lambda-nodejs/.gitignore b/apps/sandbox/lambda-nodejs/.gitignore new file mode 100644 index 0000000000..5854f05ece --- /dev/null +++ b/apps/sandbox/lambda-nodejs/.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/apps/sandbox/lambda-nodejs/.npmignore b/apps/sandbox/lambda-nodejs/.npmignore new file mode 100644 index 0000000000..e7e1fb04f4 --- /dev/null +++ b/apps/sandbox/lambda-nodejs/.npmignore @@ -0,0 +1 @@ +tests/* diff --git a/apps/sandbox/lambda-nodejs/Dockerfile b/apps/sandbox/lambda-nodejs/Dockerfile new file mode 100644 index 0000000000..21767f1d57 --- /dev/null +++ b/apps/sandbox/lambda-nodejs/Dockerfile @@ -0,0 +1,10 @@ +FROM public.ecr.aws/lambda/nodejs:20 + +COPY app.mjs package*.json ${LAMBDA_TASK_ROOT}/ + +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/apps/sandbox/lambda-nodejs/README.md b/apps/sandbox/lambda-nodejs/README.md new file mode 100644 index 0000000000..232de6fd09 --- /dev/null +++ b/apps/sandbox/lambda-nodejs/README.md @@ -0,0 +1,45 @@ +# Sandbox Lambda Node.js + +## Build the project + +```console +nx build sandbox-lambda-nodejs +``` + +## Build the Docker image of the Lambda function + +```console +nx build-image sandbox-lambda-nodejs +``` + +## Start the Lambda function locally with Docker Compose + +Starts the Lambda function in the foreground, allowing you to view logs and interact with it +directly. + +```console +nx serve sandbox-lambda-nodejs +``` + +Starts the Lambda function in detached mode, running it in the background. This is useful if you +want to continue using the terminal for other tasks while the function runs. + +```console +nx serve-detach sandbox-lambda-nodejs +``` + +## Invoke the Lambda function locally + +To invoke the Lambda function after starting it locally, use the following command: + +```console +nx run sandbox-lambda-nodejs:invoke --event +``` + +Replace `` with the path to your JSON file containing the event payload relative +to the location of the project folder. For example, if your event payload is stored in a file +located at `events/event.json` relative to the project folder: + +```console +nx run sandbox-lambda-nodejs:invoke --event events/event.json +``` diff --git a/apps/sandbox/lambda-nodejs/app.mjs b/apps/sandbox/lambda-nodejs/app.mjs new file mode 100644 index 0000000000..7bfac1ae62 --- /dev/null +++ b/apps/sandbox/lambda-nodejs/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/apps/sandbox/lambda-nodejs/events/event.json b/apps/sandbox/lambda-nodejs/events/event.json new file mode 100644 index 0000000000..95791046f1 --- /dev/null +++ b/apps/sandbox/lambda-nodejs/events/event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"hello world 2\"}", + "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/apps/sandbox/lambda-nodejs/package.json b/apps/sandbox/lambda-nodejs/package.json new file mode 100644 index 0000000000..109ed147ff --- /dev/null +++ b/apps/sandbox/lambda-nodejs/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", + "dependencies": { + "axios": ">=1.6.0" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.2.0" + } +} diff --git a/apps/sandbox/lambda-nodejs/project.json b/apps/sandbox/lambda-nodejs/project.json new file mode 100644 index 0000000000..deb76abd7e --- /dev/null +++ b/apps/sandbox/lambda-nodejs/project.json @@ -0,0 +1,34 @@ +{ + "name": "sandbox-lambda-nodejs", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "targets": { + "build": { + "executor": "nx:run-commands", + "options": { + "command": "sam build", + "cwd": "{projectRoot}" + } + }, + "serve": { + "executor": "nx:run-commands", + "options": { + "command": "docker/sandbox/serve.sh {projectName}" + } + }, + "serve-detach": { + "executor": "nx:run-commands", + "options": { + "command": "docker/sandbox/serve-detach.sh {projectName}" + } + }, + "invoke": { + "executor": "nx:run-commands", + "options": { + "command": "curl -X POST 'http://localhost:9000/2015-03-31/functions/function/invocations' --data @{args.event}", + "cwd": "{projectRoot}" + } + } + }, + "tags": ["language:javascript"] +} diff --git a/apps/sandbox/lambda-nodejs/samconfig.toml b/apps/sandbox/lambda-nodejs/samconfig.toml new file mode 100644 index 0000000000..00c39a9c4b --- /dev/null +++ b/apps/sandbox/lambda-nodejs/samconfig.toml @@ -0,0 +1,30 @@ +# More information about the configuration file can be found here: +# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html +version = 0.1 + +[default.global.parameters] +stack_name = "sandbox-lambda-nodejs" + +[default.build.parameters] +parallel = true + +[default.validate.parameters] +lint = true + +[default.deploy.parameters] +capabilities = "CAPABILITY_IAM" +confirm_changeset = true +resolve_s3 = true +resolve_image_repos = true + +[default.package.parameters] +resolve_s3 = true + +[default.sync.parameters] +watch = true + +[default.local_start_api.parameters] +warm_containers = "EAGER" + +[default.local_start_lambda.parameters] +warm_containers = "EAGER" diff --git a/apps/sandbox/lambda-nodejs/template.yaml b/apps/sandbox/lambda-nodejs/template.yaml new file mode 100644 index 0000000000..eb14a62367 --- /dev/null +++ b/apps/sandbox/lambda-nodejs/template.yaml @@ -0,0 +1,43 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + lambda-nodejs + + Sample SAM Template for lambda-nodejs + +# 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 + Architectures: + - x86_64 + Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: get + Metadata: + DockerTag: nodejs20.x-v1 + DockerContext: . + 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/apps/sandbox/lambda-nodejs/tests/unit/test-handler.mjs b/apps/sandbox/lambda-nodejs/tests/unit/test-handler.mjs new file mode 100644 index 0000000000..41b7906390 --- /dev/null +++ b/apps/sandbox/lambda-nodejs/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/docker/sandbox/networks.yml b/docker/sandbox/networks.yml new file mode 100644 index 0000000000..af674ad9c1 --- /dev/null +++ b/docker/sandbox/networks.yml @@ -0,0 +1,4 @@ +networks: + sandbox: + name: sandbox + driver: bridge diff --git a/docker/sandbox/serve-detach.sh b/docker/sandbox/serve-detach.sh new file mode 100755 index 0000000000..a6bfcfd5ff --- /dev/null +++ b/docker/sandbox/serve-detach.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +args=( + # List of services in alphanumeric order + --file docker/sandbox/services/lambda-nodejs.yml + + --file docker/sandbox/networks.yml + --file docker/sandbox/volumes.yml + + up $1 --detach --remove-orphans +) + +docker compose "${args[@]}" diff --git a/docker/sandbox/serve.sh b/docker/sandbox/serve.sh new file mode 100755 index 0000000000..d74c8d4460 --- /dev/null +++ b/docker/sandbox/serve.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +args=( + # List of services in alphanumeric order + --file docker/sandbox/services/lambda-nodejs.yml + + --file docker/sandbox/networks.yml + --file docker/sandbox/volumes.yml + + up $1 --remove-orphans +) + +docker compose "${args[@]}" diff --git a/docker/sandbox/services/lambda-nodejs.yml b/docker/sandbox/services/lambda-nodejs.yml new file mode 100644 index 0000000000..b3ae566dd5 --- /dev/null +++ b/docker/sandbox/services/lambda-nodejs.yml @@ -0,0 +1,9 @@ +services: + sandbox-lambda-nodejs: + image: ghcr.io/sage-bionetworks/sandbox-lambda-nodejs:${SANDBOX_VERSION:-local} + container_name: sandbox-lambda-nodejs + restart: always + networks: + - sandbox + ports: + - '9000:8080' diff --git a/docker/sandbox/volumes.yml b/docker/sandbox/volumes.yml new file mode 100644 index 0000000000..69f2f52d59 --- /dev/null +++ b/docker/sandbox/volumes.yml @@ -0,0 +1,3 @@ +# volumes: +# iatlas-postgres-data: +# name: iatlas-postgres-data diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index abbc150e06..3c04f385bd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,6 +3,7 @@ packages: - 'libs/**' - '!apps/openchallenges/infra' - '!apps/openchallenges/infra/old' + - '!apps/sandbox/lambda-nodejs' # prevent the creation of libs/**/api-client-angular/node_modules when running `workspace-install` # that break the apps and tests - '!libs/**/api-client-angular'