diff --git a/examples/app/README.md b/examples/app/README.md index 29861533b4..dec7e9b55b 100644 --- a/examples/app/README.md +++ b/examples/app/README.md @@ -2,46 +2,52 @@ This is a deployable CDK app that deploys AWS Lambda functions as part of a CloudFormation stack. These Lambda functions use the utilities made available as part of Powertools for AWS Lambda (TypeScript) to demonstrate their usage. -> **Note** -> You will need to have a valid AWS Account in order to deploy these resources. These resources may incur costs to your AWS Account. The cost from **some services** are covered by the [AWS Free Tier](https://aws.amazon.com/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=*all&awsf.Free%20Tier%20Categories=*all) but not all of them. If you don't have an AWS Account follow [these instructions to create one](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/). +> [!Warning] +> You will need to have a valid AWS Account in order to deploy these resources. Many of the services in the example are covered [AWS Free Tier](https://aws.amazon.com/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=*all&awsf.Free%20Tier%20Categories=*all) but you may incur charges if you exceed the free tier limits. +If you don't have an AWS Account follow [these instructions to create one](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/). The example functions, located in the `functions` folder, are frontend by a REST API that is deployed using AWS API Gateway. The API has three endpoints: - * `POST /` - Adds an item to the DynamoDB table - * `GET /` - Retrieves all items from the DynamoDB table - * `GET /{id}` - Retrieves a specific item from the DynamoDB table +* `POST /` - Adds an item to the DynamoDB table +* `GET /` - Retrieves all items from the DynamoDB table +* `GET /{id}` - Retrieves a specific item from the DynamoDB table ## Deploying the stack - * Navigate to this location of the repo in your terminal (`examples/cdk`) - * `npm ci` - * `npm run cdk deploy --all --profile ` +> [!Note] +> The `examples/app` directory where this example is located is part of a monorepo. If you are interested in deploying the example only, follow the instructions below. +> If instead you are working on the monorepo and want to deploy the example, follow the instructions in the [CONTRIBUTING](../../CONTRIBUTING.md#dev-setup) doc, then run `npm run cdk deploy -w examples/app` from the project's root. -Note: Prior to deploying you may need to run `cdk bootstrap aws:/// --profile ` if you have not already bootstrapped your account for CDK. +If this is the first time you're using CDK in your AWS Account & AWS Region, you may need to run `npm run cdk bootstrap aws:/// --profile ` to bootstrap your account for CDK. -> **Note** -> You can find your API Gateway Endpoint URL in the output values displayed after deployment. +Then, still from within the `examples/app` directory, run the following commands: + +* `npm i --prefix ./` to install the dependencies +* `npm run cdk deploy` and follow the prompts to deploy the stack + +When the deployment is complete, you will see the output values that include the API Gateway Endpoint URL. ## Execute the functions via API Gateway Use the API Gateway Endpoint URL from the output values to execute the functions. First, let's add two items to the DynamoDB Table by running: ```bash -curl -XPOST --header 'Content-Type: application/json' --data '{"id":"myfirstitem","name":"Some Name for the first item"}' https://randomid12345.execute-api.eu-central-1.amazonaws.com/prod/ -curl -XPOST --header 'Content-Type: application/json' --data '{"id":"myseconditem","name":"Some Name for the second item"}' https://randomid1245.execute-api.eu-central-1.amazonaws.com/prod/ +curl -XPOST --header 'Content-Type: application/json' --data '{"id":"myfirstitem","name":"Some Name for the first item"}' https://.execute-api.eu-central-1.amazonaws.com/prod/ +curl -XPOST --header 'Content-Type: application/json' --data '{"id":"myseconditem","name":"Some Name for the second item"}' https://.execute-api.eu-central-1.amazonaws.com/prod/ ```` Now, let's retrieve all items by running: ```sh -curl -XGET https://randomid12345.execute-api.eu-central-1.amazonaws.com/prod/ +curl -XGET https://.execute-api.eu-central-1.amazonaws.com/prod/ ``` And finally, let's retrieve a specific item by running: + ```bash -curl -XGET https://randomid12345.execute-api.eu-central-1.amazonaws.com/prod/myseconditem/ +curl -XGET https://.execute-api.eu-central-1.amazonaws.com/prod/myseconditem/ ``` ## Observe the outputs in AWS CloudWatch & X-Ray @@ -49,7 +55,8 @@ curl -XGET https://randomid12345.execute-api.eu-central-1.amazonaws.com/prod/mys ### CloudWatch If we check the logs in CloudWatch, we can see that the logs are structured like this -``` + +```text 2022-04-26T17:00:23.808Z e8a51294-6c6a-414c-9777-6b0f24d8739b DEBUG { "level": "DEBUG", @@ -62,10 +69,11 @@ If we check the logs in CloudWatch, we can see that the logs are structured like By having structured logs like this, we can easily search and analyse them in [CloudWatch Logs Insight](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AnalyzingLogData.html). Run the following query to get all messages for a specific `awsRequestId`: -```` +````text filter awsRequestId="bcd50969-3a55-49b6-a997-91798b3f133a" | fields timestamp, message ```` + ### AWS X-Ray As we have enabled tracing for our Lambda-Funtions, you can visit [AWS CloudWatch Console](https://console.aws.amazon.com/cloudwatch/) and see [Traces](https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-traces) and a [Service Map](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-using-xray-maps.html) for our application. @@ -76,4 +84,4 @@ To delete the sample application that you created, run the command below while i ```bash cdk destroy -``` \ No newline at end of file +``` diff --git a/examples/app/cdk/example-stack.ts b/examples/app/cdk/example-stack.ts index 067ef6752f..fe6b164cb5 100644 --- a/examples/app/cdk/example-stack.ts +++ b/examples/app/cdk/example-stack.ts @@ -1,3 +1,5 @@ +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { RemovalPolicy, Stack, type StackProps } from 'aws-cdk-lib'; import { LambdaIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway'; import { @@ -21,6 +23,12 @@ import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import type { Construct } from 'constructs'; import { FunctionWithLogGroup } from './function-with-logstream-construct.js'; +// Get the current file URL +const __filename = fileURLToPath(import.meta.url); + +// Get the current directory +const __dirname = dirname(__filename); + export class PowertoolsExampleStack extends Stack { public constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); @@ -80,7 +88,7 @@ export class PowertoolsExampleStack extends Stack { * Because we are using ESM and tree shake, we create an optimized bundle. */ const putItemFn = new FunctionWithLogGroup(this, 'put-item-fn', { - entry: './functions/put-item.ts', + entry: join(__dirname, '..', './functions/put-item.ts'), functionName: 'powertools-example-put-item', bundling: { minify: true, @@ -124,7 +132,7 @@ export class PowertoolsExampleStack extends Stack { * in a centralized way across all your functions. */ const getAllItemsFn = new FunctionWithLogGroup(this, 'get-all-items-fn', { - entry: './functions/get-all-items.ts', + entry: join(__dirname, '..', './functions/get-all-items.ts'), functionName: 'powertools-example-get-all-items', layers: [powertoolsLayer], // we use the powertools layer bundling: { @@ -150,7 +158,7 @@ export class PowertoolsExampleStack extends Stack { * dependencies in it. */ const getByIdFn = new FunctionWithLogGroup(this, 'get-by-id-fn', { - entry: './functions/get-by-id.ts', + entry: join(__dirname, '..', './functions/get-by-id.ts'), functionName: 'powertools-example-get-by-id', bundling: { minify: true, @@ -172,7 +180,7 @@ export class PowertoolsExampleStack extends Stack { this, 'process-items-stream-fn', { - entry: './functions/process-items-stream.ts', + entry: join(__dirname, '..', './functions/process-items-stream.ts'), functionName: 'powertools-example-process-items-stream', layers: [powertoolsLayer], bundling: { diff --git a/examples/app/jest.config.cjs b/examples/app/jest.config.cjs deleted file mode 100644 index 4bf6bb1d25..0000000000 --- a/examples/app/jest.config.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - preset: 'ts-jest', - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - }, - transform: { - '^.+\\.ts?$': 'ts-jest', - }, - moduleFileExtensions: ['js', 'ts'], - testMatch: ['**/?(*.)+(spec|test).ts'], - testPathIgnorePatterns: ['/node_modules/'], - testEnvironment: 'node', - roots: ['/tests'], -}; diff --git a/examples/app/package.json b/examples/app/package.json index fd7ead29d6..a382e3fbff 100644 --- a/examples/app/package.json +++ b/examples/app/package.json @@ -13,7 +13,7 @@ "test": "npm run test:unit", "lint": "biome lint .", "lint:fix": "biome check --write .", - "test:unit": "export POWERTOOLS_DEV=true && jest --silent", + "test:unit": "export POWERTOOLS_DEV=true && vitest --run --silent", "test:e2e": "echo 'To be implemented ...'", "cdk": "cdk" }, @@ -29,16 +29,14 @@ }, "devDependencies": { "@types/aws-lambda": "^8.10.145", - "@types/jest": "^29.5.12", "@types/node": "22.5.2", "aws-cdk": "^2.155.0", "aws-cdk-lib": "^2.155.0", "constructs": "^10.3.0", - "jest": "^29.7.0", "source-map-support": "^0.5.21", - "ts-jest": "^29.2.5", "tsx": "^4.19.0", - "typescript": "^5.5.4" + "typescript": "^5.5.4", + "vitest": "^2.0.5" }, "dependencies": { "@aws-lambda-powertools/batch": "^2.7.0", @@ -51,13 +49,9 @@ "@aws-sdk/lib-dynamodb": "^3.637.0", "@middy/core": "^4.7.0", "@types/aws-lambda": "^8.10.145", - "@types/jest": "^29.5.12", "@types/node": "22.5.2", "constructs": "^10.3.0", "esbuild": "^0.23.1", - "jest": "^29.7.0", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", "typescript": "^5.5.4" } } diff --git a/examples/app/tests/cdk-app.test.ts b/examples/app/tests/cdk-app.test.ts index f7ebfd9404..f31b35f07c 100644 --- a/examples/app/tests/cdk-app.test.ts +++ b/examples/app/tests/cdk-app.test.ts @@ -1,9 +1,13 @@ import { App } from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; +import { describe, it } from 'vitest'; import { PowertoolsExampleStack } from '../cdk/example-stack.js'; -test('CDK code synthesize', () => { +describe('CDK example stack', () => { const app = new App(); const stack = new PowertoolsExampleStack(app, 'MyTestStack'); - Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 4); + + it('has 4 Lambda functions', () => { + Template.fromStack(stack).resourceCountIs('AWS::Lambda::Function', 4); + }); }); diff --git a/examples/app/vitest.config.ts b/examples/app/vitest.config.ts new file mode 100644 index 0000000000..d5aa737c68 --- /dev/null +++ b/examples/app/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineProject } from 'vitest/config'; + +export default defineProject({ + test: { + environment: 'node', + }, +}); diff --git a/package-lock.json b/package-lock.json index ef3242e32e..38ed65eece 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,27 +65,21 @@ "@aws-sdk/lib-dynamodb": "^3.637.0", "@middy/core": "^4.7.0", "@types/aws-lambda": "^8.10.145", - "@types/jest": "^29.5.12", "@types/node": "22.5.2", "constructs": "^10.3.0", "esbuild": "^0.23.1", - "jest": "^29.7.0", - "ts-jest": "^29.2.5", - "ts-node": "^10.9.2", "typescript": "^5.5.4" }, "devDependencies": { "@types/aws-lambda": "^8.10.145", - "@types/jest": "^29.5.12", "@types/node": "22.5.2", "aws-cdk": "^2.155.0", "aws-cdk-lib": "^2.155.0", "constructs": "^10.3.0", - "jest": "^29.7.0", "source-map-support": "^0.5.21", - "ts-jest": "^29.2.5", "tsx": "^4.19.0", - "typescript": "^5.5.4" + "typescript": "^5.5.4", + "vitest": "^2.0.5" } }, "examples/app/node_modules/aws-cdk": { @@ -2045,6 +2039,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2056,6 +2051,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -2964,6 +2960,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -2980,7 +2977,8 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -5122,22 +5120,26 @@ "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==" + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true }, "node_modules/@tufjs/canonical-json": { "version": "1.0.0", @@ -5611,6 +5613,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -5622,6 +5625,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -5765,7 +5769,8 @@ "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, "node_modules/argparse": { "version": "2.0.1", @@ -7430,7 +7435,8 @@ "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -7628,6 +7634,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, "engines": { "node": ">=0.3.1" } @@ -11327,7 +11334,8 @@ "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true }, "node_modules/make-fetch-happen": { "version": "13.0.1", @@ -15711,6 +15719,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -16074,6 +16083,7 @@ "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16247,7 +16257,8 @@ "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true }, "node_modules/v8-to-istanbul": { "version": "9.2.0", @@ -17305,6 +17316,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, "engines": { "node": ">=6" } diff --git a/vitest.workspace.ts b/vitest.workspace.ts index 32fc3c80be..3b8321503d 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -1 +1 @@ -export default ['packages/*/vitest.config.ts']; +export default ['packages/*/vitest.config.ts', 'examples/app/vitest.config.ts'];