diff --git a/.github/actions/cached-node-modules/action.yml b/.github/actions/cached-node-modules/action.yml index 42d5db19e7..38d6e1e35c 100644 --- a/.github/actions/cached-node-modules/action.yml +++ b/.github/actions/cached-node-modules/action.yml @@ -4,6 +4,9 @@ inputs: nodeVersion: # id of input description: 'Node.js version to use in the cache key' default: '20' + build: + description: 'Whether to build the packages or not' + default: 'true' outputs: cache-hit: description: "Whether the cache was hit or not" @@ -17,7 +20,7 @@ runs: shell: bash - name: Cache node modules id: cache-node-modules - uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: '**/node_modules' # Use the combo between node version, name, and SHA-256 hash of the lock file as cache key so that @@ -29,8 +32,8 @@ runs: run: npm ci shell: bash - name: Build packages - # Regardless of whether the cache was hit or not, we need to build the packages. - # + # Regardless of whether the cache was hit or not, we need to build the packages, unless the caller says otherwise + if: inputs.build == 'true' # We build the shared package first, then the others in parallel to speed up the process # even though we could just run `npm run build` in the root folder and build them in # sequence, but still in the correct order. diff --git a/.github/scripts/release_patch_package_json.js b/.github/scripts/release_patch_package_json.js index 626d4cc7d1..761c0680a1 100644 --- a/.github/scripts/release_patch_package_json.js +++ b/.github/scripts/release_patch_package_json.js @@ -44,6 +44,7 @@ const betaPackages = []; types, files, private, + type, } = pkgJson; let version = originalVersion; @@ -71,6 +72,7 @@ const betaPackages = []; main, types, files, + type, }; // Not all utilities have these fields, so only add them if they exist to avoid diff --git a/.github/workflows/make-v2-release.yml b/.github/workflows/make-v2-release.yml deleted file mode 100644 index 5aa6efc15c..0000000000 --- a/.github/workflows/make-v2-release.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Make Release v2 (pre-release) -on: - workflow_dispatch: {} - -permissions: - contents: read - -concurrency: - group: on-release-publish -jobs: - run-unit-tests: - uses: ./.github/workflows/reusable-run-linting-check-and-unit-tests.yml - publish-npm: - needs: run-unit-tests - # Needed as recommended by npm docs on publishing with provenance https://docs.npmjs.com/generating-provenance-statements - permissions: - id-token: write - environment: Release - runs-on: ubuntu-latest - outputs: - RELEASE_VERSION: ${{ steps.set-release-version.outputs.RELEASE_VERSION }} - steps: - - name: Checkout code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Setup NodeJS - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 - with: - node-version: '20' - cache: 'npm' - - name: Setup auth tokens - run: | - npm set "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" - - name: Setup dependencies - uses: ./.github/actions/cached-node-modules - - name: Version - run: | - # Version all packages to next major version (2.0.0) without pushing to git, generating changelog or running commit hooks - # Since the version stored in the lerna.json will always be a 1.x.x version, we manually set the version to 2.0.0 - npx lerna version major --force-publish --no-push --no-git-tag-version --no-commit-hooks --no-changelog --yes - - name: Set alpha iteration - run: | - # Get the current alpha version from npm i.e 2.0.0-alpha.0 -> 0, 2.0.0-alpha.1 -> 1 (default to -1 if no alpha versions exist = first pre-release) - ITERATION=$(npm show @aws-lambda-powertools/commons time --json | jq -r 'to_entries | map(select(.key | startswith("2.0.0-alpha"))) | sort_by(.key) | last | .key // "-1"') - # Write the new version to the file - echo "{ \"iteration\": $((ITERATION + 1)) }" > v2.json - - name: Increment version in UA - run: | - # Increment the version in the UA - echo "// this file is auto generated, do not modify\nexport const PT_VERSION = '2.0.0-alpha.$(jq -r '.iteration' v2.json)';" > packages/commons/src/version.ts - - name: Build - run: | - npm run build -w packages/batch \ - -w packages/commons \ - -w packages/idempotency \ - -w packages/logger \ - -w packages/metrics \ - -w packages/parameters \ - -w packages/tracer - - name: Pack packages - run: | - npm pack -w packages/batch \ - -w packages/commons \ - -w packages/idempotency \ - -w packages/logger \ - -w packages/metrics \ - -w packages/parameters \ - -w packages/tracer - - name: Publish to npm - run: | - npm publish aws-lambda-powertools-batch-*.tgz --tag next --provenance - npm publish aws-lambda-powertools-commons-*.tgz --tag next --provenance - npm publish aws-lambda-powertools-idempotency-*.tgz --tag next --provenance - npm publish aws-lambda-powertools-logger-*.tgz --tag next --provenance - npm publish aws-lambda-powertools-metrics-*.tgz --tag next --provenance - npm publish aws-lambda-powertools-parameters-*.tgz --tag next --provenance - npm publish aws-lambda-powertools-tracer-*.tgz --tag next --provenance - - name: Set release version - id: set-release-version - run: | - VERSION="2.0.0-alpha.$(cat v2.json | jq .iteration -r)" - echo RELEASE_VERSION="$VERSION" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/make-version.yml b/.github/workflows/make-version.yml index 357908381b..52d2570d81 100644 --- a/.github/workflows/make-version.yml +++ b/.github/workflows/make-version.yml @@ -30,9 +30,13 @@ jobs: uses: ./.github/actions/cached-node-modules - name: Version id: bump-version + run: npx lerna version --conventional-commits --no-git-tag-version --no-push --no-commit-hooks --yes + - name: Update user agent version run: | - npx lerna version --conventional-commits --no-git-tag-version --no-push --no-commit-hooks --yes - git add . + VERSION=$(cat lerna.json | jq .version -r) + echo -e "// this file is auto generated, do not modify\nexport const VERSION = '$VERSION';" > packages/commons/src/version.ts + - name: Stage changes + run: git add . - name: Set release version id: set-release-version run: | diff --git a/.gitignore b/.gitignore index cb2d901eae..873ab3e50d 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,4 @@ tmp # TS build files tsconfig.tsbuildinfo -.tsbuildinfo \ No newline at end of file +.tsbuildinfo diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 97a7056072..e9e6d1e941 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,2 +1,2 @@ > [!IMPORTANT] -> Maintainers' playbook moved: https://docs.powertools.aws.dev/lambda/typescript/latest/maintainers/ \ No newline at end of file +> Maintainers' playbook moved: https://docs.powertools.aws.dev/lambda/typescript/latest/maintainers/ diff --git a/docs/core/logger.md b/docs/core/logger.md index 8b182edc18..56aff581fc 100644 --- a/docs/core/logger.md +++ b/docs/core/logger.md @@ -45,11 +45,11 @@ The library has three optional settings, which can be set via environment variab These settings will be used across all logs emitted: -| Setting | Description | Environment variable | Default Value | Allowed Values | Example Value | Constructor parameter | -| ---------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------------------------------------------------------ | ------------------- | --------------------- | -| **Service name** | Sets the name of service of which the Lambda function is part of, that will be present across all log statements | `POWERTOOLS_SERVICE_NAME` | `service_undefined` | Any string | `serverlessAirline` | `serviceName` | -| **Logging level** | Sets how verbose Logger should be, from the most verbose to the least verbose (no logs) | `POWERTOOLS_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARN`, `ERROR`, `CRITICAL`, `SILENT` | `ERROR` | `logLevel` | -| **Sample rate** | Probability that a Lambda invocation will print all the log items regardless of the log level setting | `POWERTOOLS_LOGGER_SAMPLE_RATE` | `0` | `0.0` to `1.0` | `0.1` | `sampleRateValue` | +| Setting | Description | Environment variable | Default Value | Allowed Values | Example Value | Constructor parameter | +| ----------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------------------------------------------------------ | ------------------- | --------------------- | +| **Service name** | Sets the name of service of which the Lambda function is part of, that will be present across all log statements | `POWERTOOLS_SERVICE_NAME` | `service_undefined` | Any string | `serverlessAirline` | `serviceName` | +| **Logging level** | Sets how verbose Logger should be, from the most verbose to the least verbose (no logs) | `POWERTOOLS_LOG_LEVEL` | `INFO` | `DEBUG`, `INFO`, `WARN`, `ERROR`, `CRITICAL`, `SILENT` | `ERROR` | `logLevel` | +| **Sample rate** | Probability that a Lambda invocation will print all the log items regardless of the log level setting | `POWERTOOLS_LOGGER_SAMPLE_RATE` | `0` | `0.0` to `1.0` | `0.1` | `sampleRateValue` | See all environment variables in the [Environment variables](../index.md/#environment-variables) section. Check API docs to learn more about [Logger constructor options](https://docs.powertools.aws.dev/lambda/typescript/latest/api/types/_aws_lambda_powertools_logger.types.ConstructorOptions.html){target="_blank"}. @@ -110,10 +110,10 @@ This functionality will include the following keys in your structured logs: === "Middy Middleware" !!! tip "A note about Middy" - Currently we support only Middy `v3.x` that you can install it by running `npm i @middy/core@~3`. + We guarantee support only for Middy.js `v4.x`, that you can install it by running `npm i @middy/core@~4`. Check their docs to learn more about [Middy and its middleware stack](https://middy.js.org/docs/intro/getting-started){target="_blank"} as well as [best practices when working with Powertools](https://middy.js.org/docs/integrations/lambda-powertools#best-practices){target="_blank"}. - ```typescript hl_lines="1 13" + ```typescript hl_lines="2 14" --8<-- "docs/snippets/logger/middy.ts" ``` @@ -131,7 +131,7 @@ This functionality will include the following keys in your structured logs: === "Manual" - ```typescript hl_lines="6" + ```typescript hl_lines="10" --8<-- "docs/snippets/logger/manual.ts" ``` @@ -163,7 +163,7 @@ When debugging in non-production environments, you can instruct Logger to log th === "Middy Middleware" - ```typescript hl_lines="10" + ```typescript hl_lines="15" --8<-- "docs/snippets/logger/eventMiddy.ts" ``` @@ -236,7 +236,7 @@ If you want to make sure that persistent attributes added **inside the handler f === "Middy Middleware" - ```typescript hl_lines="30" + ```typescript hl_lines="31" --8<-- "docs/snippets/logger/clearStateMiddy.ts" ``` @@ -509,23 +509,21 @@ The `createChild` method allows you to create a child instance of the Logger, wh } ``` -### Sampling logs +### Sampling debug logs -Use sampling when you want to print all the log items generated in your code, based on a **percentage of your concurrent/cold start invocations**. +Use sampling when you want to dynamically change your log level to **DEBUG** based on a **percentage of your concurrent/cold start invocations**. -You can do that by setting a "sample rate", a float value ranging from `0.0` (0%) to `1` (100%), by using a `POWERTOOLS_LOGGER_SAMPLE_RATE` env var or passing the `sampleRateValue` parameter in the Logger constructor. -This number represents the probability that a Lambda invocation will print all the log items regardless of the log level setting. - -For example, by setting the "sample rate" to `0.5`, roughly 50% of your lambda invocations will print all the log items, including the `debug` ones. +You can use values ranging from `0` to `1` (100%) when setting the `sampleRateValue` constructor option or `POWERTOOLS_LOGGER_SAMPLE_RATE` env var. !!! tip "When is this useful?" - In production, to avoid log data pollution and reduce CloudWatch costs, developers are encouraged to use the logger with `logLevel` equal to `ERROR` or `WARN`. - This means that only errors or warnings will be printed. + Let's imagine a sudden spike increase in concurrency triggered a transient issue downstream. When looking into the logs you might not have enough information, and while you can adjust log levels it might not happen again. - However, it might still be useful to print all the logs (including debug ones) of a very small percentage of invocations to have a better understanding of the behaviour of your code in production even when there are no errors. - - **Sampling decision happens at the Logger initialization**. This means sampling may happen significantly more or less than depending on your traffic patterns, for example a steady low number of invocations and thus few cold starts. - If you want to reset the sampling decision and refresh it for each invocation, you can call the `logger.refreshSampleRateCalculation()` method at the beginning or end of your handler. + This feature takes into account transient issues where additional debugging information can be useful. + +Sampling decision happens at the Logger initialization. This means sampling may happen significantly more or less than depending on your traffic patterns, for example a steady low number of invocations and thus few cold starts. + +!!! note + Open a [feature request](https://github.com/aws-powertools/powertools-lambda-typescript/issues/new?assignees=&labels=type%2Ffeature-request%2Ctriage&projects=aws-powertools%2F7&template=feature_request.yml&title=Feature+request%3A+TITLE) if you want Logger to calculate sampling for every invocation === "handler.ts" @@ -639,7 +637,7 @@ You can customize the structure (keys and values) of your log items by passing a === "handler.ts" - ```typescript hl_lines="2 5" + ```typescript hl_lines="2 6" --8<-- "docs/snippets/logger/bringYourOwnFormatterHandler.ts" ``` @@ -700,9 +698,6 @@ This is a Jest sample that provides the minimum information necessary for Logger --8<-- "docs/snippets/logger/unitTesting.ts" ``` -!!! tip - If you don't want to declare your own dummy Lambda Context, you can use [`ContextExamples.helloworldContext`](https://github.com/aws-powertools/powertools-lambda-typescript/blob/main/packages/commons/src/samples/resources/contexts/hello-world.ts#L3-L16) from [`@aws-lambda-powertools/commons`](https://www.npmjs.com/package/@aws-lambda-powertools/commons). - ### Suppress logs with Jest When unit testing your code with [Jest](https://jestjs.io) you can use the `POWERTOOLS_DEV` environment variable in conjunction with the Jest `--silent` CLI option to suppress logs from Logger. diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 78f550ad50..83b170f65b 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -49,7 +49,7 @@ npm install @aws-lambda-powertools/metrics !!! warning "Caution" - Using the Lambda [Advanced Logging Controls](...docs link) feature requires you to update your version of Powertools for AWS Lambda (TypeScript) to at least v1.15.0 to ensure metrics are reported correctly to Amazon CloudWatch Metrics. + Using the Lambda [Advanced Logging Controls](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced) feature requires you to update your version of Powertools for AWS Lambda (TypeScript) to at least v1.15.0 to ensure metrics are reported correctly to Amazon CloudWatch Metrics. ### Usage @@ -253,7 +253,7 @@ See below an example of how to automatically flush metrics with the Middy-compat === "handler.ts" - ```typescript hl_lines="20" + ```typescript hl_lines="2 17" --8<-- "docs/snippets/metrics/middy.ts" ``` @@ -368,7 +368,7 @@ You can optionally capture cold start metrics with the `logMetrics` middleware o === "Middy Middleware" - ```typescript hl_lines="21" + ```typescript hl_lines="18" --8<-- "docs/snippets/metrics/captureColdStartMetricMiddy.ts" ``` @@ -398,7 +398,7 @@ You can add high-cardinality data as part of your Metrics log with the `addMetad === "handler.ts" - ```typescript hl_lines="18" + ```typescript hl_lines="15" --8<-- "docs/snippets/metrics/addMetadata.ts" ``` diff --git a/docs/core/tracer.md b/docs/core/tracer.md index cf99e316c9..2c45e0faad 100644 --- a/docs/core/tracer.md +++ b/docs/core/tracer.md @@ -88,10 +88,10 @@ You can quickly start by importing the `Tracer` class, initialize it outside the === "Middy Middleware" !!! tip "A note about Middy" - Currently we support only Middy `v3.x` that you can install it by running `npm i @middy/core@~3`. + We guarantee support only for Middy.js `v4.x`, that you can install it by running `npm i @middy/core@~4`. Check their docs to learn more about [Middy and its middleware stack](https://middy.js.org/docs/intro/getting-started){target="_blank"} as well as [best practices when working with Powertools](https://middy.js.org/docs/integrations/lambda-powertools#best-practices){target="_blank"}. - ```typescript hl_lines="1 14 16" + ```typescript hl_lines="2 15 17" --8<-- "docs/snippets/tracer/middy.ts" ``` @@ -197,7 +197,7 @@ You can patch all AWS SDK v2 clients by calling the `captureAWS` method: === "index.ts" - ```typescript hl_lines="6" + ```typescript hl_lines="7" --8<-- "docs/snippets/tracer/captureAWSAll.ts" ``` @@ -284,7 +284,7 @@ Alternatively, use the `captureResponse: false` option in both `tracer.captureLa === "middy.ts" - ```typescript hl_lines="17" + ```typescript hl_lines="18" --8<-- "docs/snippets/tracer/disableCaptureResponseMiddy.ts" ``` diff --git a/docs/index.md b/docs/index.md index e4a18babed..8c649cbe58 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,7 +26,7 @@ You can use Powertools for AWS Lambda (TypeScript) in both TypeScript and JavaSc You can install Powertools for AWS Lambda (TypeScript) using one of the following options: -* **Lambda Layer**: [**arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29**](#){: .copyMe}:clipboard: +* **Lambda Layer**: [**arn:aws:lambda:{region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1**](#){: .copyMe}:clipboard: * **npm**: [`npm install @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics @aws-lambda-powertools/logger`](#){: .copyMe}:clipboard: ### Lambda Layer @@ -39,35 +39,35 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L ??? note "Click to expand and copy any regional Lambda Layer ARN" - | Region | Layer ARN | - | ---------------- | ------------------------------------------------------------------------------------------------------------ | - | `us-east-1` | [arn:aws:lambda:us-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `us-east-2` | [arn:aws:lambda:us-east-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `us-west-1` | [arn:aws:lambda:us-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `us-west-2` | [arn:aws:lambda:us-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-south-1` | [arn:aws:lambda:ap-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-east-1` | [arn:aws:lambda:ap-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ap-southeast-4` | [arn:aws:lambda:ap-southeast-4:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `eu-central-1` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `eu-central-2` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `eu-west-1` | [arn:aws:lambda:eu-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `eu-west-2` | [arn:aws:lambda:eu-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `eu-west-3` | [arn:aws:lambda:eu-west-3:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `eu-north-1` | [arn:aws:lambda:eu-north-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `eu-south-1` | [arn:aws:lambda:eu-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `eu-south-2` | [arn:aws:lambda:eu-south-2:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ca-central-1` | [arn:aws:lambda:ca-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `ca-west-1` | [arn:aws:lambda:ca-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `sa-east-1` | [arn:aws:lambda:sa-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `af-south-1` | [arn:aws:lambda:af-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `me-south-1` | [arn:aws:lambda:me-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | - | `il-central-1` | [arn:aws:lambda:il-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScript:29](#){: .copyMe}:clipboard: | + | Region | Layer ARN | + | ---------------- | ------------------------------------------------------------------------------------------------------------- | + | `us-east-1` | [arn:aws:lambda:us-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `us-east-2` | [arn:aws:lambda:us-east-2:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `us-west-1` | [arn:aws:lambda:us-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `us-west-2` | [arn:aws:lambda:us-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-south-1` | [arn:aws:lambda:ap-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-east-1` | [arn:aws:lambda:ap-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-northeast-1` | [arn:aws:lambda:ap-northeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-northeast-2` | [arn:aws:lambda:ap-northeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-northeast-3` | [arn:aws:lambda:ap-northeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-southeast-1` | [arn:aws:lambda:ap-southeast-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-southeast-2` | [arn:aws:lambda:ap-southeast-2:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-southeast-3` | [arn:aws:lambda:ap-southeast-3:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ap-southeast-4` | [arn:aws:lambda:ap-southeast-4:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `eu-central-1` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `eu-central-2` | [arn:aws:lambda:eu-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `eu-west-1` | [arn:aws:lambda:eu-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `eu-west-2` | [arn:aws:lambda:eu-west-2:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `eu-west-3` | [arn:aws:lambda:eu-west-3:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `eu-north-1` | [arn:aws:lambda:eu-north-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `eu-south-1` | [arn:aws:lambda:eu-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `eu-south-2` | [arn:aws:lambda:eu-south-2:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ca-central-1` | [arn:aws:lambda:ca-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `ca-west-1` | [arn:aws:lambda:ca-west-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `sa-east-1` | [arn:aws:lambda:sa-east-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `af-south-1` | [arn:aws:lambda:af-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `me-south-1` | [arn:aws:lambda:me-south-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | + | `il-central-1` | [arn:aws:lambda:il-central-1:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1](#){: .copyMe}:clipboard: | ??? note "Click to expand and copy code snippets for popular frameworks" @@ -78,7 +78,7 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L Type: AWS::Serverless::Function Properties: Layers: - - !Sub arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29 + - !Sub arn:aws:lambda:${AWS::Region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1 ``` If you use `esbuild` to bundle your code, make sure to exclude `@aws-lambda-powertools` from being bundled since the packages will be already present the Layer: @@ -109,7 +109,7 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L hello: handler: lambda_function.lambda_handler layers: - - arn:aws:lambda:${aws:region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29 + - arn:aws:lambda:${aws:region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1 ``` If you use `esbuild` to bundle your code, make sure to exclude `@aws-lambda-powertools` from being bundled since the packages will be already present the Layer: @@ -141,7 +141,7 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L const powertoolsLayer = lambda.LayerVersion.fromLayerVersionArn( this, 'PowertoolsLayer', - `arn:aws:lambda:${cdk.Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29` + `arn:aws:lambda:${cdk.Stack.of(this).region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1` ); new lambda.Function(this, 'Function', { @@ -193,7 +193,7 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L role = ... handler = "index.handler" runtime = "nodejs16.x" - layers = ["arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29"] + layers = ["arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1"] source_code_hash = filebase64sha256("lambda_function_payload.zip") } ``` @@ -211,7 +211,7 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L const lambdaFunction = new aws.lambda.Function('function', { layers: [ - pulumi.interpolate`arn:aws:lambda:${aws.getRegionOutput().name}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29` + pulumi.interpolate`arn:aws:lambda:${aws.getRegionOutput().name}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1` ], code: new pulumi.asset.FileArchive('lambda_function_payload.zip'), tracingConfig: { @@ -235,7 +235,7 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L ? Do you want to configure advanced settings? Yes ... ? Do you want to enable Lambda layers for this function? Yes - ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29 + ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1 ❯ amplify push -y # Updating an existing function and add the layer @@ -245,7 +245,7 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L - Name: ? Which setting do you want to update? Lambda layers configuration ? Do you want to enable Lambda layers for this function? Yes - ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29 + ? Enter up to 5 existing Lambda layer ARNs (comma-separated): arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1 ? Do you want to edit the local lambda function now? No ``` @@ -255,7 +255,7 @@ You can include Powertools for AWS Lambda (TypeScript) Lambda Layer using [AWS L Change {region} to your AWS region, e.g. `eu-west-1` ```bash title="AWS CLI" - aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScript:29 --region {region} + aws lambda get-layer-version-by-arn --arn arn:aws:lambda:{aws::region}:094274105915:layer:AWSLambdaPowertoolsTypeScriptV2:1 --region {region} ``` The pre-signed URL to download this Lambda Layer will be within `Location` key. diff --git a/docs/maintainers.md b/docs/maintainers.md index dd61ea09aa..fd8480f687 100644 --- a/docs/maintainers.md +++ b/docs/maintainers.md @@ -33,59 +33,59 @@ Previous active maintainers who contributed to this project. These are the most common labels used by maintainers to triage issues, pull requests (PR), and for project management: -| Label | Usage | Notes | -| -------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | -| triage | New issues that require maintainers review | Issue template | -| area/documentation | Improvements or additions to documentation | Examples/Readme files; Doc additions, fixes, etc.; | -| area/logger | Items related to the Logger Utility | PR automation | -| area/metrics | Items related to the Metrics Utility | PR automation | -| area/tracer | Items related to the Tracer Utility | PR automation | -| area/idempotency | Items related to the Idempotency Utility | PR automation | -| area/parameters | Items related to the Parameters Utility | PR automation | -| area/commons | Items related to the Commons Utility | PR automation | -| area/jmespath | Items related to the JMESPath Utility | PR automation | -| area/validation | Items related to the Validation Utility | PR automation | -| area/batch | Items related to the Batch Processing Utility | PR automation | -| area/parser | Items related to the Parser Utility | PR automation | -| area/automation | Items related to automation like GitHub workflows or CI/CD | PR automation | -| area/layers | Items related to the Lambda Layers pipeline | PR automation | -| size/XS | PRs between 0-9 LOC | PR automation | -| size/S | PRs between 10-29 LOC | PR automation | -| size/M | PRs between 30-99 LOC | PR automation | -| size/L | PRs between 100-499 LOC | PR automation | -| size/XL | PRs between 500-999 LOC, often PRs that grown with feedback | PR automation | -| size/XXL | PRs with 1K+ LOC, largely documentation related | PR automation | -| customer-reference | Authorization to use company name in our documentation | Public Relations | -| community-content | Suggested content to feature in our documentation | Public Relations | -| do-not-merge | PRs that are blocked for varying reasons | Timeline is uncertain | -| type/bug | Unexpected, reproducible and unintended software behavior | PR/Release automation; Doc snippets are excluded; | -| type/bug-upstream | Bug caused by upstream dependency | | -| type/not-a-bug | New and existing bug reports incorrectly submitted as bug | Analytics | -| type/deprecation | This item contains code deprecation | | -| type/duplicate | This issue is a duplicate of an existing one | Analytics | -| type/feature-request | Issue requesting new or enhancements to existing features | Issue template | -| type/feature | PRs that introduce new features | PR automation | -| type/enhancement | PRs that introduce minor changes, usually to existing features | PR automation | -| type/RFC | Technical design documents related to a feature request | | -| type/internal | PRs that introduce changes in governance, tech debt and chores (linting setup, baseline, etc.) | PR automation | -| type/tests | PRs that add or change tests | PR automation | -| type/dependencies | Changes that touch dependencies, e.g. Dependabot, etc. | Issues/PR automation | -| type/breaking-change | Changes that will cause customer impact and need careful triage | | -| status/blocked | Items which progress is blocked by external dependency or reason | | -| status/confirmed | Items with clear scope and that are ready for implementation | | -| status/discussing | Items that need to be discussed, elaborated, or refined | | -| status/on-hold | Items that are on hold and will be revisited in the future | | -| status/pending-release | Merged changes that will be available soon | Release automation auto-closes/notifies it | -| status/completed | Items that are complete and have been merged and/or shipped | | -| status/rejected | This is something we will not be working on. At least, not in the measurable future | | -| status/pending-close-response-required | This issue will be closed soon unless the discussion moves forward | Stale Automation | +| Label | Usage | Notes | +| -------------------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| triage | New issues that require maintainers review | Issue template | +| area/documentation | Improvements or additions to documentation | Examples/Readme files; Doc additions, fixes, etc.; | +| area/logger | Items related to the Logger Utility | PR automation | +| area/metrics | Items related to the Metrics Utility | PR automation | +| area/tracer | Items related to the Tracer Utility | PR automation | +| area/idempotency | Items related to the Idempotency Utility | PR automation | +| area/parameters | Items related to the Parameters Utility | PR automation | +| area/commons | Items related to the Commons Utility | PR automation | +| area/jmespath | Items related to the JMESPath Utility | PR automation | +| area/validation | Items related to the Validation Utility | PR automation | +| area/batch | Items related to the Batch Processing Utility | PR automation | +| area/parser | Items related to the Parser Utility | PR automation | +| area/automation | Items related to automation like GitHub workflows or CI/CD | PR automation | +| area/layers | Items related to the Lambda Layers pipeline | PR automation | +| size/XS | PRs between 0-9 LOC | PR automation | +| size/S | PRs between 10-29 LOC | PR automation | +| size/M | PRs between 30-99 LOC | PR automation | +| size/L | PRs between 100-499 LOC | PR automation | +| size/XL | PRs between 500-999 LOC, often PRs that grown with feedback | PR automation | +| size/XXL | PRs with 1K+ LOC, largely documentation related | PR automation | +| customer-reference | Authorization to use company name in our documentation | Public Relations | +| community-content | Suggested content to feature in our documentation | Public Relations | +| do-not-merge | PRs that are blocked for varying reasons | Timeline is uncertain | +| type/bug | Unexpected, reproducible and unintended software behavior | PR/Release automation; Doc snippets are excluded; | +| type/bug-upstream | Bug caused by upstream dependency | | +| type/not-a-bug | New and existing bug reports incorrectly submitted as bug | Analytics | +| type/deprecation | This item contains code deprecation | | +| type/duplicate | This issue is a duplicate of an existing one | Analytics | +| type/feature-request | Issue requesting new or enhancements to existing features | Issue template | +| type/feature | PRs that introduce new features | PR automation | +| type/enhancement | PRs that introduce minor changes, usually to existing features | PR automation | +| type/RFC | Technical design documents related to a feature request | | +| type/internal | PRs that introduce changes in governance, tech debt and chores (linting setup, baseline, etc.) | PR automation | +| type/tests | PRs that add or change tests | PR automation | +| type/dependencies | Changes that touch dependencies, e.g. Dependabot, etc. | Issues/PR automation | +| type/breaking-change | Changes that will cause customer impact and need careful triage | | +| status/blocked | Items which progress is blocked by external dependency or reason | | +| status/confirmed | Items with clear scope and that are ready for implementation | | +| status/discussing | Items that need to be discussed, elaborated, or refined | | +| status/on-hold | Items that are on hold and will be revisited in the future | | +| status/pending-release | Merged changes that will be available soon | Release automation auto-closes/notifies it | +| status/completed | Items that are complete and have been merged and/or shipped | | +| status/rejected | This is something we will not be working on. At least, not in the measurable future | | +| status/pending-close-response-required | This issue will be closed soon unless the discussion moves forward | Stale Automation | | revisit-in-3-months | Blocked issues/PRs that need to be revisited | Often related to `need-customer-feedback`, prioritization, etc. | -| good-first-issue | Something that is suitable for those who want to start contributing | | -| help-wanted | Tasks you want help from anyone to move forward | Bandwidth, complex topics, etc. | -| need-customer-feedback | Tasks that need more feedback before proceeding | 80/20% rule, uncertain, etc. | -| need-more-information | Missing information before making any calls | Signal that investigation or answers are needed | -| need-response | Requires a response from a customer and might be automatically closed if none is received | Marked as stale after 2 weeks, and closed after 3 | -| need-issue | PR is missing a related issue for tracking change | | +| good-first-issue | Something that is suitable for those who want to start contributing | | +| help-wanted | Tasks you want help from anyone to move forward | Bandwidth, complex topics, etc. | +| need-customer-feedback | Tasks that need more feedback before proceeding | 80/20% rule, uncertain, etc. | +| need-more-information | Missing information before making any calls | Signal that investigation or answers are needed | +| need-response | Requires a response from a customer and might be automatically closed if none is received | Marked as stale after 2 weeks, and closed after 3 | +| need-issue | PR is missing a related issue for tracking change | | ## Maintainer Responsibilities diff --git a/docs/requirements.txt b/docs/requirements.txt index 0acd7bb18f..8423d56167 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -462,4 +462,4 @@ watchdog==3.0.0 \ --hash=sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64 \ --hash=sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44 \ --hash=sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33 - # via mkdocs + # via mkdocs \ No newline at end of file diff --git a/docs/snippets/batch/advancedTracingRecordHandler.ts b/docs/snippets/batch/advancedTracingRecordHandler.ts index a65c9f976d..ea85b6833a 100644 --- a/docs/snippets/batch/advancedTracingRecordHandler.ts +++ b/docs/snippets/batch/advancedTracingRecordHandler.ts @@ -3,7 +3,8 @@ import { EventType, processPartialResponse, } from '@aws-lambda-powertools/batch'; -import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer'; +import { Tracer } from '@aws-lambda-powertools/tracer'; +import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; import middy from '@middy/core'; import type { SQSEvent, diff --git a/docs/snippets/batch/customPartialProcessor.ts b/docs/snippets/batch/customPartialProcessor.ts index e634b74d53..65bcd1694c 100644 --- a/docs/snippets/batch/customPartialProcessor.ts +++ b/docs/snippets/batch/customPartialProcessor.ts @@ -13,7 +13,7 @@ import type { SuccessResponse, FailureResponse, BaseRecord, -} from '@aws-lambda-powertools/batch'; +} from '@aws-lambda-powertools/batch/types'; import type { SQSEvent, Context, SQSBatchResponse } from 'aws-lambda'; const tableName = process.env.TABLE_NAME || 'table-not-found'; diff --git a/docs/snippets/batch/extendingFailure.ts b/docs/snippets/batch/extendingFailure.ts index ab1ef530f9..ccfa78966a 100644 --- a/docs/snippets/batch/extendingFailure.ts +++ b/docs/snippets/batch/extendingFailure.ts @@ -1,11 +1,13 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; import { BatchProcessor, EventType, - FailureResponse, - EventSourceType, processPartialResponse, } from '@aws-lambda-powertools/batch'; +import type { + FailureResponse, + EventSourceDataClassTypes, +} from '@aws-lambda-powertools/batch/types'; import { Logger } from '@aws-lambda-powertools/logger'; import type { SQSEvent, @@ -23,10 +25,10 @@ class MyProcessor extends BatchProcessor { } public failureHandler( - record: EventSourceType, + record: EventSourceDataClassTypes, error: Error ): FailureResponse { - this.#metrics.addMetric('BatchRecordFailures', MetricUnits.Count, 1); + this.#metrics.addMetric('BatchRecordFailures', MetricUnit.Count, 1); return super.failureHandler(record, error); } diff --git a/docs/snippets/batch/testingYourCode.ts b/docs/snippets/batch/testingYourCode.ts index a7ffa0e5be..d477e3dfc6 100644 --- a/docs/snippets/batch/testingYourCode.ts +++ b/docs/snippets/batch/testingYourCode.ts @@ -1,7 +1,22 @@ -import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons'; import { handler, processor } from './gettingStartedSQS'; import sqsEvent from './samples/sampleSQSEvent.json'; +const context = { + callbackWaitsForEmptyEventLoop: true, + functionVersion: '$LATEST', + functionName: 'foo-bar-function', + memoryLimitInMB: '128', + logGroupName: '/aws/lambda/foo-bar-function-123456abcdef', + logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', + invokedFunctionArn: + 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', + awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', + getRemainingTimeInMillis: () => 1234, + done: () => console.log('Done!'), + fail: () => console.log('Failed!'), + succeed: () => console.log('Succeeded!'), +}; + describe('Function tests', () => { beforeEach(() => { jest.clearAllMocks(); @@ -9,7 +24,6 @@ describe('Function tests', () => { test('should return one failed message', async () => { // Prepare - const context = dummyContext.helloworldContext; const processorResult = processor; // access processor for additional assertions const successfulRecord = sqsEvent.Records[0]; const failedRecord = sqsEvent.Records[1]; diff --git a/docs/snippets/idempotency/idempotentDecoratorBase.ts b/docs/snippets/idempotency/idempotentDecoratorBase.ts index 852ddae075..409a34647f 100644 --- a/docs/snippets/idempotency/idempotentDecoratorBase.ts +++ b/docs/snippets/idempotency/idempotentDecoratorBase.ts @@ -1,5 +1,5 @@ import type { Context } from 'aws-lambda'; -import type { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; import { IdempotencyConfig, idempotent, diff --git a/docs/snippets/idempotency/samples/makeIdempotentJmes.json b/docs/snippets/idempotency/samples/makeIdempotentJmes.json index 9f608983da..158b3a874f 100644 --- a/docs/snippets/idempotency/samples/makeIdempotentJmes.json +++ b/docs/snippets/idempotency/samples/makeIdempotentJmes.json @@ -27,4 +27,4 @@ }, "body": "{\"user\":\"xyz\",\"productId\":\"123456789\"}", "isBase64Encoded": false -} \ No newline at end of file +} diff --git a/docs/snippets/idempotency/samples/workingWIthIdempotencyRequiredKeyError.json b/docs/snippets/idempotency/samples/workingWIthIdempotencyRequiredKeyError.json index a905b83e7a..14ca8067ae 100644 --- a/docs/snippets/idempotency/samples/workingWIthIdempotencyRequiredKeyError.json +++ b/docs/snippets/idempotency/samples/workingWIthIdempotencyRequiredKeyError.json @@ -4,4 +4,4 @@ "name": "foo", "productId": 10000 } -} \ No newline at end of file +} diff --git a/docs/snippets/idempotency/samples/workingWIthIdempotencyRequiredKeySuccess.json b/docs/snippets/idempotency/samples/workingWIthIdempotencyRequiredKeySuccess.json index e721b2c24c..ef9c932907 100644 --- a/docs/snippets/idempotency/samples/workingWIthIdempotencyRequiredKeySuccess.json +++ b/docs/snippets/idempotency/samples/workingWIthIdempotencyRequiredKeySuccess.json @@ -4,4 +4,4 @@ "name": "Foo" }, "productId": 10000 -} \ No newline at end of file +} diff --git a/docs/snippets/idempotency/samples/workingWithBatch.json b/docs/snippets/idempotency/samples/workingWithBatch.json index 44bd07a141..73a5029d61 100644 --- a/docs/snippets/idempotency/samples/workingWithBatch.json +++ b/docs/snippets/idempotency/samples/workingWithBatch.json @@ -23,4 +23,4 @@ "awsRegion": "us-east-2" } ] -} \ No newline at end of file +} diff --git a/docs/snippets/logger/bringYourOwnFormatterClass.ts b/docs/snippets/logger/bringYourOwnFormatterClass.ts index ab7e207cb9..48c5642d8c 100644 --- a/docs/snippets/logger/bringYourOwnFormatterClass.ts +++ b/docs/snippets/logger/bringYourOwnFormatterClass.ts @@ -1,15 +1,18 @@ -import { LogFormatter } from '@aws-lambda-powertools/logger'; -import { +import { LogFormatter, LogItem } from '@aws-lambda-powertools/logger'; +import type { LogAttributes, UnformattedAttributes, -} from '@aws-lambda-powertools/logger/lib/types'; +} from '@aws-lambda-powertools/logger/types'; // Replace this line with your own type type MyCompanyLog = LogAttributes; class MyCompanyLogFormatter extends LogFormatter { - public formatAttributes(attributes: UnformattedAttributes): MyCompanyLog { - return { + public formatAttributes( + attributes: UnformattedAttributes, + additionalLogAttributes: LogAttributes + ): LogItem { + const baseAttributes: MyCompanyLog = { message: attributes.message, service: attributes.serviceName, environment: attributes.environment, @@ -31,6 +34,11 @@ class MyCompanyLogFormatter extends LogFormatter { sampleRateValue: attributes.sampleRateValue, }, }; + + const logItem = new LogItem({ attributes: baseAttributes }); + logItem.addAttributes(additionalLogAttributes); // add any attributes not explicitly defined + + return logItem; } } diff --git a/docs/snippets/logger/bringYourOwnFormatterHandler.ts b/docs/snippets/logger/bringYourOwnFormatterHandler.ts index c374c4a255..7eadd526c0 100644 --- a/docs/snippets/logger/bringYourOwnFormatterHandler.ts +++ b/docs/snippets/logger/bringYourOwnFormatterHandler.ts @@ -1,5 +1,6 @@ import { Logger } from '@aws-lambda-powertools/logger'; -import { MyCompanyLogFormatter } from './utils/formatters/MyCompanyLogFormatter'; +import { MyCompanyLogFormatter } from './bringYourOwnFormatterClass'; +import type { Context } from 'aws-lambda'; const logger = new Logger({ logFormatter: new MyCompanyLogFormatter(), @@ -15,7 +16,10 @@ const logger = new Logger({ }, }); -export const handler = async (event, context): Promise => { +export const handler = async ( + _event: unknown, + context: Context +): Promise => { logger.addContext(context); logger.info('This is an INFO log', { diff --git a/docs/snippets/logger/clearStateDecorator.ts b/docs/snippets/logger/clearStateDecorator.ts index e31d66b190..0f4ec2b81f 100644 --- a/docs/snippets/logger/clearStateDecorator.ts +++ b/docs/snippets/logger/clearStateDecorator.ts @@ -1,5 +1,5 @@ import { Logger } from '@aws-lambda-powertools/logger'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; // Persistent attributes added outside the handler will be // cached across invocations @@ -14,12 +14,15 @@ const logger = new Logger({ class Lambda implements LambdaInterface { // Enable the clear state flag @logger.injectLambdaContext({ clearState: true }) - public async handler(event: unknown, _context: unknown): Promise { + public async handler( + event: { specialKey: string }, + _context: unknown + ): Promise { // Persistent attributes added inside the handler will NOT be cached // across invocations - if (event['special_key'] === '123456') { + if (event['specialKey'] === '123456') { logger.appendKeys({ - details: { special_key: '123456' }, + details: { specialKey: '123456' }, }); } logger.debug('This is a DEBUG log'); diff --git a/docs/snippets/logger/clearStateMiddy.ts b/docs/snippets/logger/clearStateMiddy.ts index 7696c75b63..1c18e550a0 100644 --- a/docs/snippets/logger/clearStateMiddy.ts +++ b/docs/snippets/logger/clearStateMiddy.ts @@ -1,4 +1,5 @@ -import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware'; import middy from '@middy/core'; // Persistent attributes added outside the handler will be @@ -12,14 +13,14 @@ const logger = new Logger({ }); const lambdaHandler = async ( - event: { special_key: string }, + event: { specialKey: string }, _context: unknown ): Promise => { // Persistent attributes added inside the handler will NOT be cached // across invocations if (event['special_key'] === '123456') { logger.appendKeys({ - details: { special_key: event['special_key'] }, + details: { special_key: event['specialKey'] }, }); } logger.debug('This is a DEBUG log'); diff --git a/docs/snippets/logger/decorator.ts b/docs/snippets/logger/decorator.ts index 0cd9be0cf4..1f3317596a 100644 --- a/docs/snippets/logger/decorator.ts +++ b/docs/snippets/logger/decorator.ts @@ -1,5 +1,5 @@ import { Logger } from '@aws-lambda-powertools/logger'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const logger = new Logger(); diff --git a/docs/snippets/logger/eventDecorator.ts b/docs/snippets/logger/eventDecorator.ts index 9adf1518f1..bded346d2b 100644 --- a/docs/snippets/logger/eventDecorator.ts +++ b/docs/snippets/logger/eventDecorator.ts @@ -1,5 +1,5 @@ import { Logger } from '@aws-lambda-powertools/logger'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const logger = new Logger(); diff --git a/docs/snippets/logger/eventMiddy.ts b/docs/snippets/logger/eventMiddy.ts index a3648c734a..3c3a40596b 100644 --- a/docs/snippets/logger/eventMiddy.ts +++ b/docs/snippets/logger/eventMiddy.ts @@ -1,4 +1,5 @@ -import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware'; import middy from '@middy/core'; const logger = new Logger(); diff --git a/docs/snippets/logger/manual.ts b/docs/snippets/logger/manual.ts index 00eaa38ac5..287b5beea0 100644 --- a/docs/snippets/logger/manual.ts +++ b/docs/snippets/logger/manual.ts @@ -1,8 +1,12 @@ import { Logger } from '@aws-lambda-powertools/logger'; +import type { Context } from 'aws-lambda'; const logger = new Logger(); -export const handler = async (_event, context): Promise => { +export const handler = async ( + _event: unknown, + context: Context +): Promise => { logger.addContext(context); logger.info('This is an INFO log with some context'); diff --git a/docs/snippets/logger/middy.ts b/docs/snippets/logger/middy.ts index f0dcbaa5aa..d92320abfe 100644 --- a/docs/snippets/logger/middy.ts +++ b/docs/snippets/logger/middy.ts @@ -1,4 +1,5 @@ -import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware'; import middy from '@middy/core'; const logger = new Logger(); diff --git a/docs/snippets/logger/unitTesting.ts b/docs/snippets/logger/unitTesting.ts index d120be2858..671c93c181 100644 --- a/docs/snippets/logger/unitTesting.ts +++ b/docs/snippets/logger/unitTesting.ts @@ -1,8 +1,24 @@ -import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons'; +declare const handler: (event: unknown, context: unknown) => Promise; + +const context = { + callbackWaitsForEmptyEventLoop: true, + functionVersion: '$LATEST', + functionName: 'foo-bar-function', + memoryLimitInMB: '128', + logGroupName: '/aws/lambda/foo-bar-function-123456abcdef', + logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', + invokedFunctionArn: + 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', + awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', + getRemainingTimeInMillis: () => 1234, + done: () => console.log('Done!'), + fail: () => console.log('Failed!'), + succeed: () => console.log('Succeeded!'), +}; describe('MyUnitTest', () => { test('Lambda invoked successfully', async () => { const testEvent = { test: 'test' }; - await handler(testEvent, dummyContext); + await handler(testEvent, context); }); }); diff --git a/docs/snippets/metrics/addHighResolutionMetric.ts b/docs/snippets/metrics/addHighResolutionMetric.ts index f2981b8514..001f753278 100644 --- a/docs/snippets/metrics/addHighResolutionMetric.ts +++ b/docs/snippets/metrics/addHighResolutionMetric.ts @@ -1,6 +1,6 @@ import { Metrics, - MetricUnits, + MetricUnit, MetricResolution, } from '@aws-lambda-powertools/metrics'; @@ -15,7 +15,7 @@ export const handler = async ( ): Promise => { metrics.addMetric( 'successfulBooking', - MetricUnits.Count, + MetricUnit.Count, 1, MetricResolution.High ); diff --git a/docs/snippets/metrics/addMetadata.ts b/docs/snippets/metrics/addMetadata.ts index 6836191b91..9e86310e67 100644 --- a/docs/snippets/metrics/addMetadata.ts +++ b/docs/snippets/metrics/addMetadata.ts @@ -1,8 +1,5 @@ -import { - Metrics, - MetricUnits, - logMetrics, -} from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; import middy from '@middy/core'; const metrics = new Metrics({ @@ -14,7 +11,7 @@ const lambdaHandler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); metrics.addMetadata('bookingId', '7051cd10-6283-11ec-90d6-0242ac120003'); }; diff --git a/docs/snippets/metrics/basicUsage.ts b/docs/snippets/metrics/basicUsage.ts index 868f5b67af..1deb2285bb 100644 --- a/docs/snippets/metrics/basicUsage.ts +++ b/docs/snippets/metrics/basicUsage.ts @@ -1,4 +1,4 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -9,5 +9,5 @@ export const handler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }; diff --git a/docs/snippets/metrics/captureColdStartMetricDecorator.ts b/docs/snippets/metrics/captureColdStartMetricDecorator.ts index d2e050224e..390657c93f 100644 --- a/docs/snippets/metrics/captureColdStartMetricDecorator.ts +++ b/docs/snippets/metrics/captureColdStartMetricDecorator.ts @@ -1,5 +1,5 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -9,6 +9,6 @@ const metrics = new Metrics({ export class MyFunction implements LambdaInterface { @metrics.logMetrics({ captureColdStartMetric: true }) public async handler(_event: unknown, _context: unknown): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); } } diff --git a/docs/snippets/metrics/captureColdStartMetricMiddy.ts b/docs/snippets/metrics/captureColdStartMetricMiddy.ts index c598a09e03..a09ada1939 100644 --- a/docs/snippets/metrics/captureColdStartMetricMiddy.ts +++ b/docs/snippets/metrics/captureColdStartMetricMiddy.ts @@ -1,8 +1,5 @@ -import { - Metrics, - MetricUnits, - logMetrics, -} from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; import middy from '@middy/core'; const metrics = new Metrics({ @@ -14,7 +11,7 @@ const lambdaHandler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }; export const handler = middy(lambdaHandler).use( diff --git a/docs/snippets/metrics/createMetrics.ts b/docs/snippets/metrics/createMetrics.ts index e7065e8f29..9a66a1bbb3 100644 --- a/docs/snippets/metrics/createMetrics.ts +++ b/docs/snippets/metrics/createMetrics.ts @@ -1,4 +1,4 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -9,6 +9,6 @@ export const handler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); metrics.publishStoredMetrics(); }; diff --git a/docs/snippets/metrics/customDimensions.ts b/docs/snippets/metrics/customDimensions.ts index 8eeaebebda..a36473571c 100644 --- a/docs/snippets/metrics/customDimensions.ts +++ b/docs/snippets/metrics/customDimensions.ts @@ -1,4 +1,4 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -10,6 +10,6 @@ export const handler = async ( _context: unknown ): Promise => { metrics.addDimension('environment', 'prod'); - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); metrics.publishStoredMetrics(); }; diff --git a/docs/snippets/metrics/decorator.ts b/docs/snippets/metrics/decorator.ts index 9d69c5fa56..3595cc41d2 100644 --- a/docs/snippets/metrics/decorator.ts +++ b/docs/snippets/metrics/decorator.ts @@ -1,5 +1,5 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -9,7 +9,7 @@ const metrics = new Metrics({ class Lambda implements LambdaInterface { @metrics.logMetrics() public async handler(_event: unknown, _context: unknown): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); } } diff --git a/docs/snippets/metrics/defaultDimensions.ts b/docs/snippets/metrics/defaultDimensions.ts index c0bc92b99b..7c0340e5f5 100644 --- a/docs/snippets/metrics/defaultDimensions.ts +++ b/docs/snippets/metrics/defaultDimensions.ts @@ -1,4 +1,4 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -10,5 +10,5 @@ export const handler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }; diff --git a/docs/snippets/metrics/defaultDimensionsDecorator.ts b/docs/snippets/metrics/defaultDimensionsDecorator.ts index 23796a16fc..3c3767ecfd 100644 --- a/docs/snippets/metrics/defaultDimensionsDecorator.ts +++ b/docs/snippets/metrics/defaultDimensionsDecorator.ts @@ -1,5 +1,5 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -11,7 +11,7 @@ export class Lambda implements LambdaInterface { // Decorate your handler class method @metrics.logMetrics({ defaultDimensions: DEFAULT_DIMENSIONS }) public async handler(_event: unknown, _context: unknown): Promise { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); } } diff --git a/docs/snippets/metrics/defaultDimensionsMiddy.ts b/docs/snippets/metrics/defaultDimensionsMiddy.ts index b75ed56a6b..b5f7ec9256 100644 --- a/docs/snippets/metrics/defaultDimensionsMiddy.ts +++ b/docs/snippets/metrics/defaultDimensionsMiddy.ts @@ -1,8 +1,5 @@ -import { - Metrics, - MetricUnits, - logMetrics, -} from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; import middy from '@middy/core'; const metrics = new Metrics({ @@ -14,7 +11,7 @@ const lambdaHandler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }; // Wrap the handler with middy diff --git a/docs/snippets/metrics/manual.ts b/docs/snippets/metrics/manual.ts index d64a3b5393..79c0068ca8 100644 --- a/docs/snippets/metrics/manual.ts +++ b/docs/snippets/metrics/manual.ts @@ -1,4 +1,4 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -9,6 +9,6 @@ export const handler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 10); + metrics.addMetric('successfulBooking', MetricUnit.Count, 10); metrics.publishStoredMetrics(); }; diff --git a/docs/snippets/metrics/middy.ts b/docs/snippets/metrics/middy.ts index 0a8330e931..d45a4aa08d 100644 --- a/docs/snippets/metrics/middy.ts +++ b/docs/snippets/metrics/middy.ts @@ -1,8 +1,5 @@ -import { - Metrics, - MetricUnits, - logMetrics, -} from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; import middy from '@middy/core'; const metrics = new Metrics({ @@ -14,7 +11,7 @@ const lambdaHandler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }; export const handler = middy(lambdaHandler).use(logMetrics(metrics)); diff --git a/docs/snippets/metrics/multiValueMetrics.ts b/docs/snippets/metrics/multiValueMetrics.ts index 6614e39409..be185d0718 100644 --- a/docs/snippets/metrics/multiValueMetrics.ts +++ b/docs/snippets/metrics/multiValueMetrics.ts @@ -1,4 +1,4 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -9,7 +9,7 @@ export const handler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('performedActionA', MetricUnits.Count, 2); + metrics.addMetric('performedActionA', MetricUnit.Count, 2); // do something else... - metrics.addMetric('performedActionA', MetricUnits.Count, 1); + metrics.addMetric('performedActionA', MetricUnit.Count, 1); }; diff --git a/docs/snippets/metrics/sam.ts b/docs/snippets/metrics/sam.ts index 79cb23e522..999e3fd9e3 100644 --- a/docs/snippets/metrics/sam.ts +++ b/docs/snippets/metrics/sam.ts @@ -1,8 +1,8 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; // Metrics parameters fetched from the environment variables (see template.yaml tab) const metrics = new Metrics(); -metrics.addMetric('successfulBooking', MetricUnits.Count, 1); +metrics.addMetric('successfulBooking', MetricUnit.Count, 1); // You can also pass the parameters in the constructor // const metrics = new Metrics({ diff --git a/docs/snippets/metrics/setDefaultDimensions.ts b/docs/snippets/metrics/setDefaultDimensions.ts index ddb54f7691..7125519d54 100644 --- a/docs/snippets/metrics/setDefaultDimensions.ts +++ b/docs/snippets/metrics/setDefaultDimensions.ts @@ -1,4 +1,4 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -10,5 +10,5 @@ export const handler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }; diff --git a/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts b/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts index d7b811aa3b..44bc941bf4 100644 --- a/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts +++ b/docs/snippets/metrics/singleMetricDifferentDimsDecorator.ts @@ -1,5 +1,5 @@ -import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const metrics = new Metrics({ namespace: 'serverlessAirline', @@ -11,12 +11,12 @@ class Lambda implements LambdaInterface { public async handler(_event: unknown, _context: unknown): Promise { metrics.addDimension('metricUnit', 'milliseconds'); // This metric will have the "metricUnit" dimension, and no "metricType" dimension: - metrics.addMetric('latency', MetricUnits.Milliseconds, 56); + metrics.addMetric('latency', MetricUnit.Milliseconds, 56); const singleMetric = metrics.singleMetric(); // This metric will have the "metricType" dimension, and no "metricUnit" dimension: singleMetric.addDimension('metricType', 'business'); - singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); + singleMetric.addMetric('orderSubmitted', MetricUnit.Count, 1); } } diff --git a/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts b/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts index 66642078df..1344455fc8 100644 --- a/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts +++ b/docs/snippets/metrics/singleMetricDifferentDimsMiddy.ts @@ -1,8 +1,5 @@ -import { - Metrics, - MetricUnits, - logMetrics, -} from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; import middy from '@middy/core'; const metrics = new Metrics({ @@ -16,12 +13,12 @@ const lambdaHandler = async ( ): Promise => { metrics.addDimension('metricUnit', 'milliseconds'); // This metric will have the "metricUnit" dimension, and no "metricType" dimension: - metrics.addMetric('latency', MetricUnits.Milliseconds, 56); + metrics.addMetric('latency', MetricUnit.Milliseconds, 56); const singleMetric = metrics.singleMetric(); // This metric will have the "metricType" dimension, and no "metricUnit" dimension: singleMetric.addDimension('metricType', 'business'); - singleMetric.addMetric('orderSubmitted', MetricUnits.Count, 1); + singleMetric.addMetric('orderSubmitted', MetricUnit.Count, 1); }; export const handler = middy(lambdaHandler).use(logMetrics(metrics)); diff --git a/docs/snippets/metrics/throwOnEmptyMetrics.ts b/docs/snippets/metrics/throwOnEmptyMetrics.ts index 8fc01ae4b3..2acb3f3dfd 100644 --- a/docs/snippets/metrics/throwOnEmptyMetrics.ts +++ b/docs/snippets/metrics/throwOnEmptyMetrics.ts @@ -1,8 +1,5 @@ -import { - Metrics, - MetricUnits, - logMetrics, -} from '@aws-lambda-powertools/metrics'; +import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; import middy from '@middy/core'; const metrics = new Metrics({ @@ -14,7 +11,7 @@ const lambdaHandler = async ( _event: unknown, _context: unknown ): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }; export const handler = middy(lambdaHandler).use( diff --git a/docs/snippets/package.json b/docs/snippets/package.json index a786f7aaae..92650ec9ca 100644 --- a/docs/snippets/package.json +++ b/docs/snippets/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "1.9.0", + "version": "1.14.0", "description": "A collection code snippets for the Powertools for AWS Lambda (TypeScript) docs", "author": { "name": "Amazon Web Services", @@ -39,4 +39,4 @@ "axios": "^1.6.7", "hashi-vault-js": "^0.4.14" } -} +} \ No newline at end of file diff --git a/docs/snippets/parameters/appConfigProviderCustomClient.ts b/docs/snippets/parameters/appConfigProviderCustomClient.ts index d4315fb58c..887fcc765a 100644 --- a/docs/snippets/parameters/appConfigProviderCustomClient.ts +++ b/docs/snippets/parameters/appConfigProviderCustomClient.ts @@ -5,6 +5,8 @@ import { AppConfigDataClient } from '@aws-sdk/client-appconfigdata'; const appConfigClient = new AppConfigDataClient({ region: 'us-east-1' }); // pass the client to the provider const configsProvider = new AppConfigProvider({ + application: 'my-app', + environment: 'my-env', awsSdkV3Client: appConfigClient, }); diff --git a/docs/snippets/parameters/customProviderVault.ts b/docs/snippets/parameters/customProviderVault.ts index 9fea8f22aa..91490f45a7 100644 --- a/docs/snippets/parameters/customProviderVault.ts +++ b/docs/snippets/parameters/customProviderVault.ts @@ -1,7 +1,6 @@ import { Logger } from '@aws-lambda-powertools/logger'; import { BaseProvider } from '@aws-lambda-powertools/parameters/base'; import Vault from 'hashi-vault-js'; -import { isErrorResponse } from './customProviderVaultTypes'; import type { HashiCorpVaultProviderOptions, HashiCorpVaultGetOptions, @@ -18,7 +17,9 @@ class HashiCorpVaultProvider extends BaseProvider { * @param {HashiCorpVaultProviderOptions} config - The configuration object. */ public constructor(config: HashiCorpVaultProviderOptions) { - super(); + super({ + proto: Vault, + }); const { url, token, clientConfig, vaultClient } = config; if (vaultClient) { @@ -28,7 +29,7 @@ class HashiCorpVaultProvider extends BaseProvider { throw Error('Not a valid Vault client provided'); } } else { - const config: Vault.VaultConfig = { + const config = { baseUrl: url, ...(clientConfig ?? { timeout: 10000, @@ -90,7 +91,7 @@ class HashiCorpVaultProvider extends BaseProvider { mount ); - if (isErrorResponse(response)) { + if (response.isVaultError) { this.#logger.error('An error occurred', { error: response.vaultHelpMessage, }); diff --git a/docs/snippets/parameters/customProviderVaultTypes.ts b/docs/snippets/parameters/customProviderVaultTypes.ts index 09845d3cbd..ac5d99ac4a 100644 --- a/docs/snippets/parameters/customProviderVaultTypes.ts +++ b/docs/snippets/parameters/customProviderVaultTypes.ts @@ -1,5 +1,5 @@ import { GetOptionsInterface } from '@aws-lambda-powertools/parameters/base/types'; -import type Vault from 'hashi-vault-js'; +import Vault from 'hashi-vault-js'; /** * Base interface for HashiCorpVaultProviderOptions. @@ -26,7 +26,7 @@ interface HashiCorpVaultProviderOptionsWithClientConfig /** * Optional configuration to pass during client initialization to customize the `hashi-vault-js` client. */ - clientConfig?: Omit; + clientConfig?: unknown; /** * This property should never be passed. */ @@ -83,15 +83,4 @@ interface HashiCorpVaultGetOptions extends GetOptionsInterface { sdkOptions?: HashiCorpVaultReadKVSecretOptions; } -/** - * Typeguard that discriminates the type of the response and excludes the ErrorResponse type. - * - * @param object The response object to discriminate - */ -export const isErrorResponse = ( - object: Vault.ReadKVSecretResponse -): object is Vault.ErrorResponse => { - return 'isVaultError' in object; -}; - export type { HashiCorpVaultProviderOptions, HashiCorpVaultGetOptions }; diff --git a/docs/snippets/parameters/dynamoDBProviderCustomClient.ts b/docs/snippets/parameters/dynamoDBProviderCustomClient.ts index 2eb94ef190..ef9d2af2a0 100644 --- a/docs/snippets/parameters/dynamoDBProviderCustomClient.ts +++ b/docs/snippets/parameters/dynamoDBProviderCustomClient.ts @@ -4,7 +4,10 @@ import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; // construct your clients with any custom configuration const dynamoDBClient = new DynamoDBClient({ region: 'us-east-1' }); // pass the client to the provider -const valuesProvider = new DynamoDBProvider({ awsSdkV3Client: dynamoDBClient }); +const valuesProvider = new DynamoDBProvider({ + tableName: 'my-table', + awsSdkV3Client: dynamoDBClient, +}); export const handler = async (): Promise => { // Retrieve a single value diff --git a/docs/snippets/parameters/testingYourCodeProvidersJestMock.ts b/docs/snippets/parameters/testingYourCodeProvidersJestMock.ts index 96d4d025d9..b93062e765 100644 --- a/docs/snippets/parameters/testingYourCodeProvidersJestMock.ts +++ b/docs/snippets/parameters/testingYourCodeProvidersJestMock.ts @@ -1,5 +1,6 @@ import { handler } from './testingYourCodeFunctionsHandler'; import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig'; +import { Uint8ArrayBlobAdapter } from '@smithy/util-stream'; describe('Function tests', () => { const providerSpy = jest.spyOn(AppConfigProvider.prototype, 'get'); @@ -16,7 +17,9 @@ describe('Function tests', () => { name: 'paywall', }, }; - providerSpy.mockResolvedValueOnce(expectedConfig); + providerSpy.mockResolvedValueOnce( + Uint8ArrayBlobAdapter.fromString(JSON.stringify(expectedConfig)) + ); // Act const result = await handler({}, {}); diff --git a/docs/snippets/tracer/accessRootTraceId.ts b/docs/snippets/tracer/accessRootTraceId.ts index f868d8b5e2..2491f7530f 100644 --- a/docs/snippets/tracer/accessRootTraceId.ts +++ b/docs/snippets/tracer/accessRootTraceId.ts @@ -5,7 +5,7 @@ const tracer = new Tracer({ serviceName: 'serverlessAirline' }); export const handler = async ( _event: unknown, _context: unknown -): Promise => { +): Promise => { try { } catch (err) { const rootTraceId = tracer.getRootXrayTraceId(); diff --git a/docs/snippets/tracer/captureMethodDecorator.ts b/docs/snippets/tracer/captureMethodDecorator.ts index 1b15cf44c7..809a87fd2a 100644 --- a/docs/snippets/tracer/captureMethodDecorator.ts +++ b/docs/snippets/tracer/captureMethodDecorator.ts @@ -1,5 +1,5 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); diff --git a/docs/snippets/tracer/decorator.ts b/docs/snippets/tracer/decorator.ts index 616c3eb63d..985b82bde5 100644 --- a/docs/snippets/tracer/decorator.ts +++ b/docs/snippets/tracer/decorator.ts @@ -1,5 +1,5 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); diff --git a/docs/snippets/tracer/disableCaptureResponseHandler.ts b/docs/snippets/tracer/disableCaptureResponseHandler.ts index 9a08a31dcc..2c35dfb080 100644 --- a/docs/snippets/tracer/disableCaptureResponseHandler.ts +++ b/docs/snippets/tracer/disableCaptureResponseHandler.ts @@ -1,5 +1,5 @@ import { Tracer } from '@aws-lambda-powertools/tracer'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); diff --git a/docs/snippets/tracer/disableCaptureResponseMethod.ts b/docs/snippets/tracer/disableCaptureResponseMethod.ts index 26b6e2519a..9633608907 100644 --- a/docs/snippets/tracer/disableCaptureResponseMethod.ts +++ b/docs/snippets/tracer/disableCaptureResponseMethod.ts @@ -1,4 +1,4 @@ -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; import { Tracer } from '@aws-lambda-powertools/tracer'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); diff --git a/docs/snippets/tracer/disableCaptureResponseMiddy.ts b/docs/snippets/tracer/disableCaptureResponseMiddy.ts index 82aa1b345c..5af7ec2342 100644 --- a/docs/snippets/tracer/disableCaptureResponseMiddy.ts +++ b/docs/snippets/tracer/disableCaptureResponseMiddy.ts @@ -1,4 +1,5 @@ -import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer'; +import { Tracer } from '@aws-lambda-powertools/tracer'; +import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; import middy from '@middy/core'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); diff --git a/docs/snippets/tracer/middy.ts b/docs/snippets/tracer/middy.ts index 22fe705619..591b9ff00d 100644 --- a/docs/snippets/tracer/middy.ts +++ b/docs/snippets/tracer/middy.ts @@ -1,4 +1,5 @@ -import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer'; +import { Tracer } from '@aws-lambda-powertools/tracer'; +import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; import middy from '@middy/core'; const tracer = new Tracer({ serviceName: 'serverlessAirline' }); diff --git a/docs/upgrade.md b/docs/upgrade.md index 72d196722b..bc104bc099 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -3,8 +3,6 @@ title: Upgrade guide description: Guide to update between major Powertools for AWS Lambda (TypeScript) versions --- -!!! note "We expect to release v2 by the end of February 2024." - ## Migrate from v1 to v2 V2 is focused on official support for ESM (ECMAScript modules). We've made other minimal breaking changes to make your transition to v2 as smooth as possible. @@ -20,6 +18,7 @@ V2 is focused on official support for ESM (ECMAScript modules). We've made other | **Logger** | Updated [custom log formatter](#custom-log-formatter) to include standard as well as persistent keys. | Yes | | **Logger and Tracer** | Removed deprecated `createLogger` and `createTracer` helper functions in favor of direct instantiation. | Yes | + ### First steps Before you start, we suggest making a copy of your current working project or create a new git branch. diff --git a/docs/utilities/batch.md b/docs/utilities/batch.md index 5c2b5631f1..486d7c12a0 100644 --- a/docs/utilities/batch.md +++ b/docs/utilities/batch.md @@ -64,6 +64,13 @@ This behavior changes when you enable [ReportBatchItemFailures feature](https:// ## Getting started +### Installation + +Install the library in your project +```shell +npm i @aws-lambda-powertools/batch +``` + For this feature to work, you need to **(1)** configure your Lambda function event source to use `ReportBatchItemFailures`, and **(2)** return [a specific response](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting){target="_blank" rel="nofollow"} to report which records failed to be processed. Use your preferred deployment framework to set the correct configuration while this utility handles the correct response to be returned. diff --git a/examples/cdk/CHANGELOG.md b/examples/cdk/CHANGELOG.md index e6e225294f..bfa49015fa 100644 --- a/examples/cdk/CHANGELOG.md +++ b/examples/cdk/CHANGELOG.md @@ -7,10 +7,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package cdk-sample - - - - ## [1.18.0](https://github.com/aws-powertools/powertools-lambda-typescript/compare/v1.17.0...v1.18.0) (2024-01-26) **Note:** Version bump only for package cdk-sample diff --git a/examples/cdk/functions/common/powertools.ts b/examples/cdk/functions/common/powertools.ts index 01949a91ac..fa0b798afa 100644 --- a/examples/cdk/functions/common/powertools.ts +++ b/examples/cdk/functions/common/powertools.ts @@ -1,7 +1,7 @@ import { Logger } from '@aws-lambda-powertools/logger'; import { Metrics } from '@aws-lambda-powertools/metrics'; import { Tracer } from '@aws-lambda-powertools/tracer'; -import { PT_VERSION } from '@aws-lambda-powertools/commons/lib/version'; +import { PT_VERSION } from '@aws-lambda-powertools/commons'; const defaultValues = { region: process.env.AWS_REGION || 'N/A', diff --git a/examples/cdk/functions/get-all-items.ts b/examples/cdk/functions/get-all-items.ts index 7fe222d773..878bdbb37a 100644 --- a/examples/cdk/functions/get-all-items.ts +++ b/examples/cdk/functions/get-all-items.ts @@ -1,6 +1,6 @@ -import { injectLambdaContext } from '@aws-lambda-powertools/logger'; -import { logMetrics } from '@aws-lambda-powertools/metrics'; -import { captureLambdaHandler } from '@aws-lambda-powertools/tracer'; +import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; +import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; import { ScanCommand } from '@aws-sdk/lib-dynamodb'; import middy from '@middy/core'; import { diff --git a/examples/cdk/functions/get-by-id.ts b/examples/cdk/functions/get-by-id.ts index 88e76511a1..f35c053eef 100644 --- a/examples/cdk/functions/get-by-id.ts +++ b/examples/cdk/functions/get-by-id.ts @@ -1,4 +1,4 @@ -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; import { GetCommand } from '@aws-sdk/lib-dynamodb'; import { APIGatewayProxyEvent, diff --git a/examples/cdk/functions/uuid.ts b/examples/cdk/functions/uuid.ts index 58e017dbea..04ab559e1f 100644 --- a/examples/cdk/functions/uuid.ts +++ b/examples/cdk/functions/uuid.ts @@ -1,6 +1,6 @@ import { randomUUID } from 'node:crypto'; -exports.handler = async (_event) => { +exports.handler = async () => { return { statusCode: 200, body: JSON.stringify(randomUUID()), diff --git a/examples/cdk/package.json b/examples/cdk/package.json index 1c396417ef..a35a55d66c 100644 --- a/examples/cdk/package.json +++ b/examples/cdk/package.json @@ -50,4 +50,4 @@ "phin": "^3.7.0", "source-map-support": "^0.5.21" } -} +} \ No newline at end of file diff --git a/examples/sam/package.json b/examples/sam/package.json index 962af11079..7f4715c93c 100644 --- a/examples/sam/package.json +++ b/examples/sam/package.json @@ -41,4 +41,4 @@ "@middy/core": "^4.7.0", "phin": "^3.7.0" } -} +} \ No newline at end of file diff --git a/examples/sam/src/common/powertools.ts b/examples/sam/src/common/powertools.ts index 01949a91ac..fa0b798afa 100644 --- a/examples/sam/src/common/powertools.ts +++ b/examples/sam/src/common/powertools.ts @@ -1,7 +1,7 @@ import { Logger } from '@aws-lambda-powertools/logger'; import { Metrics } from '@aws-lambda-powertools/metrics'; import { Tracer } from '@aws-lambda-powertools/tracer'; -import { PT_VERSION } from '@aws-lambda-powertools/commons/lib/version'; +import { PT_VERSION } from '@aws-lambda-powertools/commons'; const defaultValues = { region: process.env.AWS_REGION || 'N/A', diff --git a/examples/sam/src/get-all-items.ts b/examples/sam/src/get-all-items.ts index a43b3f3c1e..cb2b09fbfd 100644 --- a/examples/sam/src/get-all-items.ts +++ b/examples/sam/src/get-all-items.ts @@ -6,9 +6,9 @@ import { import middy from '@middy/core'; import { tableName } from './common/constants'; import { logger, tracer, metrics } from './common/powertools'; -import { logMetrics } from '@aws-lambda-powertools/metrics'; -import { injectLambdaContext } from '@aws-lambda-powertools/logger'; -import { captureLambdaHandler } from '@aws-lambda-powertools/tracer'; +import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; +import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware'; +import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; import { docClient } from './common/dynamodb-client'; import { ScanCommand } from '@aws-sdk/lib-dynamodb'; import { getUuid } from './common/getUuid'; diff --git a/examples/sam/src/get-by-id.ts b/examples/sam/src/get-by-id.ts index 88e76511a1..f35c053eef 100644 --- a/examples/sam/src/get-by-id.ts +++ b/examples/sam/src/get-by-id.ts @@ -1,4 +1,4 @@ -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; import { GetCommand } from '@aws-sdk/lib-dynamodb'; import { APIGatewayProxyEvent, diff --git a/examples/sam/src/get-uuid.ts b/examples/sam/src/get-uuid.ts index 58e017dbea..04ab559e1f 100644 --- a/examples/sam/src/get-uuid.ts +++ b/examples/sam/src/get-uuid.ts @@ -1,6 +1,6 @@ import { randomUUID } from 'node:crypto'; -exports.handler = async (_event) => { +exports.handler = async () => { return { statusCode: 200, body: JSON.stringify(randomUUID()), diff --git a/layers/bin/layers.ts b/layers/bin/layers.ts index bc7390c4fd..be4605b3e3 100644 --- a/layers/bin/layers.ts +++ b/layers/bin/layers.ts @@ -11,7 +11,7 @@ const app = new App(); new LayerPublisherStack(app, 'LayerPublisherStack', { powertoolsPackageVersion: app.node.tryGetContext('PowertoolsPackageVersion'), buildFromLocal: app.node.tryGetContext('BuildFromLocal') || false, - layerName: 'AWSLambdaPowertoolsTypeScript', + layerName: 'AWSLambdaPowertoolsTypeScriptV2', ssmParameterLayerArn: SSM_PARAM_LAYER_ARN, }); diff --git a/layers/package.json b/layers/package.json index 489d4faa41..cb5249cb52 100644 --- a/layers/package.json +++ b/layers/package.json @@ -43,4 +43,4 @@ "aws-cdk-lib": "^2.127.0", "esbuild": "^0.20.0" } -} +} \ No newline at end of file diff --git a/layers/tests/e2e/layerPublisher.class.test.functionCode.ts b/layers/tests/e2e/layerPublisher.class.test.functionCode.ts index d63f389d7c..b95eeafea9 100644 --- a/layers/tests/e2e/layerPublisher.class.test.functionCode.ts +++ b/layers/tests/e2e/layerPublisher.class.test.functionCode.ts @@ -81,7 +81,15 @@ const getVersionFromModule = async (moduleName: string): Promise => { export const handler = async (): Promise => { // Check that the packages version matches the expected one - for (const moduleName of ['commons', 'logger', 'metrics', 'tracer']) { + for (const moduleName of [ + 'commons', + 'logger', + 'metrics', + 'tracer', + 'parameters', + 'idempotency', + 'batch', + ]) { const moduleVersion = await getVersionFromModule(moduleName); if (moduleVersion != expectedVersion) { throw new Error( diff --git a/layers/tests/e2e/layerPublisher.test.ts b/layers/tests/e2e/layerPublisher.test.ts index 6772da71a6..9f5535c9c0 100644 --- a/layers/tests/e2e/layerPublisher.test.ts +++ b/layers/tests/e2e/layerPublisher.test.ts @@ -7,12 +7,12 @@ import { App } from 'aws-cdk-lib'; import { LayerVersion } from 'aws-cdk-lib/aws-lambda'; import { LayerPublisherStack } from '../../src/layer-publisher-stack'; import { - TestNodejsFunction, TestStack, TestInvocationLogs, invokeFunctionOnce, generateTestUniqueName, } from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, diff --git a/lerna.json b/lerna.json index 840f5d9e4f..78a7368c03 100644 --- a/lerna.json +++ b/lerna.json @@ -10,9 +10,10 @@ "packages/testing", "examples/cdk", "examples/sam", - "layers" + "layers", + "docs/snippets" ], "version": "1.18.1", "npmClient": "npm", "message": "chore(release): %s [skip ci]" -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9e1782f55f..d75014c63c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,6 @@ "lerna": "^8.1.2", "lint-staged": "^15.2.2", "prettier": "^3.2.5", - "rimraf": "^5.0.5", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "typedoc": "^0.25.8", @@ -54,7 +53,7 @@ }, "docs/snippets": { "name": "docs", - "version": "1.9.0", + "version": "1.14.0", "license": "MIT-0", "devDependencies": { "@aws-sdk/client-appconfigdata": "^3.515.0", @@ -71,7 +70,7 @@ }, "examples/cdk": { "name": "cdk-sample", - "version": "1.18.1", + "version": "1.18.0", "license": "MIT-0", "dependencies": { "@middy/core": "^4.7.0", @@ -15020,55 +15019,6 @@ "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", "dev": true }, - "node_modules/rimraf": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", - "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", - "dev": true, - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -17184,12 +17134,18 @@ "packages/batch": { "name": "@aws-lambda-powertools/batch", "version": "1.18.1", - "license": "MIT-0" + "license": "MIT-0", + "devDependencies": { + "@aws-lambda-powertools/testing-utils": "file:../testing" + } }, "packages/commons": { "name": "@aws-lambda-powertools/commons", "version": "1.18.1", - "license": "MIT-0" + "license": "MIT-0", + "devDependencies": { + "@aws-lambda-powertools/testing-utils": "file:../testing" + } }, "packages/idempotency": { "name": "@aws-lambda-powertools/idempotency", @@ -17277,6 +17233,7 @@ "@aws-sdk/util-base64-node": "^3.209.0" }, "devDependencies": { + "@aws-lambda-powertools/testing-utils": "file:../testing", "@aws-sdk/client-appconfigdata": "^3.515.0", "@aws-sdk/client-dynamodb": "^3.515.0", "@aws-sdk/client-secrets-manager": "^3.515.0", diff --git a/package.json b/package.json index 783e966040..fff04e368a 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "lerna": "^8.1.2", "lint-staged": "^15.2.2", "prettier": "^3.2.5", - "rimraf": "^5.0.5", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "typedoc": "^0.25.8", diff --git a/packages/batch/jest.config.js b/packages/batch/jest.config.cjs similarity index 93% rename from packages/batch/jest.config.js rename to packages/batch/jest.config.cjs index 3db7c7a6da..9eaa88fbf4 100644 --- a/packages/batch/jest.config.js +++ b/packages/batch/jest.config.cjs @@ -5,6 +5,9 @@ module.exports = { }, runner: 'groups', preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, diff --git a/packages/batch/package.json b/packages/batch/package.json index fab6fc4a2d..88286e4f72 100644 --- a/packages/batch/package.json +++ b/packages/batch/package.json @@ -13,16 +13,16 @@ "test": "npm run test:unit", "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", "jest": "jest --detectOpenHandles --verbose", - "test:e2e:nodejs14x": "echo 'Not Implemented'", "test:e2e:nodejs16x": "echo 'Not Implemented'", "test:e2e:nodejs18x": "echo 'Not Implemented'", "test:e2e:nodejs20x": "echo 'Not Implemented'", "test:e2e": "echo 'Not Implemented'", "watch": "jest --watch", - "build": "tsc --build --force", + "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", - "prebuild": "rimraf ./lib", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { @@ -30,8 +30,33 @@ }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/batch#readme", "license": "MIT-0", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } + }, + "./types": { + "import": "./lib/esm/types.js", + "require": "./lib/cjs/types.js" + } + }, + "typesVersions": { + "*": { + "types": [ + "lib/cjs/types.d.ts", + "lib/esm/types.d.ts" + ] + } + }, + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "files": [ "lib" ], @@ -50,5 +75,8 @@ "batch-processing", "serverless", "nodejs" - ] -} + ], + "devDependencies": { + "@aws-lambda-powertools/testing-utils": "file:../testing" + } +} \ No newline at end of file diff --git a/packages/batch/src/BasePartialBatchProcessor.ts b/packages/batch/src/BasePartialBatchProcessor.ts index 3cf4b30309..10b04b3e4e 100644 --- a/packages/batch/src/BasePartialBatchProcessor.ts +++ b/packages/batch/src/BasePartialBatchProcessor.ts @@ -3,14 +3,18 @@ import type { KinesisStreamRecord, SQSRecord, } from 'aws-lambda'; -import { BasePartialProcessor } from './BasePartialProcessor'; -import { DATA_CLASS_MAPPING, DEFAULT_RESPONSE, EventType } from './constants'; -import { FullBatchFailureError } from './errors'; +import { BasePartialProcessor } from './BasePartialProcessor.js'; +import { + DATA_CLASS_MAPPING, + DEFAULT_RESPONSE, + EventType, +} from './constants.js'; +import { FullBatchFailureError } from './errors.js'; import type { EventSourceDataClassTypes, PartialItemFailureResponse, PartialItemFailures, -} from './types'; +} from './types.js'; /** * Process batch and partially report failed items diff --git a/packages/batch/src/BasePartialProcessor.ts b/packages/batch/src/BasePartialProcessor.ts index 9de6901394..f4ec911446 100644 --- a/packages/batch/src/BasePartialProcessor.ts +++ b/packages/batch/src/BasePartialProcessor.ts @@ -5,7 +5,7 @@ import type { FailureResponse, ResultType, SuccessResponse, -} from './types'; +} from './types.js'; /** * Abstract class for batch processors. diff --git a/packages/batch/src/BatchProcessor.ts b/packages/batch/src/BatchProcessor.ts index bbe9856f07..960bcd4dde 100644 --- a/packages/batch/src/BatchProcessor.ts +++ b/packages/batch/src/BatchProcessor.ts @@ -1,6 +1,6 @@ -import { BasePartialBatchProcessor } from './BasePartialBatchProcessor'; -import { BatchProcessingError } from './errors'; -import type { BaseRecord, FailureResponse, SuccessResponse } from './types'; +import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +import { BatchProcessingError } from './errors.js'; +import type { BaseRecord, FailureResponse, SuccessResponse } from './types.js'; /** * Process native partial responses from SQS, Kinesis Data Streams, and DynamoDB diff --git a/packages/batch/src/BatchProcessorSync.ts b/packages/batch/src/BatchProcessorSync.ts index 5bb8ce1f12..87a1566f4c 100644 --- a/packages/batch/src/BatchProcessorSync.ts +++ b/packages/batch/src/BatchProcessorSync.ts @@ -1,6 +1,6 @@ -import { BasePartialBatchProcessor } from './BasePartialBatchProcessor'; -import { BatchProcessingError } from './errors'; -import type { BaseRecord, FailureResponse, SuccessResponse } from './types'; +import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +import { BatchProcessingError } from './errors.js'; +import type { BaseRecord, FailureResponse, SuccessResponse } from './types.js'; /** * Process native partial responses from SQS, Kinesis Data Streams, and DynamoDB diff --git a/packages/batch/src/SqsFifoPartialProcessor.ts b/packages/batch/src/SqsFifoPartialProcessor.ts index 2aca4ed814..3189c00fd5 100644 --- a/packages/batch/src/SqsFifoPartialProcessor.ts +++ b/packages/batch/src/SqsFifoPartialProcessor.ts @@ -1,7 +1,7 @@ -import { BatchProcessorSync } from './BatchProcessorSync'; -import { EventType } from './constants'; -import { SqsFifoShortCircuitError } from './errors'; -import type { FailureResponse, SuccessResponse } from './types'; +import { BatchProcessorSync } from './BatchProcessorSync.js'; +import { EventType } from './constants.js'; +import { SqsFifoShortCircuitError } from './errors.js'; +import type { FailureResponse, SuccessResponse } from './types.js'; /** * Process native partial responses from SQS FIFO queues diff --git a/packages/batch/src/constants.ts b/packages/batch/src/constants.ts index d06d5a8872..5f55e1e347 100644 --- a/packages/batch/src/constants.ts +++ b/packages/batch/src/constants.ts @@ -6,7 +6,7 @@ import type { import type { PartialItemFailureResponse, EventSourceDataClassTypes, -} from './types'; +} from './types.js'; const EventType = { SQS: 'SQS', diff --git a/packages/batch/src/errors.ts b/packages/batch/src/errors.ts index 8d2b0327b0..4ce6b27de9 100644 --- a/packages/batch/src/errors.ts +++ b/packages/batch/src/errors.ts @@ -1,4 +1,4 @@ -import { EventType } from './constants'; +import { EventType } from './constants.js'; /** * Base error thrown by the Batch Processing utility diff --git a/packages/batch/src/index.ts b/packages/batch/src/index.ts index b1b1069b26..6613712b7e 100644 --- a/packages/batch/src/index.ts +++ b/packages/batch/src/index.ts @@ -1,10 +1,13 @@ -export * from './constants'; -export * from './errors'; -export * from './types'; -export * from './BasePartialProcessor'; -export * from './BasePartialBatchProcessor'; -export * from './BatchProcessorSync'; -export * from './BatchProcessor'; -export * from './processPartialResponseSync'; -export * from './processPartialResponse'; -export * from './SqsFifoPartialProcessor'; +export { EventType } from './constants.js'; +export { + BatchProcessingError, + FullBatchFailureError, + SqsFifoShortCircuitError, + UnexpectedBatchTypeError, +} from './errors.js'; +export { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +export { BatchProcessorSync } from './BatchProcessorSync.js'; +export { BatchProcessor } from './BatchProcessor.js'; +export { processPartialResponseSync } from './processPartialResponseSync.js'; +export { processPartialResponse } from './processPartialResponse.js'; +export { SqsFifoPartialProcessor } from './SqsFifoPartialProcessor.js'; diff --git a/packages/batch/src/processPartialResponse.ts b/packages/batch/src/processPartialResponse.ts index 947b4268b6..8c5d7d79c9 100644 --- a/packages/batch/src/processPartialResponse.ts +++ b/packages/batch/src/processPartialResponse.ts @@ -1,10 +1,10 @@ -import { BasePartialBatchProcessor } from './BasePartialBatchProcessor'; -import { UnexpectedBatchTypeError } from './errors'; +import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +import { UnexpectedBatchTypeError } from './errors.js'; import type { BaseRecord, BatchProcessingOptions, PartialItemFailureResponse, -} from './types'; +} from './types.js'; /** * Higher level function to handle batch event processing diff --git a/packages/batch/src/processPartialResponseSync.ts b/packages/batch/src/processPartialResponseSync.ts index 474713e7d5..7fb7056385 100644 --- a/packages/batch/src/processPartialResponseSync.ts +++ b/packages/batch/src/processPartialResponseSync.ts @@ -1,10 +1,10 @@ -import { BasePartialBatchProcessor } from './BasePartialBatchProcessor'; -import { UnexpectedBatchTypeError } from './errors'; +import { BasePartialBatchProcessor } from './BasePartialBatchProcessor.js'; +import { UnexpectedBatchTypeError } from './errors.js'; import type { BaseRecord, BatchProcessingOptions, PartialItemFailureResponse, -} from './types'; +} from './types.js'; /** * Higher level function to handle batch event processing diff --git a/packages/batch/tests/helpers/handlers.ts b/packages/batch/tests/helpers/handlers.ts index 0256129f9b..d8f9a638a0 100644 --- a/packages/batch/tests/helpers/handlers.ts +++ b/packages/batch/tests/helpers/handlers.ts @@ -2,8 +2,8 @@ import type { DynamoDBRecord, KinesisStreamRecord, SQSRecord, + Context, } from 'aws-lambda'; -import type { Context } from 'aws-lambda'; const sqsRecordHandler = (record: SQSRecord): string => { const body = record.body; diff --git a/packages/batch/tests/unit/BasePartialProcessor.test.ts b/packages/batch/tests/unit/BasePartialProcessor.test.ts new file mode 100644 index 0000000000..aa3bdb3d4b --- /dev/null +++ b/packages/batch/tests/unit/BasePartialProcessor.test.ts @@ -0,0 +1,74 @@ +/** + * Test BasePartialBatchProcessor class + * + * @group unit/batch/class/basepartialbatchprocessor + */ +import { BasePartialBatchProcessor, EventType } from '../../src/index.js'; +import type { + BaseRecord, + FailureResponse, + SuccessResponse, +} from '../../src/types.js'; +import { sqsRecordFactory } from '../helpers/factories.js'; +import { sqsRecordHandler } from '../helpers/handlers.js'; + +describe('Class: BasePartialBatchProcessor', () => { + const ENVIRONMENT_VARIABLES = process.env; + + beforeEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + process.env = { ...ENVIRONMENT_VARIABLES }; + }); + + afterAll(() => { + process.env = ENVIRONMENT_VARIABLES; + }); + + class MyPartialProcessor extends BasePartialBatchProcessor { + public constructor() { + super(EventType.SQS); + } + + public async processRecord( + _record: BaseRecord + ): Promise { + throw new Error('Not implemented'); + } + + public processRecordSync( + record: BaseRecord + ): SuccessResponse | FailureResponse { + console.log('Processing record'); + + return this.successHandler(record, 'success'); + } + } + + describe('create custom batch partial processor', () => { + it('should create a custom batch partial processor', () => { + // Act + const processor = new MyPartialProcessor(); + + // Assess + expect(processor).toBeInstanceOf(BasePartialBatchProcessor); + }); + + it('should process a batch of records', () => { + // Prepare + const processor = new MyPartialProcessor(); + const records = [sqsRecordFactory('success')]; + const consoleSpy = jest.spyOn(console, 'log'); + + // Act + processor.register(records, sqsRecordHandler); + const processedMessages = processor.processSync(); + + // Assess + expect(processedMessages).toStrictEqual([ + ['success', records[0].body, records[0]], + ]); + expect(consoleSpy).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/packages/batch/tests/unit/BatchProcessor.test.ts b/packages/batch/tests/unit/BatchProcessor.test.ts index 99cb2245e5..1b1114ca86 100644 --- a/packages/batch/tests/unit/BatchProcessor.test.ts +++ b/packages/batch/tests/unit/BatchProcessor.test.ts @@ -1,29 +1,34 @@ /** - * Test AsyncBatchProcessor class + * Test BatchProcessor class * - * @group unit/batch/class/asyncBatchProcessor + * @group unit/batch/class/batchprocessor */ import type { Context } from 'aws-lambda'; -import { helloworldContext as dummyContext } from '@aws-lambda-powertools/commons/lib/samples/resources/contexts'; -import { BatchProcessor } from '../../src/BatchProcessor'; -import { EventType } from '../../src/constants'; -import { BatchProcessingError, FullBatchFailureError } from '../../src/errors'; -import type { BatchProcessingOptions } from '../../src/types'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import { + BatchProcessor, + EventType, + BatchProcessingError, + FullBatchFailureError, +} from '../../src/index.js'; +import type { BatchProcessingOptions } from '../../src/types.js'; import { dynamodbRecordFactory, kinesisRecordFactory, sqsRecordFactory, -} from '../helpers/factories'; +} from '../helpers/factories.js'; import { asyncDynamodbRecordHandler, asyncKinesisRecordHandler, asyncSqsRecordHandler, asyncHandlerWithContext, -} from '../helpers/handlers'; +} from '../helpers/handlers.js'; describe('Class: AsyncBatchProcessor', () => { const ENVIRONMENT_VARIABLES = process.env; - const options: BatchProcessingOptions = { context: dummyContext }; + const options: BatchProcessingOptions = { + context, + }; beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/batch/tests/unit/BatchProcessorSync.test.ts b/packages/batch/tests/unit/BatchProcessorSync.test.ts index 4f77d299cc..bf93f590a0 100644 --- a/packages/batch/tests/unit/BatchProcessorSync.test.ts +++ b/packages/batch/tests/unit/BatchProcessorSync.test.ts @@ -1,29 +1,34 @@ /** - * Test BatchProcessor class + * Test BatchProcessorSync class * - * @group unit/batch/class/batchprocessor + * @group unit/batch/class/batchprocessorsync */ import type { Context } from 'aws-lambda'; -import { helloworldContext as dummyContext } from '@aws-lambda-powertools/commons/lib/samples/resources/contexts'; -import { BatchProcessorSync } from '../../src/BatchProcessorSync'; -import { EventType } from '../../src/constants'; -import { BatchProcessingError, FullBatchFailureError } from '../../src/errors'; -import type { BatchProcessingOptions } from '../../src/types'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import { + BatchProcessorSync, + EventType, + BatchProcessingError, + FullBatchFailureError, +} from '../../src/index.js'; +import type { BatchProcessingOptions } from '../../src/types.js'; import { dynamodbRecordFactory, kinesisRecordFactory, sqsRecordFactory, -} from '../helpers/factories'; +} from '../helpers/factories.js'; import { dynamodbRecordHandler, handlerWithContext, kinesisRecordHandler, sqsRecordHandler, -} from '../helpers/handlers'; +} from '../helpers/handlers.js'; describe('Class: BatchProcessor', () => { const ENVIRONMENT_VARIABLES = process.env; - const options: BatchProcessingOptions = { context: dummyContext }; + const options: BatchProcessingOptions = { + context, + }; beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/batch/tests/unit/SqsFifoPartialProcessor.test.ts b/packages/batch/tests/unit/SqsFifoPartialProcessor.test.ts index 5c7f6c5796..61d12183fc 100644 --- a/packages/batch/tests/unit/SqsFifoPartialProcessor.test.ts +++ b/packages/batch/tests/unit/SqsFifoPartialProcessor.test.ts @@ -7,9 +7,9 @@ import { SqsFifoPartialProcessor, processPartialResponseSync, SqsFifoShortCircuitError, -} from '../../src'; -import { sqsRecordFactory } from '../helpers/factories'; -import { sqsRecordHandler } from '../helpers/handlers'; +} from '../../src/index.js'; +import { sqsRecordFactory } from '../helpers/factories.js'; +import { sqsRecordHandler } from '../helpers/handlers.js'; describe('Class: SqsFifoBatchProcessor', () => { const ENVIRONMENT_VARIABLES = process.env; diff --git a/packages/batch/tests/unit/processPartialResponse.test.ts b/packages/batch/tests/unit/processPartialResponse.test.ts index 2dbe585b45..732d40ee3b 100644 --- a/packages/batch/tests/unit/processPartialResponse.test.ts +++ b/packages/batch/tests/unit/processPartialResponse.test.ts @@ -9,31 +9,35 @@ import type { KinesisStreamEvent, SQSEvent, } from 'aws-lambda'; -import { helloworldContext as dummyContext } from '@aws-lambda-powertools/commons/lib/samples/resources/contexts'; -import { Custom as dummyEvent } from '@aws-lambda-powertools/commons/lib/samples/resources/events'; -import { BatchProcessor } from '../../src/BatchProcessor'; -import { processPartialResponse } from '../../src/processPartialResponse'; -import { EventType } from '../../src/constants'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import { + BatchProcessor, + processPartialResponse, + EventType, + UnexpectedBatchTypeError, +} from '../../src/index.js'; import type { BatchProcessingOptions, PartialItemFailureResponse, -} from '../../src/types'; +} from '../../src/types.js'; import { dynamodbRecordFactory, kinesisRecordFactory, sqsRecordFactory, -} from '../helpers/factories'; +} from '../helpers/factories.js'; import { asyncDynamodbRecordHandler, asyncHandlerWithContext, asyncKinesisRecordHandler, asyncSqsRecordHandler, -} from '../helpers/handlers'; +} from '../helpers/handlers.js'; +import assert from 'node:assert'; describe('Function: processPartialResponse()', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext; - const options: BatchProcessingOptions = { context: dummyContext }; + const options: BatchProcessingOptions = { + context, + }; beforeEach(() => { jest.clearAllMocks(); @@ -169,7 +173,6 @@ describe('Function: processPartialResponse()', () => { test('Process partial response through handler for SQS records with incorrect event type', async () => { // Prepare const processor = new BatchProcessor(EventType.SQS); - const event = dummyEvent; const handler = async ( event: SQSEvent, @@ -182,14 +185,18 @@ describe('Function: processPartialResponse()', () => { ); }; - // Act & Assess - await expect(() => - handler(event as unknown as SQSEvent, context) - ).rejects.toThrowError( - `Unexpected batch type. Possible values are: ${Object.keys( - EventType - ).join(', ')}` - ); + try { + // Act + await handler({} as unknown as SQSEvent, context); + } catch (error) { + // Assess + assert(error instanceof UnexpectedBatchTypeError); + expect(error.message).toBe( + `Unexpected batch type. Possible values are: ${Object.keys( + EventType + ).join(', ')}` + ); + } }); test('Process partial response through handler with context provided', async () => { diff --git a/packages/batch/tests/unit/processPartialResponseSync.test.ts b/packages/batch/tests/unit/processPartialResponseSync.test.ts index 4c3e9a47a5..4fe7da8609 100644 --- a/packages/batch/tests/unit/processPartialResponseSync.test.ts +++ b/packages/batch/tests/unit/processPartialResponseSync.test.ts @@ -9,31 +9,35 @@ import type { KinesisStreamEvent, SQSEvent, } from 'aws-lambda'; -import { helloworldContext as dummyContext } from '@aws-lambda-powertools/commons/lib/samples/resources/contexts'; -import { Custom as dummyEvent } from '@aws-lambda-powertools/commons/lib/samples/resources/events'; -import { BatchProcessorSync } from '../../src/BatchProcessorSync'; -import { processPartialResponseSync } from '../../src/processPartialResponseSync'; -import { EventType } from '../../src/constants'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import { + BatchProcessorSync, + processPartialResponseSync, + EventType, + UnexpectedBatchTypeError, +} from '../../src/index.js'; import type { BatchProcessingOptions, PartialItemFailureResponse, -} from '../../src/types'; +} from '../../src/types.js'; import { dynamodbRecordFactory, kinesisRecordFactory, sqsRecordFactory, -} from '../helpers/factories'; +} from '../helpers/factories.js'; import { dynamodbRecordHandler, handlerWithContext, kinesisRecordHandler, sqsRecordHandler, -} from '../helpers/handlers'; +} from '../helpers/handlers.js'; +import assert from 'node:assert'; describe('Function: processPartialResponse()', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext; - const options: BatchProcessingOptions = { context: dummyContext }; + const options: BatchProcessingOptions = { + context, + }; beforeEach(() => { jest.clearAllMocks(); @@ -169,7 +173,6 @@ describe('Function: processPartialResponse()', () => { test('Process partial response through handler for SQS records with incorrect event type', () => { // Prepare const processor = new BatchProcessorSync(EventType.SQS); - const event = dummyEvent; const handler = ( event: SQSEvent, @@ -178,12 +181,18 @@ describe('Function: processPartialResponse()', () => { return processPartialResponseSync(event, sqsRecordHandler, processor); }; - // Act & Assess - expect(() => handler(event as unknown as SQSEvent, context)).toThrowError( - `Unexpected batch type. Possible values are: ${Object.keys( - EventType - ).join(', ')}` - ); + try { + // Act + handler({} as unknown as SQSEvent, context); + } catch (error) { + // Assess + assert(error instanceof UnexpectedBatchTypeError); + expect(error.message).toBe( + `Unexpected batch type. Possible values are: ${Object.keys( + EventType + ).join(', ')}` + ); + } }); test('Process partial response through handler with context provided', () => { diff --git a/packages/batch/tsconfig.esm.json b/packages/batch/tsconfig.esm.json new file mode 100644 index 0000000000..123291b0cf --- /dev/null +++ b/packages/batch/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/esm.json" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/batch/tsconfig.json b/packages/batch/tsconfig.json index 1cb9d72773..4836a14962 100644 --- a/packages/batch/tsconfig.json +++ b/packages/batch/tsconfig.json @@ -1,8 +1,9 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./lib", + "outDir": "./lib/cjs/", "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" }, "include": [ "./src/**/*" diff --git a/packages/commons/jest.config.js b/packages/commons/jest.config.cjs similarity index 83% rename from packages/commons/jest.config.js rename to packages/commons/jest.config.cjs index 512b4ab601..957bbe96d7 100644 --- a/packages/commons/jest.config.js +++ b/packages/commons/jest.config.cjs @@ -4,6 +4,9 @@ module.exports = { color: 'red', }, preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, @@ -13,7 +16,7 @@ module.exports = { roots: ['/src', '/tests'], testPathIgnorePatterns: ['/node_modules/'], testEnvironment: 'node', - coveragePathIgnorePatterns: ['/node_modules/'], + coveragePathIgnorePatterns: ['/node_modules/', 'src/types/index.ts'], coverageThreshold: { global: { statements: 100, diff --git a/packages/commons/package.json b/packages/commons/package.json index 7bfd391569..ff4f7ef7e3 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -16,19 +16,45 @@ "test:e2e": "echo 'Not Applicable'", "watch": "jest --watch", "generateVersionFile": "echo \"// this file is auto generated, do not modify\nexport const PT_VERSION = '$(jq -r '.version' package.json)';\" > src/version.ts", - "build": "tsc --build --force", + "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", - "prebuild": "rimraf ./lib", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { "*.{js,ts}": "npm run lint-fix" }, - "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/metrics#readme", + "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/commons#readme", "license": "MIT-0", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } + }, + "./types": { + "import": "./lib/esm/types/index.js", + "require": "./lib/cjs/types/index.js" + } + }, + "typesVersions": { + "*": { + "types": [ + "lib/cjs/types/index.d.ts", + "lib/esm/types/index.d.ts" + ] + } + }, + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "files": [ "lib" ], @@ -45,5 +71,8 @@ "powertools", "serverless", "nodejs" - ] -} + ], + "devDependencies": { + "@aws-lambda-powertools/testing-utils": "file:../testing" + } +} \ No newline at end of file diff --git a/packages/commons/src/awsSdk/index.ts b/packages/commons/src/awsSdk/index.ts deleted file mode 100644 index 6a771c2265..0000000000 --- a/packages/commons/src/awsSdk/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { addUserAgentMiddleware } from './userAgentMiddleware'; -export { isSdkClient } from './utils'; diff --git a/packages/commons/src/awsSdk/utils.ts b/packages/commons/src/awsSdk/utils.ts deleted file mode 100644 index 611525abe1..0000000000 --- a/packages/commons/src/awsSdk/utils.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { SdkClient } from '../types/awsSdk'; - -/** - * @internal - * Type guard to check if the client provided is a valid AWS SDK v3 client - */ -const isSdkClient = (client: unknown): client is SdkClient => - typeof client === 'object' && - client !== null && - 'send' in client && - typeof client.send === 'function' && - 'config' in client && - client.config !== undefined && - typeof client.config === 'object' && - client.config !== null && - 'middlewareStack' in client && - client.middlewareStack !== undefined && - typeof client.middlewareStack === 'object' && - client.middlewareStack !== null && - 'identify' in client.middlewareStack && - typeof client.middlewareStack.identify === 'function' && - 'addRelativeTo' in client.middlewareStack && - typeof client.middlewareStack.addRelativeTo === 'function'; - -export { isSdkClient }; diff --git a/packages/commons/src/awsSdk/userAgentMiddleware.ts b/packages/commons/src/awsSdkUtils.ts similarity index 63% rename from packages/commons/src/awsSdk/userAgentMiddleware.ts rename to packages/commons/src/awsSdkUtils.ts index fe50e12571..29095c3d37 100644 --- a/packages/commons/src/awsSdk/userAgentMiddleware.ts +++ b/packages/commons/src/awsSdkUtils.ts @@ -1,6 +1,5 @@ -import { PT_VERSION } from '../version'; -import { isSdkClient } from './utils'; -import type { MiddlewareArgsLike } from '../types/awsSdk'; +import { PT_VERSION } from './version.js'; +import type { MiddlewareArgsLike, SdkClient } from './types/awsSdk.js'; /** * @internal @@ -13,6 +12,28 @@ const middlewareOptions = { tags: ['POWERTOOLS', 'USER_AGENT'], }; +/** + * @internal + * Type guard to check if the client provided is a valid AWS SDK v3 client + */ +const isSdkClient = (client: unknown): client is SdkClient => + typeof client === 'object' && + client !== null && + 'send' in client && + typeof client.send === 'function' && + 'config' in client && + client.config !== undefined && + typeof client.config === 'object' && + client.config !== null && + 'middlewareStack' in client && + client.middlewareStack !== undefined && + typeof client.middlewareStack === 'object' && + client.middlewareStack !== null && + 'identify' in client.middlewareStack && + typeof client.middlewareStack.identify === 'function' && + 'addRelativeTo' in client.middlewareStack && + typeof client.middlewareStack.addRelativeTo === 'function'; + /** * @internal * returns a middleware function for the MiddlewareStack, that can be used for the SDK clients @@ -64,4 +85,4 @@ const addUserAgentMiddleware = (client: unknown, feature: string): void => { } }; -export { customUserAgentMiddleware, addUserAgentMiddleware }; +export { customUserAgentMiddleware, addUserAgentMiddleware, isSdkClient }; diff --git a/packages/commons/src/config/EnvironmentVariablesService.ts b/packages/commons/src/config/EnvironmentVariablesService.ts index f222fccfb8..599f4c88d1 100644 --- a/packages/commons/src/config/EnvironmentVariablesService.ts +++ b/packages/commons/src/config/EnvironmentVariablesService.ts @@ -1,4 +1,4 @@ -import { ConfigService } from './ConfigService'; +import type { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; /** * Class EnvironmentVariablesService @@ -13,7 +13,7 @@ import { ConfigService } from './ConfigService'; * @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime * @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables */ -class EnvironmentVariablesService implements ConfigService { +class EnvironmentVariablesService implements ConfigServiceInterface { /** * @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables * @protected diff --git a/packages/commons/src/config/index.ts b/packages/commons/src/config/index.ts deleted file mode 100644 index 1530e49ebf..0000000000 --- a/packages/commons/src/config/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ConfigService'; -export * from './EnvironmentVariablesService'; diff --git a/packages/commons/src/types/utils.ts b/packages/commons/src/guards.ts similarity index 81% rename from packages/commons/src/types/utils.ts rename to packages/commons/src/guards.ts index 7da5fd17ab..7776280e8b 100644 --- a/packages/commons/src/types/utils.ts +++ b/packages/commons/src/guards.ts @@ -50,10 +50,3 @@ const isString = (value: unknown): value is string => { }; export { isRecord, isString, isTruthy, isNullOrUndefined }; - -type JSONPrimitive = string | number | boolean | null | undefined; -type JSONValue = JSONPrimitive | JSONObject | JSONArray; -type JSONObject = { [key: number | string]: JSONValue }; -type JSONArray = Array; - -export type { JSONPrimitive, JSONValue, JSONObject, JSONArray }; diff --git a/packages/commons/src/index.ts b/packages/commons/src/index.ts index 0549592bf2..cdf0815bad 100644 --- a/packages/commons/src/index.ts +++ b/packages/commons/src/index.ts @@ -1,8 +1,12 @@ -export * from './utils/lambda'; -export * from './Utility'; -export * from './config'; -export * as ContextExamples from './samples/resources/contexts'; -export * as Events from './samples/resources/events'; -export * from './types/middy'; -export * from './types/utils'; -export * from './awsSdk'; +export { isRecord, isString, isTruthy, isNullOrUndefined } from './guards.js'; +export { Utility } from './Utility.js'; +export { EnvironmentVariablesService } from './config/EnvironmentVariablesService.js'; +export { addUserAgentMiddleware, isSdkClient } from './awsSdkUtils.js'; +export { cleanupMiddlewares } from './middleware/cleanupMiddlewares.js'; +export { + TRACER_KEY, + LOGGER_KEY, + METRICS_KEY, + IDEMPOTENCY_KEY, +} from './middleware/constants.js'; +export { PT_VERSION } from './version.js'; diff --git a/packages/commons/src/middleware/cleanupMiddlewares.ts b/packages/commons/src/middleware/cleanupMiddlewares.ts index e35719b9d7..4f1046e6f1 100644 --- a/packages/commons/src/middleware/cleanupMiddlewares.ts +++ b/packages/commons/src/middleware/cleanupMiddlewares.ts @@ -3,8 +3,8 @@ import { METRICS_KEY, LOGGER_KEY, IDEMPOTENCY_KEY, -} from './constants'; -import type { MiddyLikeRequest, CleanupFunction } from '../types/middy'; +} from './constants.js'; +import type { MiddyLikeRequest, CleanupFunction } from '../types/middy.js'; // Typeguard to assert that an object is of Function type const isFunction = (obj: unknown): obj is CleanupFunction => { diff --git a/packages/commons/src/middleware/index.ts b/packages/commons/src/middleware/index.ts deleted file mode 100644 index 85f7388af3..0000000000 --- a/packages/commons/src/middleware/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './cleanupMiddlewares'; -export * from './constants'; diff --git a/packages/commons/src/samples/resources/contexts/index.ts b/packages/commons/src/samples/resources/contexts/index.ts deleted file mode 100644 index 436927eee2..0000000000 --- a/packages/commons/src/samples/resources/contexts/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './hello-world'; diff --git a/packages/commons/src/samples/resources/events/aws/apigateway-authorizer.json b/packages/commons/src/samples/resources/events/aws/apigateway-authorizer.json deleted file mode 100644 index 2bbf060353..0000000000 --- a/packages/commons/src/samples/resources/events/aws/apigateway-authorizer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "TOKEN", - "authorizationToken": "incoming-client-token", - "methodArn": "arn:aws:execute-api:eu-central-1:123456789012:example/prod/POST/{proxy+}" -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/apigateway-aws-proxy.json b/packages/commons/src/samples/resources/events/aws/apigateway-aws-proxy.json deleted file mode 100644 index fb46b342eb..0000000000 --- a/packages/commons/src/samples/resources/events/aws/apigateway-aws-proxy.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "body": "eyJ0ZXN0IjoiYm9keSJ9", - "resource": "/{proxy+}", - "path": "/path/to/resource", - "httpMethod": "POST", - "isBase64Encoded": true, - "queryStringParameters": { - "foo": "bar" - }, - "multiValueQueryStringParameters": { - "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.eu-central-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" - }, - "multiValueHeaders": { - "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": [ - "0123456789.execute-api.eu-central-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" - } -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/appsync-resolver.json b/packages/commons/src/samples/resources/events/aws/appsync-resolver.json deleted file mode 100644 index 94960dd1ed..0000000000 --- a/packages/commons/src/samples/resources/events/aws/appsync-resolver.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "arguments": { - "id": "my identifier" - }, - "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": {} -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/batch-get-job.json b/packages/commons/src/samples/resources/events/aws/batch-get-job.json deleted file mode 100644 index cf95238fc4..0000000000 --- a/packages/commons/src/samples/resources/events/aws/batch-get-job.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "jobName": "hello_world", - "jobQueue": "default", - "jobDefinition": "hello_world" -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/batch-submit-job.json b/packages/commons/src/samples/resources/events/aws/batch-submit-job.json deleted file mode 100644 index cf95238fc4..0000000000 --- a/packages/commons/src/samples/resources/events/aws/batch-submit-job.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "jobName": "hello_world", - "jobQueue": "default", - "jobDefinition": "hello_world" -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudformation-create-request.json b/packages/commons/src/samples/resources/events/aws/cloudformation-create-request.json deleted file mode 100644 index 461752bb16..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudformation-create-request.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "RequestType": "Create", - "ResponseURL": "http://pre-signed-S3-url-for-response", - "StackId": "arn:aws:cloudformation:eu-central-1:123456789012:stack/MyStack/guid", - "RequestId": "unique id for this create request", - "ResourceType": "Custom::TestResource", - "LogicalResourceId": "MyTestResource", - "ResourceProperties": { - "StackName": "MyStack", - "List": [ - "1", - "2", - "3" - ] - } -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-ab-test.json b/packages/commons/src/samples/resources/events/aws/cloudfront-ab-test.json deleted file mode 100644 index bdc6b76e2d..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-ab-test.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "user-agent": [ - { - "key": "User-Agent", - "value": "Test Agent" - } - ], - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ], - "cookie": [ - { - "key": "Cookie", - "value": "SomeCookie=1; AnotherOne=A; X-Experiment-Name=B" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-access-request-in-response.json b/packages/commons/src/samples/resources/events/aws/cloudfront-access-request-in-response.json deleted file mode 100644 index ecb1240549..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-access-request-in-response.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ], - "user-name": [ - { - "key": "User-Name", - "value": "CloudFront" - } - ] - }, - "clientIp": "2001:cdba::3257:9652", - "uri": "/test", - "method": "GET" - }, - "response": { - "status": "200", - "statusDescription": "OK", - "headers": { - "x-cache": [ - { - "key": "X-Cache", - "value": "Hello from Cloudfront" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-http-direct.json b/packages/commons/src/samples/resources/events/aws/cloudfront-http-direct.json deleted file mode 100644 index 1f13e53dd1..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-http-direct.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "user-agent": [ - { - "key": "User-Agent", - "value": "test-agent" - } - ], - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-modify-querystring.json b/packages/commons/src/samples/resources/events/aws/cloudfront-modify-querystring.json deleted file mode 100644 index 7485310e94..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-modify-querystring.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "querystring": "auth=test&foo=bar", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ], - "user-agent": [ - { - "key": "User-Agent", - "value": "Test Agent" - } - ], - "user-name": [ - { - "key": "User-Name", - "value": "aws-cloudfront" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-modify-response-header.json b/packages/commons/src/samples/resources/events/aws/cloudfront-modify-response-header.json deleted file mode 100644 index 8ac2b4bc0f..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-modify-response-header.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "response": { - "status": "200", - "statusDescription": "OK", - "headers": { - "vary": [ - { - "key": "Vary", - "value": "*" - } - ], - "last-modified": [ - { - "key": "Last-Modified", - "value": "2016-11-25" - } - ], - "x-amz-meta-last-modified": [ - { - "key": "X-Amz-Meta-Last-Modified", - "value": "2016-01-01" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-multiple-remote-calls-aggregate-response.json b/packages/commons/src/samples/resources/events/aws/cloudfront-multiple-remote-calls-aggregate-response.json deleted file mode 100644 index 1360629d73..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-multiple-remote-calls-aggregate-response.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/forecast/Seattle:NewYork:Chicago:SanFrancisco", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ], - "user-agent": [ - { - "key": "User-Agent", - "value": "Test Agent" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-normalize-querystring-to-improve-cache-hit.json b/packages/commons/src/samples/resources/events/aws/cloudfront-normalize-querystring-to-improve-cache-hit.json deleted file mode 100644 index 21100c997a..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-normalize-querystring-to-improve-cache-hit.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "querystring": "size=LARGE&color=RED", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ], - "user-agent": [ - { - "key": "User-Agent", - "value": "Test Agent" - } - ], - "user-name": [ - { - "key": "User-Name", - "value": "aws-cloudfront" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-redirect-on-viewer-country.json b/packages/commons/src/samples/resources/events/aws/cloudfront-redirect-on-viewer-country.json deleted file mode 100644 index 2d24b0cb43..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-redirect-on-viewer-country.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ], - "cloudfront-viewer-country": [ - { - "key": "CloudFront-Viewer-Country", - "value": "TW" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-redirect-unauthenticated-users.json b/packages/commons/src/samples/resources/events/aws/cloudfront-redirect-unauthenticated-users.json deleted file mode 100644 index 4d064cfd0c..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-redirect-unauthenticated-users.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "method": "GET", - "querystring": "foo=bar", - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-response-generation.json b/packages/commons/src/samples/resources/events/aws/cloudfront-response-generation.json deleted file mode 100644 index 2bfde0f0d6..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-response-generation.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-serve-object-on-viewer-device.json b/packages/commons/src/samples/resources/events/aws/cloudfront-serve-object-on-viewer-device.json deleted file mode 100644 index f1f50a8980..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-serve-object-on-viewer-device.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ], - "cloudfront-is-desktop-viewer": [ - { - "key": "CloudFront-Is-Desktop-Viewer", - "value": "true" - } - ], - "cloudfront-is-mobile-viewer": [ - { - "key": "CloudFront-Is-Mobile-Viewer", - "value": "false" - } - ], - "cloudfront-is-smarttv-viewer": [ - { - "key": "CloudFront-Is-SmartTV-Viewer", - "value": "false" - } - ], - "cloudfront-is-tablet-viewer": [ - { - "key": "CloudFront-Is-Tablet-Viewer", - "value": "false" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudfront-simple-remote-call.json b/packages/commons/src/samples/resources/events/aws/cloudfront-simple-remote-call.json deleted file mode 100644 index bf4625d06d..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudfront-simple-remote-call.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "Records": [ - { - "cf": { - "config": { - "distributionId": "EXAMPLE" - }, - "request": { - "uri": "/test", - "method": "GET", - "clientIp": "2001:cdba::3257:9652", - "headers": { - "host": [ - { - "key": "Host", - "value": "d123.cf.net" - } - ], - "user-agent": [ - { - "key": "User-Agent", - "value": "Test Agent" - } - ], - "user-name": [ - { - "key": "User-Name", - "value": "aws-cloudfront" - } - ] - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudwatch-logs.json b/packages/commons/src/samples/resources/events/aws/cloudwatch-logs.json deleted file mode 100644 index 2b455b9bc4..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudwatch-logs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "awslogs": { - "data": "H4sIAAAAAAAAAHWPwQqCQBCGX0Xm7EFtK+smZBEUgXoLCdMhFtKV3akI8d0bLYmibvPPN3wz00CJxmQnTO41whwWQRIctmEcB6sQbFC3CjW3XW8kxpOpP+OC22d1Wml1qZkQGtoMsScxaczKN3plG8zlaHIta5KqWsozoTYw3/djzwhpLwivWFGHGpAFe7DL68JlBUk+l7KSN7tCOEJ4M3/qOI49vMHj+zCKdlFqLaU2ZHV2a4Ct/an0/ivdX8oYc1UVX860fQDQiMdxRQEAAA==" - } -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cloudwatch-scheduled-event.json b/packages/commons/src/samples/resources/events/aws/cloudwatch-scheduled-event.json deleted file mode 100644 index dfa0a04b5a..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cloudwatch-scheduled-event.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c", - "detail-type": "Scheduled Event", - "source": "aws.events", - "account": "123456789012", - "time": "1970-01-01T00:00:00Z", - "region": "eu-central-1", - "resources": [ - "arn:aws:events:eu-central-1:123456789012:rule/ExampleRule" - ], - "detail": {} -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/codecommit-repository.json b/packages/commons/src/samples/resources/events/aws/codecommit-repository.json deleted file mode 100644 index 227cac73bf..0000000000 --- a/packages/commons/src/samples/resources/events/aws/codecommit-repository.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "Records": [ - { - "awsRegion": "eu-central-1", - "codecommit": { - "references": [ - { - "commit": "5c4ef1049f1d27deadbeeff313e0730018be182b", - "ref": "refs/heads/master" - } - ] - }, - "customData": "this is custom data", - "eventId": "5a824061-17ca-46a9-bbf9-114edeadbeef", - "eventName": "TriggerEventTest", - "eventPartNumber": 1, - "eventSource": "aws:codecommit", - "eventSourceARN": "arn:aws:codecommit:eu-central-1:123456789012:my-repo", - "eventTime": "2016-01-01T23:59:59.000+0000", - "eventTotalParts": 1, - "eventTriggerConfigId": "5a824061-17ca-46a9-bbf9-114edeadbeef", - "eventTriggerName": "my-trigger", - "eventVersion": "1.0", - "userIdentityARN": "arn:aws:iam::123456789012:root" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/codepipeline-job.json b/packages/commons/src/samples/resources/events/aws/codepipeline-job.json deleted file mode 100644 index 1ebc6ccb9e..0000000000 --- a/packages/commons/src/samples/resources/events/aws/codepipeline-job.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "CodePipeline.job": { - "data": { - "artifactCredentials": { - "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", - "sessionToken": "token", - "accessKeyId": "AKIAIOSFODNN7EXAMPLE" - }, - "actionConfiguration": { - "configuration": { - "FunctionName": "my-function", - "UserParameters": "user-parameter-string" - } - }, - "inputArtifacts": [ - { - "revision": "ca2bdeadbeef7d1932acb4977e08c803295d9896", - "name": "input-artifact", - "location": { - "type": "S3", - "s3Location": { - "objectKey": "test/key", - "bucketName": "example-bucket" - } - } - } - ], - "outputArtifacts": [ - { - "revision": null, - "name": "output-artifact", - "location": { - "type": "S3", - "s3Location": { - "objectKey": "test/key2", - "bucketName": "example-bucket2" - } - } - } - ] - }, - "id": "c968ef10-6415-4127-80b1-42502218a8c7", - "accountId": "123456789012" - } -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/cognito-sync-trigger.json b/packages/commons/src/samples/resources/events/aws/cognito-sync-trigger.json deleted file mode 100644 index e9c7a0ddd0..0000000000 --- a/packages/commons/src/samples/resources/events/aws/cognito-sync-trigger.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": 2, - "eventType": "SyncTrigger", - "region": "eu-central-1", - "identityPoolId": "identityPoolId", - "identityId": "identityId", - "datasetName": "datasetName", - "datasetRecords": { - "SampleKey1": { - "oldValue": "oldValue1", - "newValue": "newValue1", - "op": "replace" - }, - "SampleKey2": { - "oldValue": "oldValue2", - "newValue": "newValue2", - "op": "replace" - } - } -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/config-oversized-item-change-notification.json b/packages/commons/src/samples/resources/events/aws/config-oversized-item-change-notification.json deleted file mode 100644 index fb20d98bb7..0000000000 --- a/packages/commons/src/samples/resources/events/aws/config-oversized-item-change-notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "invokingEvent": "{\"configurationItemSummary\": {\"changeType\": \"UPDATE\",\"configurationItemVersion\": \"1.2\",\"configurationItemCaptureTime\":\"2016-10-06T16:46:16.261Z\",\"configurationStateId\": 0,\"awsAccountId\":\"123456789012\",\"configurationItemStatus\": \"OK\",\"resourceType\": \"AWS::EC2::Instance\",\"resourceId\":\"i-00000000\",\"resourceName\":null,\"ARN\":\"arn:aws:ec2:eu-central-1:123456789012:instance/i-00000000\",\"awsRegion\": \"eu-central-1\",\"availabilityZone\":\"eu-central-1\",\"configurationStateMd5Hash\":\"8f1ee69b287895a0f8bc5753eca68e96\",\"resourceCreationTime\":\"2016-10-06T16:46:10.489Z\"},\"messageType\":\"OversizedConfigurationItemChangeNotification\"}", - "ruleParameters": "{\"\":\"\"}", - "resultToken": "myResultToken", - "eventLeftScope": false, - "executionRoleArn": "arn:aws:iam::123456789012:role/config-role", - "configRuleArn": "arn:aws:config:eu-central-1:123456789012:config-rule/config-rule-0123456", - "configRuleName": "change-triggered-config-rule", - "configRuleId": "config-rule-0123456", - "accountId": "123456789012", - "version": "1.0" -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/config-periodic-rule.json b/packages/commons/src/samples/resources/events/aws/config-periodic-rule.json deleted file mode 100644 index 03322a8b12..0000000000 --- a/packages/commons/src/samples/resources/events/aws/config-periodic-rule.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "invokingEvent": "{\"awsAccountId\":\"123456789012\",\"notificationCreationTime\":\"1970-01-01T00:00:00.0Z\",\"messageType\":\"ScheduledNotification\",\"recordVersion\":\"1.0\"}", - "ruleParameters": "{\"myParameterKey\":\"myParameterValue\"}", - "resultToken": "myResultToken", - "eventLeftScope": false, - "executionRoleArn": "arn:aws:iam::123456789012:role/config-role", - "configRuleArn": "arn:aws:config:eu-central-1:123456789012:config-rule/config-rule-0123456", - "configRuleName": "periodic-config-rule", - "configRuleId": "config-rule-0123456", - "accountId": "123456789012", - "version": "1.0" -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/confit-item-change-notification.json b/packages/commons/src/samples/resources/events/aws/confit-item-change-notification.json deleted file mode 100644 index cf79568736..0000000000 --- a/packages/commons/src/samples/resources/events/aws/confit-item-change-notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "invokingEvent": "{\"configurationItem\":{\"configurationItemCaptureTime\":\"2016-10-06T16:46:16.261Z\",\"awsAccountId\":\"123456789012\",\"configurationItemStatus\":\"OK\",\"resourceId\":\"i-00000000\",\"resourceName\":\"foo\",\"configurationStateMd5Hash\":\"8f1ee69b297895a0f8bc5753eca68e96\",\"resourceCreationTime\":\"2016-10-06T16:46:10.489Z\",\"configurationStateId\":0,\"configurationItemVersion\":\"1.2\",\"ARN\":\"arn:aws:ec2:eu-central-1:123456789012:instance/i-00000000\",\"awsRegion\":\"eu-central-1\",\"availabilityZone\":\"eu-central-1\",\"resourceType\":\"AWS::EC2::Instance\",\"tags\":{\"\":\"\"},\"relationships\":[{\"resourceId\":\"eipalloc-00000000\",\"resourceType\":\"AWS::EC2::EIP\",\"name\":\"Is attached to ElasticIp\"}],\"configuration\":{\"\":\"\"}},\"messageType\":\"ConfigurationItemChangeNotification\"}", - "ruleParameters": "{\"\":\"\"}", - "resultToken": "myResultToken", - "eventLeftScope": false, - "executionRoleArn": "arn:aws:iam::123456789012:role/config-role", - "configRuleArn": "arn:aws:config:eu-central-1:123456789012:config-rule/config-rule-0123456", - "configRuleName": "change-triggered-config-rule", - "configRuleId": "config-rule-0123456", - "accountId": "123456789012", - "version": "1.0" -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/connect-contact-flow-event.json b/packages/commons/src/samples/resources/events/aws/connect-contact-flow-event.json deleted file mode 100644 index b192be3150..0000000000 --- a/packages/commons/src/samples/resources/events/aws/connect-contact-flow-event.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "Name": "ContactFlowEvent", - "Details": { - "ContactData": { - "Attributes": {}, - "Channel": "VOICE", - "ContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe", - "CustomerEndpoint": { - "Address": "+11234567890", - "Type": "TELEPHONE_NUMBER" - }, - "InitialContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe", - "InitiationMethod": "API", - "InstanceARN": "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa", - "MediaStreams": { - "Customer": { - "Audio": { - "StartFragmentNumber": "91343852333181432392682062622220590765191907586", - "StartTimestamp": "1565781909613", - "StreamARN": "arn:aws:kinesisvideo:eu-central-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806" - } - } - }, - "PreviousContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe", - "Queue": null, - "SystemEndpoint": { - "Address": "+11234567890", - "Type": "TELEPHONE_NUMBER" - } - }, - "Parameters": {} - } -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/dynamodb-update-2.json b/packages/commons/src/samples/resources/events/aws/dynamodb-update-2.json deleted file mode 100644 index c7f8ca2795..0000000000 --- a/packages/commons/src/samples/resources/events/aws/dynamodb-update-2.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "Records": [ - { - "eventID": "1", - "eventVersion": "1.0", - "dynamodb": { - "Keys": { - "Id": { - "N": "101" - } - }, - "NewImage": { - "Message": { - "S": "New item!" - }, - "Id": { - "N": "101" - } - }, - "StreamViewType": "NEW_AND_OLD_IMAGES", - "SequenceNumber": "111", - "SizeBytes": 26 - }, - "awsRegion": "eu-central-1", - "eventName": "INSERT", - "eventSourceARN": "arn:aws:dynamodb:eu-central-1:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", - "eventSource": "aws:dynamodb" - }, - { - "eventID": "2", - "eventVersion": "1.0", - "dynamodb": { - "OldImage": { - "Message": { - "S": "New item!" - }, - "Id": { - "N": "101" - } - }, - "SequenceNumber": "222", - "Keys": { - "Id": { - "N": "101" - } - }, - "SizeBytes": 59, - "NewImage": { - "Message": { - "S": "This item has changed" - }, - "Id": { - "N": "101" - } - }, - "StreamViewType": "NEW_AND_OLD_IMAGES" - }, - "awsRegion": "eu-central-1", - "eventName": "MODIFY", - "eventSourceARN": "arn:aws:dynamodb:eu-central-1:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", - "eventSource": "aws:dynamodb" - }, - { - "eventID": "3", - "eventVersion": "1.0", - "dynamodb": { - "Keys": { - "Id": { - "N": "101" - } - }, - "SizeBytes": 38, - "SequenceNumber": "333", - "OldImage": { - "Message": { - "S": "This item has changed" - }, - "Id": { - "N": "101" - } - }, - "StreamViewType": "NEW_AND_OLD_IMAGES" - }, - "awsRegion": "eu-central-1", - "eventName": "REMOVE", - "eventSourceARN": "arn:aws:dynamodb:eu-central-1:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", - "eventSource": "aws:dynamodb" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/dynamodb-update.json b/packages/commons/src/samples/resources/events/aws/dynamodb-update.json deleted file mode 100644 index f59d46c645..0000000000 --- a/packages/commons/src/samples/resources/events/aws/dynamodb-update.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "Records": [ - { - "eventID": "c4ca4238a0b923820dcc509a6f75849b", - "eventName": "INSERT", - "eventVersion": "1.1", - "eventSource": "aws:dynamodb", - "awsRegion": "eu-central-1", - "dynamodb": { - "Keys": { - "Id": { - "N": "101" - } - }, - "NewImage": { - "Message": { - "S": "New item!" - }, - "Id": { - "N": "101" - } - }, - "ApproximateCreationDateTime": 1428537600, - "SequenceNumber": "4421584500000000017450439091", - "SizeBytes": 26, - "StreamViewType": "NEW_AND_OLD_IMAGES" - }, - "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" - }, - { - "eventID": "c81e728d9d4c2f636f067f89cc14862c", - "eventName": "MODIFY", - "eventVersion": "1.1", - "eventSource": "aws:dynamodb", - "awsRegion": "eu-central-1", - "dynamodb": { - "Keys": { - "Id": { - "N": "101" - } - }, - "NewImage": { - "Message": { - "S": "This item has changed" - }, - "Id": { - "N": "101" - } - }, - "OldImage": { - "Message": { - "S": "New item!" - }, - "Id": { - "N": "101" - } - }, - "ApproximateCreationDateTime": 1428537600, - "SequenceNumber": "4421584500000000017450439092", - "SizeBytes": 59, - "StreamViewType": "NEW_AND_OLD_IMAGES" - }, - "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" - }, - { - "eventID": "eccbc87e4b5ce2fe28308fd9f2a7baf3", - "eventName": "REMOVE", - "eventVersion": "1.1", - "eventSource": "aws:dynamodb", - "awsRegion": "eu-central-1", - "dynamodb": { - "Keys": { - "Id": { - "N": "101" - } - }, - "OldImage": { - "Message": { - "S": "This item has changed" - }, - "Id": { - "N": "101" - } - }, - "ApproximateCreationDateTime": 1428537600, - "SequenceNumber": "4421584500000000017450439093", - "SizeBytes": 38, - "StreamViewType": "NEW_AND_OLD_IMAGES" - }, - "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-analytics-compressed.json b/packages/commons/src/samples/resources/events/aws/kinesis-analytics-compressed.json deleted file mode 100644 index b6c3a539d5..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-analytics-compressed.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "invocationId": "invocationIdExample", - "applicationArn": "arn:aws:kinesisanalytics:eu-central-1:123456789012:application/example-application", - "streamArn": "arn:aws:kinesis:eu-central-1:123456789012:stream/example-stream", - "records": [ - { - "recordId": "49571347871967966406409637155186850213682522142927749122", - "data": "H4sIAAAAAAAA/6vmUspLzE1VslLKTsxNzFHS4VJKTEkpSi0uBgol5SRmKHHVAgDd1tysJAAAAA==" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-analytics-dynamodb.json b/packages/commons/src/samples/resources/events/aws/kinesis-analytics-dynamodb.json deleted file mode 100644 index 8d45307cac..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-analytics-dynamodb.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "invocationId": "invocationIdExample", - "applicationArn": "arn:aws:kinesisanalytics:eu-central-1:123456789012:application/example-application", - "streamArn": "arn:aws:kinesis:eu-central-1:123456789012:stream/example-stream", - "records": [ - { - "recordId": "49571347871967966406409637155186850213682522142927749122", - "data": "eyJST1dUSU1FX1RJTUVTVEFNUCI6IjIwMTctMTItMTUgMDE6MDk6NTAuMDAwIiwiVkVISUNMRUlEIjoiNSIsIlZFSElDTEVDT1VOVCI6MTh9" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-analytics-kpl.json b/packages/commons/src/samples/resources/events/aws/kinesis-analytics-kpl.json deleted file mode 100644 index 807467ead1..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-analytics-kpl.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "invocationId": "invocationIdExample", - "applicationArn": "arn:aws:kinesisanalytics:eu-central-1:123456789012:application/example-application", - "streamArn": "arn:aws:kinesis:eu-central-1:123456789012:stream/example-stream", - "records": [ - { - "recordId": "49571347871967966406409637155186850213682522142927749122", - "kinesisStreamRecordMetadata": { - "shardId": "shardId-000000000001", - "partitionKey": "partitionKey-01", - "approximateArrivalTimestamp": 1505169657117, - "sequenceNumber": "49571347871967966406409637155186850213682522142927749122" - }, - "data": "84mawgoNMTUwNTE2OTY0NTI1MBIlODYwOTcwOTY1NTE0NjQwMzU1MjIwNTI5MjgyNDk2MzU3MTUzNxImNzk1MTM3MTQxMzM4MTE5NTU1OTExMzA1ODMxNDEyOTg2NTM2OTgaiAEIABAAGoEBNDAsYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEKGogBCAAQARqBATQxLGFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhCkU/elbPye5g1wGkAmxGCNg=" - }, - { - "recordId": "49571347871967966406409637141563465152445265354688561154", - "kinesisStreamRecordMetadata": { - "shardId": "shardId-000000000002", - "partitionKey": "partitionKey-02", - "approximateArrivalTimestamp": 1505169647777, - "sequenceNumber": "49571347871967966406409637141563465152445265354688561154" - }, - "data": "NCxhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEK" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-analytics.json b/packages/commons/src/samples/resources/events/aws/kinesis-analytics.json deleted file mode 100644 index 7f1db565b1..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-analytics.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "invocationId": "invocationIdExample", - "applicationArn": "arn:aws:kinesisanalytics:eu-central-1:123456789012:application/example-application", - "streamArn": "arn:aws:kinesis:eu-central-1:123456789012:stream/example-stream", - "records": [ - { - "recordId": "49571347871967966406409637155186850213682522142927749122", - "data": "VGhpcyBpcyBhIHRlc3QgZnJvbSBLaW5lc2lzIEFuYWx5dGljcw==" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-apachelog.json b/packages/commons/src/samples/resources/events/aws/kinesis-apachelog.json deleted file mode 100644 index 3f917a1613..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-apachelog.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "invocationId": "invocationIdExample", - "region": "eu-central-1", - "records": [ - { - "recordId": "49546986683135544286507457936321625675700192471156785154", - "approximateArrivalTimestamp": 1495072949453, - "data": "NjQuMjQyLjg4LjEwIC0gLSBbMDcvTWFyLzIwMDQ6MTY6MTA6MDIgLTA4MDBdICJHRVQgL21haWxtYW4vbGlzdGluZm8vaHNkaXZpc2lvbiBIVFRQLzEuMSIgMjAwIDYyOTE==" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-cloudwatch-logs-processor.json b/packages/commons/src/samples/resources/events/aws/kinesis-cloudwatch-logs-processor.json deleted file mode 100644 index c7978af868..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-cloudwatch-logs-processor.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "records": [ - { - "recordId": "49578734086442259037497492980620233840400173390482112514000000", - "data": "H4sIAAAAAAAAADWO0QqCMBiFX2XsWiJFi7wLUW8sIYUuQmLpnxvpJttMQnz3Ztrlxzmc8424BaVIDfmnA+zjID3nlzS5n8IsO8YhtrAYOMg5aURfDUSXNBG1MkEj6liKvjPZQpmWQNoFVf9QpWSdZoJHrNEgFfZvxa8XvoHrGUfMqqWumdHQpDVjtmdvHc91dwdn71p/vVngmqBVD616PgoolC/Ga0SBNJoi8USVWWKczM8oYhKoULDBUzF9Aeua5yHsAAAA", - "approximateArrivalTimestamp": 1510254469499 - }, - { - "recordId": "49578734086442259037497492980621442766219788363254202370000000", - "data": "H4sIAAAAAAAAAJWRTWsbMRCG/8ueLZjRjL5yc9NNLnZDapemlFAkrTYstb3Lep0Qgv97x00KgTSHnAQzmkeP3nmqtmW/j3dl/TiU6qz6PF/Pfy3r1Wp+WVezqn/YlVHK2pK3Hr0Jxkt5099djv1hkE7uh0eVHzZqE7epiarb3fe/ixzDYVJoELRhssYQqsXLlEJ3jd8//biy4QYWz7jVNJa4/TDveQwV+qsada0v/HnthLg/pH0eu2Hq+t1Ft5nKuK/Ofn4EvnpDUAu7Xi6/LL9en3/z1e1f7fq+7KYT+qnqGrEnsi54AGS2wbHWxjCjoWAYGawmzawByIG3Dp0JzjOxsaI8dbKJKW4l1BcTdgg+zP5tSPCeQ/Bso/I+o+I2kUptjgrRlQyasslUHWdvZRwGJ4+HYJGCtiKgQTYKSJ4gODLgAkpFk3f0rkyA1zLGSsvoVsVCRTFakUkNqKxt1IyFc8T/y0gEmoHZo5a/W9HhU0TeWHMyIJaoQC6zDvC+DL6WSW3MqZSkiolJcWoalWybJSNIJTXcRgjV8fb4BwwLrNzwAgAA", - "approximateArrivalTimestamp": 1510254473773 - }, - { - "recordId": "49578734086442259037497492980622651692039402992428908546000000", - "data": "H4sIAAAAAAAAAJWRbWsbMQyA/8t9jkGSJdnut2zLCiXZyJKyZaMM352vHEty4e7SUkr++9yXwUbXD8Vgg2w9eizdF7s0DPE6re8OqTgrPkzX05+L2Wo1PZ8Vk6K73ac+h0mtV49egvgc3nbX5313POSbqjvcmep2a7ZxV9bRtPub7lfKx+E4GhQEErYqYtHMn7MMuiV+fbf5rOEbzJ9wq7FPcfdm3lOaNReXyws/3cw2fvk9A4djOVR9exjbbv+x3Y6pH4qzH29hr14QzFzXi8WnxZfl+0tfXD1az27SfnxA3xdtneWtVRc8ADJrcEwkwoxigzAyiBNxzkJuIxGrei+g3gbgrDy2eRBj3OWePpuwQ/Bh8mdAGR+J69pJMFXKihwTGJ+aYJArpkjYQB2K0+SljMPgyFIIijaQgs2BAMEyexbns1NeoqpsCV+VCfCPTOVLLgUMU4h5S5UpE4BRm6ROqCEF/r8MExBDro3ED0XBMigFVM0iQlkRvZLml9a/LoN/yzSYKoIKTOmVTf6VNTHZxkjTIElkqlCL09XpN5PgkxrvAgAA", - "approximateArrivalTimestamp": 1510254474027 - }, - { - "recordId": "49578734086442259037497492980623860617859017621603614722000000", - "data": "H4sIAAAAAAAAAJWRW28aQQyF/8s+M9J47LHHeaMtzUOhEQXSVlVUDctstCqwCJZEUcR/r3OpFCnNQ17mcjw+8+n4vtqUwyFfl/ndrlRn1afhfPh7MprNhuejalB1t9uyNzkwJk6QosZk8rq7Pt93x51V6m535+rbtVvnzXKVXbu96f4U23bH3kEEHyIhx4jgxs9dDmQK3z/8vGD94cdPdrN+X/Lm3X5PbcHp5QLkYrqYLC6/mOHhuDzU+3bXt932c7vuy/5Qnf16j/fslYMb83wy+Tr5Nv24SNXVI/Xopmz7B+v7ql0ZPCKLJu+BiFUohBiJIKJGAvIkSTgpsU8aVBNangymsCH3rQ2izxvL9JmEBOzh4N+AzL6gX3JD7CLn4kg8OiVduahNkIa0BtbqNHgNI6AS0P5kQA3sUcA4IDCElCBKwgdgiCoI+CaM+pcwbAVfN8F5r2owGV0OdpWkS8kp52a1/D8MBR8sDUoQKDIbDnqlhAgQLTMWz8YbRQT92zDwEkbIQ10YHUZbKGfvUmrAIWodih2btKpOV6e/zXGIX+8CAAA=", - "approximateArrivalTimestamp": 1510254474388 - } - ], - "region": "eu-central-1", - "deliveryStreamArn": "arn:aws:firehose:eu-central-1:123456789012:deliverystream/copy-cwl-lambda-invoke-input-151025436553-Firehose-8KILJ01Q5OBN", - "invocationId": "a7234216-12b6-4bc0-96d7-82606c0e80cf" -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-get-records.json b/packages/commons/src/samples/resources/events/aws/kinesis-get-records.json deleted file mode 100644 index 12abdabd3c..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-get-records.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "Records": [ - { - "kinesis": { - "partitionKey": "partitionKey-03", - "kinesisSchemaVersion": "1.0", - "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=", - "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", - "approximateArrivalTimestamp": 1428537600 - }, - "eventSource": "aws:kinesis", - "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", - "invokeIdentityArn": "arn:aws:iam::EXAMPLE", - "eventVersion": "1.0", - "eventName": "aws:kinesis:record", - "eventSourceARN": "arn:aws:kinesis:EXAMPLE", - "awsRegion": "eu-central-1" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-kinesis-firehose.json b/packages/commons/src/samples/resources/events/aws/kinesis-kinesis-firehose.json deleted file mode 100644 index df23583934..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-kinesis-firehose.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "invocationId": "invocationIdExample", - "deliveryStreamArn": "arn:aws:kinesis:EXAMPLE", - "region": "eu-central-1", - "records": [ - { - "recordId": "49546986683135544286507457936321625675700192471156785154", - "approximateArrivalTimestamp": 1495072949453, - "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-streams-source.json b/packages/commons/src/samples/resources/events/aws/kinesis-streams-source.json deleted file mode 100644 index a462dc7e07..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-streams-source.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "invocationId": "invocationIdExample", - "deliverySteamArn": "arn:aws:kinesis:EXAMPLE", - "region": "eu-central-1", - "records": [ - { - "recordId": "49546986683135544286507457936321625675700192471156785154", - "approximateArrivalTimestamp": 1495072949453, - "kinesisRecordMetadata": { - "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", - "subsequenceNumber": "123456", - "partitionKey": "partitionKey-03", - "shardId": "shardId-000000000000", - "approximateArrivalTimestamp": 1495072949453 - }, - "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/kinesis-syslog.json b/packages/commons/src/samples/resources/events/aws/kinesis-syslog.json deleted file mode 100644 index bac8ff5567..0000000000 --- a/packages/commons/src/samples/resources/events/aws/kinesis-syslog.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "invocationId": "fir", - "region": "eu-central-1", - "records": [ - { - "recordId": "49546986683135544286507457936321625675700192471156785154", - "approximateArrivalTimestamp": 1495072949453, - "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=" - }, - { - "recordId": "49546986683135544286507457936321625675700192471156785154", - "approximateArrivalTimestamp": "2012-04-23T18:25:43.511Z", - "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/rekognition-s3-request.json b/packages/commons/src/samples/resources/events/aws/rekognition-s3-request.json deleted file mode 100644 index 89e0bd312c..0000000000 --- a/packages/commons/src/samples/resources/events/aws/rekognition-s3-request.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "Records": [ - { - "eventVersion": "2.0", - "eventSource": "aws:s3", - "awsRegion": "eu-central-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" - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/s3-delete.json b/packages/commons/src/samples/resources/events/aws/s3-delete.json deleted file mode 100644 index fd30611694..0000000000 --- a/packages/commons/src/samples/resources/events/aws/s3-delete.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "Records": [ - { - "eventVersion": "2.0", - "eventSource": "aws:s3", - "awsRegion": "eu-central-1", - "eventTime": "1970-01-01T00:00:00.000Z", - "eventName": "ObjectRemoved:Delete", - "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", - "sequencer": "0A1B2C3D4E5F678901" - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/s3-put.json b/packages/commons/src/samples/resources/events/aws/s3-put.json deleted file mode 100644 index 89e0bd312c..0000000000 --- a/packages/commons/src/samples/resources/events/aws/s3-put.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "Records": [ - { - "eventVersion": "2.0", - "eventSource": "aws:s3", - "awsRegion": "eu-central-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" - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/sagemaker-ground-truth-annotation-consolidation.json b/packages/commons/src/samples/resources/events/aws/sagemaker-ground-truth-annotation-consolidation.json deleted file mode 100644 index 2aeb4fbbce..0000000000 --- a/packages/commons/src/samples/resources/events/aws/sagemaker-ground-truth-annotation-consolidation.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": "2018-10-16", - "labelingJobArn": "arn:aws:sagemaker:eu-central-1:123456789012:labeling-job/example-job", - "labelAttributeName": "example-attribute", - "roleArn": "aws:aws:iam::{{account_id}}:role/sagemaker-role", - "payload": { - "s3Uri": "s3://sagemakerexample/output/example-job/annotations/worker_response/iteration-1/0/2019-09-06_18:35:03.json" - } -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/sagemaker-ground-truth-pre-human.json b/packages/commons/src/samples/resources/events/aws/sagemaker-ground-truth-pre-human.json deleted file mode 100644 index 26c9755d2c..0000000000 --- a/packages/commons/src/samples/resources/events/aws/sagemaker-ground-truth-pre-human.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "2018-10-16", - "labelingJobArn": "arn:aws:sagemaker:eu-central-1:123456789012:labeling-job/example-job", - "dataObject": { - "source-ref": "s3://sagemakerexample/object_to_annotate.jpg" - } -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/ses-email-receiving.json b/packages/commons/src/samples/resources/events/aws/ses-email-receiving.json deleted file mode 100644 index 3a80dfe175..0000000000 --- a/packages/commons/src/samples/resources/events/aws/ses-email-receiving.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "Records": [ - { - "eventSource": "aws:ses", - "eventVersion": "1.0", - "ses": { - "mail": { - "commonHeaders": { - "date": "Wed, 7 Oct 2015 12:34:56 -0700", - "from": [ - "Jane Doe " - ], - "messageId": "<0123456789example.com>", - "returnPath": "janedoe@example.com", - "subject": "Test Subject", - "to": [ - "johndoe@example.com" - ] - }, - "destination": [ - "johndoe@example.com" - ], - "headers": [ - { - "name": "Return-Path", - "value": "" - }, - { - "name": "Received", - "value": "from mailer.example.com (mailer.example.com [203.0.113.1]) by inbound-smtp.eu-central-1.amazonaws.com with SMTP id o3vrnil0e2ic28trm7dfhrc2v0cnbeccl4nbp0g1 for johndoe@example.com; Wed, 07 Oct 2015 12:34:56 +0000 (UTC)" - }, - { - "name": "DKIM-Signature", - "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=example; h=mime-version:from:date:message-id:subject:to:content-type; bh=jX3F0bCAI7sIbkHyy3mLYO28ieDQz2R0P8HwQkklFj4=; b=sQwJ+LMe9RjkesGu+vqU56asvMhrLRRYrWCbVt6WJulueecwfEwRf9JVWgkBTKiL6m2hr70xDbPWDhtLdLO+jB3hzjVnXwK3pYIOHw3vxG6NtJ6o61XSUwjEsp9tdyxQjZf2HNYee873832l3K1EeSXKzxYk9Pwqcpi3dMC74ct9GukjIevf1H46hm1L2d9VYTL0LGZGHOAyMnHmEGB8ZExWbI+k6khpurTQQ4sp4PZPRlgHtnj3Zzv7nmpTo7dtPG5z5S9J+L+Ba7dixT0jn3HuhaJ9b+VThboo4YfsX9PMNhWWxGjVksSFOcGluPO7QutCPyoY4gbxtwkN9W69HA==" - }, - { - "name": "MIME-Version", - "value": "1.0" - }, - { - "name": "From", - "value": "Jane Doe " - }, - { - "name": "Date", - "value": "Wed, 7 Oct 2015 12:34:56 -0700" - }, - { - "name": "Message-ID", - "value": "<0123456789example.com>" - }, - { - "name": "Subject", - "value": "Test Subject" - }, - { - "name": "To", - "value": "johndoe@example.com" - }, - { - "name": "Content-Type", - "value": "text/plain; charset=UTF-8" - } - ], - "headersTruncated": false, - "messageId": "o3vrnil0e2ic28trm7dfhrc2v0clambda4nbp0g1", - "source": "janedoe@example.com", - "timestamp": "1970-01-01T00:00:00.000Z" - }, - "receipt": { - "action": { - "functionArn": "arn:aws:lambda:eu-central-1:123456789012:function:Example", - "invocationType": "Event", - "type": "Lambda" - }, - "dkimVerdict": { - "status": "PASS" - }, - "processingTimeMillis": 574, - "recipients": [ - "johndoe@example.com" - ], - "spamVerdict": { - "status": "PASS" - }, - "spfVerdict": { - "status": "PASS" - }, - "timestamp": "1970-01-01T00:00:00.000Z", - "virusVerdict": { - "status": "PASS" - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/sns-notification.json b/packages/commons/src/samples/resources/events/aws/sns-notification.json deleted file mode 100644 index 4bb79dee1e..0000000000 --- a/packages/commons/src/samples/resources/events/aws/sns-notification.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "Records": [ - { - "EventSource": "aws:sns", - "EventVersion": "1.0", - "EventSubscriptionArn": "arn:aws:sns:eu-central-1:{{{accountId}}}:ExampleTopic", - "Sns": { - "Type": "Notification", - "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", - "TopicArn": "arn:aws:sns:eu-central-1:123456789012:ExampleTopic", - "Subject": "example subject", - "Message": "example message", - "Timestamp": "1970-01-01T00:00:00.000Z", - "SignatureVersion": "1", - "Signature": "EXAMPLE", - "SigningCertUrl": "EXAMPLE", - "UnsubscribeUrl": "EXAMPLE", - "MessageAttributes": { - "Test": { - "Type": "String", - "Value": "TestString" - }, - "TestBinary": { - "Type": "Binary", - "Value": "TestBinary" - } - } - } - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/sqs-receive-message.json b/packages/commons/src/samples/resources/events/aws/sqs-receive-message.json deleted file mode 100644 index a24697a237..0000000000 --- a/packages/commons/src/samples/resources/events/aws/sqs-receive-message.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "Records": [ - { - "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", - "receiptHandle": "MessageReceiptHandle", - "body": "Hello from SQS!", - "attributes": { - "ApproximateReceiveCount": "1", - "SentTimestamp": "1523232000000", - "SenderId": "123456789012", - "ApproximateFirstReceiveTimestamp": "1523232000001" - }, - "messageAttributes": {}, - "md5OfBody": "{{{md5_of_body}}}", - "eventSource": "aws:sqs", - "eventSourceARN": "arn:aws:sqs:eu-central-1:123456789012:MyQueue", - "awsRegion": "eu-central-1" - } - ] -} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/aws/stepfunctions-error.json b/packages/commons/src/samples/resources/events/aws/stepfunctions-error.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/packages/commons/src/samples/resources/events/aws/stepfunctions-error.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/packages/commons/src/samples/resources/events/custom/index.ts b/packages/commons/src/samples/resources/events/custom/index.ts deleted file mode 100644 index bf173e958f..0000000000 --- a/packages/commons/src/samples/resources/events/custom/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const CustomEvent = { - key1: 'value1', - key2: 'value2', - key3: 'value3', -}; diff --git a/packages/commons/src/samples/resources/events/index.ts b/packages/commons/src/samples/resources/events/index.ts deleted file mode 100644 index 45582e4428..0000000000 --- a/packages/commons/src/samples/resources/events/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as Custom from './custom'; diff --git a/packages/commons/src/config/ConfigService.ts b/packages/commons/src/types/ConfigServiceInterface.ts similarity index 76% rename from packages/commons/src/config/ConfigService.ts rename to packages/commons/src/types/ConfigServiceInterface.ts index df2d69bd81..1e249f9373 100644 --- a/packages/commons/src/config/ConfigService.ts +++ b/packages/commons/src/types/ConfigServiceInterface.ts @@ -3,25 +3,22 @@ * * This class defines common methods and variables that can be set by the developer * in the runtime. - * - * @class - * @abstract */ -abstract class ConfigService { +interface ConfigServiceInterface { /** * It returns the value of an environment variable that has given name. * * @param {string} name * @returns {string} */ - public abstract get(name: string): string; + get(name: string): string; /** * It returns the value of the POWERTOOLS_SERVICE_NAME environment variable. * * @returns {string} */ - public abstract getServiceName(): string; + getServiceName(): string; /** * It returns the value of the _X_AMZN_TRACE_ID environment variable. @@ -33,14 +30,14 @@ abstract class ConfigService { * * @returns {string|undefined} */ - public abstract getXrayTraceId(): string | undefined; + getXrayTraceId(): string | undefined; /** * It returns true if the `POWERTOOLS_DEV` environment variable is set to truthy value. * * @returns {boolean} */ - public abstract isDevMode(): boolean; + isDevMode(): boolean; /** * It returns true if the string value represents a boolean true value. @@ -48,7 +45,7 @@ abstract class ConfigService { * @param {string} value * @returns boolean */ - public abstract isValueTrue(value: string): boolean; + isValueTrue(value: string): boolean; } -export { ConfigService }; +export type { ConfigServiceInterface }; diff --git a/packages/commons/src/types/LambdaInterface.ts b/packages/commons/src/types/LambdaInterface.ts new file mode 100644 index 0000000000..7eb6609487 --- /dev/null +++ b/packages/commons/src/types/LambdaInterface.ts @@ -0,0 +1,31 @@ +import type { Handler } from 'aws-lambda'; + +type SyncHandler = ( + event: Parameters[0], + context: Parameters[1], + callback: Parameters[2] +) => void; + +type AsyncHandler = ( + event: Parameters[0], + context: Parameters[1] +) => Promise[2]>[1]>>; + +interface LambdaInterface { + handler: SyncHandler | AsyncHandler; +} + +type HandlerMethodDecorator = ( + target: LambdaInterface, + propertyKey: string | symbol, + descriptor: + | TypedPropertyDescriptor> + | TypedPropertyDescriptor> +) => void; + +export type { + AsyncHandler, + SyncHandler, + LambdaInterface, + HandlerMethodDecorator, +}; diff --git a/packages/commons/src/types/awsSdk.ts b/packages/commons/src/types/awsSdk.ts index a62ba6c891..f29d1bc8ff 100644 --- a/packages/commons/src/types/awsSdk.ts +++ b/packages/commons/src/types/awsSdk.ts @@ -19,4 +19,4 @@ interface SdkClient { */ type MiddlewareArgsLike = { request: { headers: { [key: string]: string } } }; -export { SdkClient, MiddlewareArgsLike }; +export type { SdkClient, MiddlewareArgsLike }; diff --git a/packages/commons/src/types/index.ts b/packages/commons/src/types/index.ts new file mode 100644 index 0000000000..b57b2dfec4 --- /dev/null +++ b/packages/commons/src/types/index.ts @@ -0,0 +1,19 @@ +export type { + MiddlewareLikeObj, + MiddyLikeRequest, + CleanupFunction, +} from './middy.js'; +export type { SdkClient, MiddlewareArgsLike } from './awsSdk.js'; +export type { + JSONPrimitive, + JSONValue, + JSONObject, + JSONArray, +} from './json.js'; +export type { + SyncHandler, + AsyncHandler, + LambdaInterface, + HandlerMethodDecorator, +} from './LambdaInterface.js'; +export type { ConfigServiceInterface } from './ConfigServiceInterface.js'; diff --git a/packages/commons/src/types/json.ts b/packages/commons/src/types/json.ts new file mode 100644 index 0000000000..765491c699 --- /dev/null +++ b/packages/commons/src/types/json.ts @@ -0,0 +1,6 @@ +type JSONPrimitive = string | number | boolean | null | undefined; +type JSONValue = JSONPrimitive | JSONObject | JSONArray; +type JSONObject = { [key: number | string]: JSONValue }; +type JSONArray = Array; + +export type { JSONPrimitive, JSONValue, JSONObject, JSONArray }; diff --git a/packages/commons/src/types/middy.ts b/packages/commons/src/types/middy.ts index 0399408d8c..92c94a0ba5 100644 --- a/packages/commons/src/types/middy.ts +++ b/packages/commons/src/types/middy.ts @@ -57,4 +57,4 @@ type MiddyLikeRequest = { */ type CleanupFunction = (request: MiddyLikeRequest) => Promise; -export { MiddlewareLikeObj, MiddyLikeRequest, CleanupFunction }; +export type { MiddlewareLikeObj, MiddyLikeRequest, CleanupFunction }; diff --git a/packages/commons/src/utils/lambda/LambdaInterface.ts b/packages/commons/src/utils/lambda/LambdaInterface.ts deleted file mode 100644 index 0871d433f9..0000000000 --- a/packages/commons/src/utils/lambda/LambdaInterface.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Handler } from 'aws-lambda'; - -export type SyncHandler = ( - event: Parameters[0], - context: Parameters[1], - callback: Parameters[2] -) => void; - -export type AsyncHandler = ( - event: Parameters[0], - context: Parameters[1] -) => Promise[2]>[1]>>; - -interface LambdaInterface { - handler: SyncHandler | AsyncHandler; -} - -export { LambdaInterface }; diff --git a/packages/commons/src/utils/lambda/index.ts b/packages/commons/src/utils/lambda/index.ts deleted file mode 100644 index f57f966d0c..0000000000 --- a/packages/commons/src/utils/lambda/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './LambdaInterface'; diff --git a/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/commons/tests/unit/EnvironmentVariablesService.test.ts similarity index 98% rename from packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts rename to packages/commons/tests/unit/EnvironmentVariablesService.test.ts index b1e90b6b08..230e04afb0 100644 --- a/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/commons/tests/unit/EnvironmentVariablesService.test.ts @@ -1,10 +1,9 @@ /** * Test EnvironmentVariablesService class * - * @group unit/commons/all + * @group unit/commons/environmentService */ - -import { EnvironmentVariablesService } from '../../../src/config'; +import { EnvironmentVariablesService } from '../../src/index.js'; describe('Class: EnvironmentVariablesService', () => { const ENVIRONMENT_VARIABLES = process.env; diff --git a/packages/commons/tests/unit/LambdaInterface.test.ts b/packages/commons/tests/unit/LambdaInterface.test.ts index 52049aadc0..7d7decc5a0 100644 --- a/packages/commons/tests/unit/LambdaInterface.test.ts +++ b/packages/commons/tests/unit/LambdaInterface.test.ts @@ -5,14 +5,15 @@ */ import { Handler } from 'aws-lambda'; import { Callback, Context } from 'aws-lambda'; -import { - ContextExamples, +import context from '@aws-lambda-powertools/testing-utils/context'; +import type { SyncHandler, AsyncHandler, LambdaInterface, -} from '../../src'; +} from '../../src/types/index.js'; describe('LambdaInterface with arrow function', () => { + jest.spyOn(console, 'log').mockImplementation(); test('it compiles when given a callback', async () => { class LambdaFunction implements LambdaInterface { public handler: SyncHandler = async ( @@ -28,10 +29,8 @@ describe('LambdaInterface with arrow function', () => { }; } - await new LambdaFunction().handler( - {}, - ContextExamples.helloworldContext, - () => console.log('Lambda invoked!') + new LambdaFunction().handler({}, context, () => + console.log('Lambda invoked!') ); }); @@ -45,7 +44,7 @@ describe('LambdaInterface with arrow function', () => { }; } - await new LambdaFunction().handler({}, ContextExamples.helloworldContext); + await new LambdaFunction().handler({}, context); }); }); @@ -62,10 +61,8 @@ describe('LambdaInterface with standard function', () => { } } - await new LambdaFunction().handler( - {}, - ContextExamples.helloworldContext, - () => console.log('Lambda invoked!') + new LambdaFunction().handler({}, context, () => + console.log('Lambda invoked!') ); }); @@ -80,7 +77,7 @@ describe('LambdaInterface with standard function', () => { } } - await new LambdaFunction().handler({}, ContextExamples.helloworldContext); + await new LambdaFunction().handler({}, context); }); }); @@ -138,7 +135,7 @@ describe('LambdaInterface with decorator', () => { } } - await new LambdaFunction().handler({}, ContextExamples.helloworldContext); + await new LambdaFunction().handler({}, context); }); test('decorator with callback compile', async () => { @@ -154,10 +151,8 @@ describe('LambdaInterface with decorator', () => { } } - await new LambdaFunction().handler( - {}, - ContextExamples.helloworldContext, - () => console.log('Lambda invoked!') + new LambdaFunction().handler({}, context, () => + console.log('Lambda invoked!') ); }); }); diff --git a/packages/commons/tests/unit/Utility.test.ts b/packages/commons/tests/unit/Utility.test.ts index f289d15d85..9e9d18194e 100644 --- a/packages/commons/tests/unit/Utility.test.ts +++ b/packages/commons/tests/unit/Utility.test.ts @@ -3,7 +3,7 @@ * * @group unit/commons/utility */ -import { Utility } from '../../src'; +import { Utility } from '../../src/index.js'; describe('Class: Utility', () => { beforeEach(() => { diff --git a/packages/commons/tests/unit/awsSdk.test.ts b/packages/commons/tests/unit/awsSdkUtils.test.ts similarity index 94% rename from packages/commons/tests/unit/awsSdk.test.ts rename to packages/commons/tests/unit/awsSdkUtils.test.ts index 4ec4a75b99..0a2e4c6cd7 100644 --- a/packages/commons/tests/unit/awsSdk.test.ts +++ b/packages/commons/tests/unit/awsSdkUtils.test.ts @@ -1,6 +1,9 @@ -import { addUserAgentMiddleware, isSdkClient } from '../../src/awsSdk'; -import { PT_VERSION as version } from '../../src/version'; -import { customUserAgentMiddleware } from '../../src/awsSdk/userAgentMiddleware'; +import { + addUserAgentMiddleware, + isSdkClient, + PT_VERSION as version, +} from '../../src/index.js'; +import { customUserAgentMiddleware } from '../../src/awsSdkUtils.js'; describe('Helpers: awsSdk', () => { describe('Function: userAgentMiddleware', () => { diff --git a/packages/commons/tests/unit/cleanupMiddlewares.test.ts b/packages/commons/tests/unit/cleanupMiddlewares.test.ts index 12583d9107..15295a0396 100644 --- a/packages/commons/tests/unit/cleanupMiddlewares.test.ts +++ b/packages/commons/tests/unit/cleanupMiddlewares.test.ts @@ -7,14 +7,18 @@ import { cleanupMiddlewares, TRACER_KEY, METRICS_KEY, -} from '../../src/middleware'; -import { helloworldContext as context } from '../../src/samples/resources/contexts/hello-world'; + LOGGER_KEY, + IDEMPOTENCY_KEY, +} from '../../src/index.js'; +import context from '@aws-lambda-powertools/testing-utils/context'; describe('Function: cleanupMiddlewares', () => { it('calls the cleanup function that are present', async () => { // Prepare const mockCleanupFunction1 = jest.fn(); const mockCleanupFunction2 = jest.fn(); + const mockCleanupFunction3 = jest.fn(); + const mockCleanupFunction4 = jest.fn(); const mockRequest = { event: {}, context: context, @@ -23,6 +27,8 @@ describe('Function: cleanupMiddlewares', () => { internal: { [TRACER_KEY]: mockCleanupFunction1, [METRICS_KEY]: mockCleanupFunction2, + [LOGGER_KEY]: mockCleanupFunction3, + [IDEMPOTENCY_KEY]: mockCleanupFunction4, }, }; @@ -34,6 +40,10 @@ describe('Function: cleanupMiddlewares', () => { expect(mockCleanupFunction1).toHaveBeenCalledWith(mockRequest); expect(mockCleanupFunction2).toHaveBeenCalledTimes(1); expect(mockCleanupFunction2).toHaveBeenCalledWith(mockRequest); + expect(mockCleanupFunction3).toHaveBeenCalledTimes(1); + expect(mockCleanupFunction3).toHaveBeenCalledWith(mockRequest); + expect(mockCleanupFunction4).toHaveBeenCalledTimes(1); + expect(mockCleanupFunction4).toHaveBeenCalledWith(mockRequest); }); it('resolves successfully if no cleanup function is present', async () => { // Prepare diff --git a/packages/commons/tests/unit/utils.test.ts b/packages/commons/tests/unit/guards.test.ts similarity index 95% rename from packages/commons/tests/unit/utils.test.ts rename to packages/commons/tests/unit/guards.test.ts index 464b05958f..53e248e643 100644 --- a/packages/commons/tests/unit/utils.test.ts +++ b/packages/commons/tests/unit/guards.test.ts @@ -1,16 +1,16 @@ /** - * Test utils functions + * Test guards functions * - * @group unit/commons/utils + * @group unit/commons/guards */ import { isRecord, isTruthy, isNullOrUndefined, isString, -} from '../../src/types/utils'; +} from '../../src/index.js'; -describe('Functions: utils', () => { +describe('Functions: guards', () => { beforeEach(() => { jest.clearAllMocks(); jest.resetModules(); diff --git a/packages/commons/tsconfig.esm.json b/packages/commons/tsconfig.esm.json new file mode 100644 index 0000000000..123291b0cf --- /dev/null +++ b/packages/commons/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/esm.json" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/commons/tsconfig.json b/packages/commons/tsconfig.json index 1cb9d72773..4836a14962 100644 --- a/packages/commons/tsconfig.json +++ b/packages/commons/tsconfig.json @@ -1,8 +1,9 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./lib", + "outDir": "./lib/cjs/", "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" }, "include": [ "./src/**/*" diff --git a/packages/idempotency/jest.config.js b/packages/idempotency/jest.config.cjs similarity index 93% rename from packages/idempotency/jest.config.js rename to packages/idempotency/jest.config.cjs index 7a512f0d83..758a5e9f23 100644 --- a/packages/idempotency/jest.config.js +++ b/packages/idempotency/jest.config.cjs @@ -5,6 +5,9 @@ module.exports = { }, runner: 'groups', preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, diff --git a/packages/idempotency/package.json b/packages/idempotency/package.json index 45ee058b20..a0c489912f 100644 --- a/packages/idempotency/package.json +++ b/packages/idempotency/package.json @@ -13,16 +13,16 @@ "test": "npm run test:unit", "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", "jest": "jest --detectOpenHandles --verbose", - "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e", "test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e", "test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e", "test:e2e": "jest --group=e2e", "watch": "jest --watch", - "build": "tsc --build --force", + "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", - "prebuild": "rimraf ./lib", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { @@ -30,46 +30,65 @@ }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/idempotency#readme", "license": "MIT-0", + "type": "module", "exports": { ".": { - "import": "./lib/index.js", - "require": "./lib/index.js" + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } }, "./persistence": { - "import": "./lib/persistence/index.js", - "require": "./lib/persistence/index.js" + "import": "./lib/esm/persistence/index.js", + "require": "./lib/cjs/persistence/index.js" }, "./dynamodb": { - "import": "./lib/persistence/DynamoDBPersistenceLayer.js", - "require": "./lib/persistence/DynamoDBPersistenceLayer.js" + "import": "./lib/esm/persistence/DynamoDBPersistenceLayer.js", + "require": "./lib/cjs/persistence/DynamoDBPersistenceLayer.js" + }, + "./dynamodb/types": { + "import": "./lib/esm/types/DynamoDBPersistence.js", + "require": "./lib/cjs/types/DynamoDBPersistence.js" }, "./middleware": { - "import": "./lib/middleware/index.js", - "require": "./lib/middleware/index.js" + "import": "./lib/esm/middleware/makeHandlerIdempotent.js", + "require": "./lib/cjs/middleware/makeHandlerIdempotent.js" }, "./types": { - "import": "./lib/types/index.d.ts", - "require": "./lib/types/index.d.ts" + "import": "./lib/esm/types/index.d.ts", + "require": "./lib/cjs/types/index.d.ts" } }, "typesVersions": { "*": { "persistence": [ - "lib/persistence/index.d.ts" + "lib/cjs/persistence/index.d.ts", + "lib/esm/persistence/index.d.ts" ], "dynamodb": [ - "lib/persistence/DynamoDBPersistenceLayer.d.ts" + "lib/cjs/persistence/DynamoDBPersistenceLayer.d.ts", + "lib/esm/persistence/DynamoDBPersistenceLayer.d.ts" + ], + "dynamodb/types": [ + "lib/cjs/types/DynamoDBPersistence.d.ts", + "lib/esm/types/DynamoDBPersistence.d.ts" ], "middleware": [ - "lib/middleware/index.d.ts" + "lib/cjs/middleware/makeHandlerIdempotent.d.ts", + "lib/esm/middleware/makeHandlerIdempotent.d.ts" ], "types": [ - "lib/types/index.d.ts" + "lib/cjs/types/index.d.ts", + "lib/esm/types/index.d.ts" ] } }, - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "files": [ "lib" ], @@ -117,4 +136,4 @@ "aws-sdk-client-mock": "^3.0.1", "aws-sdk-client-mock-jest": "^3.0.1" } -} +} \ No newline at end of file diff --git a/packages/idempotency/src/IdempotencyConfig.ts b/packages/idempotency/src/IdempotencyConfig.ts index 95b8a96a99..0f5afc6f09 100644 --- a/packages/idempotency/src/IdempotencyConfig.ts +++ b/packages/idempotency/src/IdempotencyConfig.ts @@ -1,6 +1,6 @@ -import { EnvironmentVariablesService } from './config'; +import { EnvironmentVariablesService } from './config/EnvironmentVariablesService.js'; import type { Context } from 'aws-lambda'; -import type { IdempotencyConfigOptions } from './types'; +import type { IdempotencyConfigOptions } from './types/IdempotencyOptions.js'; /** * Configuration for the idempotency feature. diff --git a/packages/idempotency/src/IdempotencyHandler.ts b/packages/idempotency/src/IdempotencyHandler.ts index a1ef92053a..03f06b1c2d 100644 --- a/packages/idempotency/src/IdempotencyHandler.ts +++ b/packages/idempotency/src/IdempotencyHandler.ts @@ -1,17 +1,21 @@ import type { JSONValue, MiddyLikeRequest, -} from '@aws-lambda-powertools/commons'; -import type { AnyFunction, IdempotencyHandlerOptions } from './types'; +} from '@aws-lambda-powertools/commons/types'; +import type { + AnyFunction, + IdempotencyHandlerOptions, +} from './types/IdempotencyOptions.js'; import { IdempotencyAlreadyInProgressError, IdempotencyInconsistentStateError, IdempotencyItemAlreadyExistsError, IdempotencyPersistenceLayerError, -} from './errors'; -import { BasePersistenceLayer, IdempotencyRecord } from './persistence'; -import { IdempotencyConfig } from './IdempotencyConfig'; -import { MAX_RETRIES, IdempotencyRecordStatus } from './constants'; +} from './errors.js'; +import { BasePersistenceLayer } from './persistence/BasePersistenceLayer.js'; +import { IdempotencyRecord } from './persistence/IdempotencyRecord.js'; +import { IdempotencyConfig } from './IdempotencyConfig.js'; +import { MAX_RETRIES, IdempotencyRecordStatus } from './constants.js'; import { search } from 'jmespath'; /** diff --git a/packages/idempotency/src/config/ConfigServiceInterface.ts b/packages/idempotency/src/config/ConfigServiceInterface.ts deleted file mode 100644 index f4938baeaf..0000000000 --- a/packages/idempotency/src/config/ConfigServiceInterface.ts +++ /dev/null @@ -1,11 +0,0 @@ -interface ConfigServiceInterface { - get(name: string): string; - - getServiceName(): string; - - getFunctionName(): string; - - getIdempotencyEnabled(): boolean; -} - -export { ConfigServiceInterface }; diff --git a/packages/idempotency/src/config/EnvironmentVariablesService.ts b/packages/idempotency/src/config/EnvironmentVariablesService.ts index f87635593b..a6d3e9b257 100644 --- a/packages/idempotency/src/config/EnvironmentVariablesService.ts +++ b/packages/idempotency/src/config/EnvironmentVariablesService.ts @@ -1,4 +1,4 @@ -import { ConfigServiceInterface } from './ConfigServiceInterface'; +import type { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; import { EnvironmentVariablesService as CommonEnvironmentVariablesService } from '@aws-lambda-powertools/commons'; /** diff --git a/packages/idempotency/src/config/index.ts b/packages/idempotency/src/config/index.ts deleted file mode 100644 index 3dfae2b192..0000000000 --- a/packages/idempotency/src/config/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './EnvironmentVariablesService'; diff --git a/packages/idempotency/src/errors.ts b/packages/idempotency/src/errors.ts index 1f40411083..5f210adfcd 100644 --- a/packages/idempotency/src/errors.ts +++ b/packages/idempotency/src/errors.ts @@ -1,4 +1,4 @@ -import type { IdempotencyRecord } from './persistence'; +import type { IdempotencyRecord } from './persistence/IdempotencyRecord.js'; /** * Item attempting to be inserted into persistence store already exists and is not expired diff --git a/packages/idempotency/src/idempotencyDecorator.ts b/packages/idempotency/src/idempotencyDecorator.ts index 8e2d7f9265..6c6ff7b2e1 100644 --- a/packages/idempotency/src/idempotencyDecorator.ts +++ b/packages/idempotency/src/idempotencyDecorator.ts @@ -1,5 +1,8 @@ -import { AnyFunction, ItempotentFunctionOptions } from './types'; -import { makeIdempotent } from './makeIdempotent'; +import { + AnyFunction, + ItempotentFunctionOptions, +} from './types/IdempotencyOptions.js'; +import { makeIdempotent } from './makeIdempotent.js'; /** * Use this decorator to make your lambda handler itempotent. @@ -12,7 +15,7 @@ import { makeIdempotent } from './makeIdempotent'; * DynamoDBPersistenceLayer, * idempotentLambdaHandler * } from '@aws-lambda-powertools/idempotency'; - * import type { LambdaInterface } from '@aws-lambda-powertools/commons'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; * * class MyLambdaFunction implements LambdaInterface { * ⁣@idempotent({ persistenceStore: new DynamoDBPersistenceLayer() }) @@ -31,7 +34,7 @@ import { makeIdempotent } from './makeIdempotent'; * DynamoDBPersistenceLayer, * idempotentFunction * } from '@aws-lambda-powertools/idempotency'; - * import type { LambdaInterface } from '@aws-lambda-powertools/commons'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; * * class MyClass implements LambdaInterface { * public async handler(event: unknown, _context: unknown) { diff --git a/packages/idempotency/src/index.ts b/packages/idempotency/src/index.ts index 0b86f82a69..ced4539b1d 100644 --- a/packages/idempotency/src/index.ts +++ b/packages/idempotency/src/index.ts @@ -1,5 +1,14 @@ -export * from './errors'; -export * from './IdempotencyConfig'; -export * from './makeIdempotent'; -export * from './idempotencyDecorator'; -export { IdempotencyRecordStatus } from './constants'; +export { + IdempotencyItemAlreadyExistsError, + IdempotencyItemNotFoundError, + IdempotencyAlreadyInProgressError, + IdempotencyInvalidStatusError, + IdempotencyValidationError, + IdempotencyInconsistentStateError, + IdempotencyPersistenceLayerError, + IdempotencyKeyError, +} from './errors.js'; +export { IdempotencyConfig } from './IdempotencyConfig.js'; +export { makeIdempotent } from './makeIdempotent.js'; +export { idempotent } from './idempotencyDecorator.js'; +export { IdempotencyRecordStatus } from './constants.js'; diff --git a/packages/idempotency/src/makeIdempotent.ts b/packages/idempotency/src/makeIdempotent.ts index 8bae3cdf6d..dafb807e53 100644 --- a/packages/idempotency/src/makeIdempotent.ts +++ b/packages/idempotency/src/makeIdempotent.ts @@ -3,9 +3,9 @@ import type { AnyFunction, ItempotentFunctionOptions, IdempotencyLambdaHandlerOptions, -} from './types'; -import { IdempotencyHandler } from './IdempotencyHandler'; -import { IdempotencyConfig } from './IdempotencyConfig'; +} from './types/IdempotencyOptions.js'; +import { IdempotencyHandler } from './IdempotencyHandler.js'; +import { IdempotencyConfig } from './IdempotencyConfig.js'; const isContext = (arg: unknown): arg is Context => { return ( diff --git a/packages/idempotency/src/middleware/index.ts b/packages/idempotency/src/middleware/index.ts deleted file mode 100644 index c95b47d06a..0000000000 --- a/packages/idempotency/src/middleware/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './makeHandlerIdempotent'; diff --git a/packages/idempotency/src/middleware/makeHandlerIdempotent.ts b/packages/idempotency/src/middleware/makeHandlerIdempotent.ts index 25c4525f58..3ce92a73a2 100644 --- a/packages/idempotency/src/middleware/makeHandlerIdempotent.ts +++ b/packages/idempotency/src/middleware/makeHandlerIdempotent.ts @@ -1,15 +1,18 @@ -import { IdempotencyHandler } from '../IdempotencyHandler'; -import { IdempotencyConfig } from '../IdempotencyConfig'; +import { IdempotencyHandler } from '../IdempotencyHandler.js'; +import { IdempotencyConfig } from '../IdempotencyConfig.js'; import { cleanupMiddlewares, IDEMPOTENCY_KEY, -} from '@aws-lambda-powertools/commons/lib/middleware'; -import type { AnyFunction, IdempotencyLambdaHandlerOptions } from '../types'; +} from '@aws-lambda-powertools/commons'; +import type { + AnyFunction, + IdempotencyLambdaHandlerOptions, +} from '../types/IdempotencyOptions.js'; import type { MiddlewareLikeObj, MiddyLikeRequest, JSONValue, -} from '@aws-lambda-powertools/commons'; +} from '@aws-lambda-powertools/commons/types'; /** * @internal diff --git a/packages/idempotency/src/persistence/BasePersistenceLayer.ts b/packages/idempotency/src/persistence/BasePersistenceLayer.ts index 7f28a45d6a..526f963378 100644 --- a/packages/idempotency/src/persistence/BasePersistenceLayer.ts +++ b/packages/idempotency/src/persistence/BasePersistenceLayer.ts @@ -1,16 +1,19 @@ import { createHash, Hash } from 'node:crypto'; import { search } from 'jmespath'; -import type { BasePersistenceLayerOptions } from '../types'; -import { IdempotencyRecordStatus } from '../constants'; -import { EnvironmentVariablesService } from '../config'; -import { IdempotencyRecord } from './IdempotencyRecord'; -import { BasePersistenceLayerInterface } from './BasePersistenceLayerInterface'; +import type { + BasePersistenceLayerOptions, + BasePersistenceLayerInterface, +} from '../types/BasePersistenceLayer.js'; +import { IdempotencyRecordStatus } from '../constants.js'; +import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js'; +import { IdempotencyRecord } from './IdempotencyRecord.js'; import { IdempotencyItemAlreadyExistsError, + IdempotencyKeyError, IdempotencyValidationError, -} from '../errors'; -import { LRUCache } from './LRUCache'; -import type { JSONValue } from '@aws-lambda-powertools/commons'; +} from '../errors.js'; +import { LRUCache } from './LRUCache.js'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; /** * Base class for all persistence layers. This class provides the basic functionality for @@ -280,7 +283,9 @@ abstract class BasePersistenceLayer implements BasePersistenceLayerInterface { if (BasePersistenceLayer.isMissingIdempotencyKey(data)) { if (this.throwOnNoIdempotencyKey) { - throw new Error('No data found to create a hashed idempotency_key'); + throw new IdempotencyKeyError( + 'No data found to create a hashed idempotency_key' + ); } console.warn( `No value found for idempotency_key. jmespath: ${this.eventKeyJmesPath}` diff --git a/packages/idempotency/src/persistence/BasePersistenceLayerInterface.ts b/packages/idempotency/src/persistence/BasePersistenceLayerInterface.ts deleted file mode 100644 index f4e792082a..0000000000 --- a/packages/idempotency/src/persistence/BasePersistenceLayerInterface.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { IdempotencyRecord } from './IdempotencyRecord'; -import type { BasePersistenceLayerOptions } from '../types/BasePersistenceLayer'; - -// TODO: move this to types folder -interface BasePersistenceLayerInterface { - configure(options?: BasePersistenceLayerOptions): void; - isPayloadValidationEnabled(): boolean; - saveInProgress(data: unknown, remainingTimeInMillis?: number): Promise; - saveSuccess(data: unknown, result: unknown): Promise; - deleteRecord(data: unknown): Promise; - getRecord(data: unknown): Promise; -} - -export { BasePersistenceLayerInterface }; diff --git a/packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts b/packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts index f30fa83b57..eacbe47e0a 100644 --- a/packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts +++ b/packages/idempotency/src/persistence/DynamoDBPersistenceLayer.ts @@ -1,9 +1,9 @@ import { IdempotencyItemAlreadyExistsError, IdempotencyItemNotFoundError, -} from '../errors'; -import { IdempotencyRecordStatus } from '../constants'; -import type { DynamoDBPersistenceOptions } from '../types'; +} from '../errors.js'; +import { IdempotencyRecordStatus } from '../constants.js'; +import type { DynamoDBPersistenceOptions } from '../types/DynamoDBPersistence.js'; import { AttributeValue, ConditionalCheckFailedException, @@ -15,8 +15,8 @@ import { UpdateItemCommand, } from '@aws-sdk/client-dynamodb'; import { marshall, unmarshall } from '@aws-sdk/util-dynamodb'; -import { IdempotencyRecord } from './IdempotencyRecord'; -import { BasePersistenceLayer } from './BasePersistenceLayer'; +import { IdempotencyRecord } from './IdempotencyRecord.js'; +import { BasePersistenceLayer } from './BasePersistenceLayer.js'; import { addUserAgentMiddleware, isSdkClient, diff --git a/packages/idempotency/src/persistence/IdempotencyRecord.ts b/packages/idempotency/src/persistence/IdempotencyRecord.ts index 7811bc7413..3dba4af73c 100644 --- a/packages/idempotency/src/persistence/IdempotencyRecord.ts +++ b/packages/idempotency/src/persistence/IdempotencyRecord.ts @@ -1,10 +1,10 @@ -import type { JSONValue } from '@aws-lambda-powertools/commons'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import type { IdempotencyRecordOptions, IdempotencyRecordStatusValue, -} from '../types'; -import { IdempotencyRecordStatus } from '../constants'; -import { IdempotencyInvalidStatusError } from '../errors'; +} from '../types/IdempotencyRecord.js'; +import { IdempotencyRecordStatus } from '../constants.js'; +import { IdempotencyInvalidStatusError } from '../errors.js'; /** * Class representing an idempotency record. diff --git a/packages/idempotency/src/persistence/LRUCache.ts b/packages/idempotency/src/persistence/LRUCache.ts index 583456d4aa..45b8630d4a 100644 --- a/packages/idempotency/src/persistence/LRUCache.ts +++ b/packages/idempotency/src/persistence/LRUCache.ts @@ -1,4 +1,4 @@ -import type { LRUCacheOptions } from '../types'; +import type { LRUCacheOptions } from '../types/LRUCache.js'; const DEFAULT_MAX_SIZE = 100; const NEWER = Symbol('newer'); diff --git a/packages/idempotency/src/persistence/index.ts b/packages/idempotency/src/persistence/index.ts index 72ea36e01f..73d7815170 100644 --- a/packages/idempotency/src/persistence/index.ts +++ b/packages/idempotency/src/persistence/index.ts @@ -1,3 +1,2 @@ -export * from './BasePersistenceLayer'; -export * from './BasePersistenceLayerInterface'; -export * from './IdempotencyRecord'; +export { BasePersistenceLayer } from './BasePersistenceLayer.js'; +export { IdempotencyRecord } from './IdempotencyRecord.js'; diff --git a/packages/idempotency/src/types/BasePersistenceLayer.ts b/packages/idempotency/src/types/BasePersistenceLayer.ts index b11db6f6b7..badda0271f 100644 --- a/packages/idempotency/src/types/BasePersistenceLayer.ts +++ b/packages/idempotency/src/types/BasePersistenceLayer.ts @@ -1,8 +1,18 @@ -import { IdempotencyConfig } from '../IdempotencyConfig'; +import { IdempotencyRecord } from '../persistence/IdempotencyRecord.js'; +import { IdempotencyConfig } from '../IdempotencyConfig.js'; type BasePersistenceLayerOptions = { config: IdempotencyConfig; functionName?: string; }; -export { BasePersistenceLayerOptions }; +interface BasePersistenceLayerInterface { + configure(options?: BasePersistenceLayerOptions): void; + isPayloadValidationEnabled(): boolean; + saveInProgress(data: unknown, remainingTimeInMillis?: number): Promise; + saveSuccess(data: unknown, result: unknown): Promise; + deleteRecord(data: unknown): Promise; + getRecord(data: unknown): Promise; +} + +export type { BasePersistenceLayerOptions, BasePersistenceLayerInterface }; diff --git a/packages/idempotency/src/types/ConfigServiceInterface.ts b/packages/idempotency/src/types/ConfigServiceInterface.ts new file mode 100644 index 0000000000..c4d8f11b25 --- /dev/null +++ b/packages/idempotency/src/types/ConfigServiceInterface.ts @@ -0,0 +1,28 @@ +import type { ConfigServiceInterface as ConfigServiceBaseInterface } from '@aws-lambda-powertools/commons/types'; + +/** + * Interface ConfigServiceInterface + * + * @interface + * @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime + * @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables + */ +interface ConfigServiceInterface extends ConfigServiceBaseInterface { + /** + * It returns the value of the AWS_LAMBDA_FUNCTION_NAME environment variable. + * + * @returns {string} + */ + getFunctionName(): string; + + /** + * It returns whether the idempotency feature is enabled or not. + * + * Reads the value of the POWERTOOLS_IDEMPOTENCY_DISABLED environment variable. + * + * @returns {boolean} + */ + getIdempotencyEnabled(): boolean; +} + +export type { ConfigServiceInterface }; diff --git a/packages/idempotency/src/types/IdempotencyOptions.ts b/packages/idempotency/src/types/IdempotencyOptions.ts index 959d014321..c8ae467ad4 100644 --- a/packages/idempotency/src/types/IdempotencyOptions.ts +++ b/packages/idempotency/src/types/IdempotencyOptions.ts @@ -1,7 +1,7 @@ import type { Context } from 'aws-lambda'; -import { BasePersistenceLayer } from '../persistence/BasePersistenceLayer'; -import { IdempotencyConfig } from '../IdempotencyConfig'; -import type { JSONValue } from '@aws-lambda-powertools/commons'; +import type { BasePersistenceLayer } from '../persistence/BasePersistenceLayer.js'; +import type { IdempotencyConfig } from '../IdempotencyConfig.js'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; /** * Configuration options for the idempotency utility. @@ -179,7 +179,7 @@ type IdempotencyConfigOptions = { lambdaContext?: Context; }; -export { +export type { AnyFunction, IdempotencyConfigOptions, ItempotentFunctionOptions, diff --git a/packages/idempotency/src/types/IdempotencyRecord.ts b/packages/idempotency/src/types/IdempotencyRecord.ts index 251d9c3c45..486cb2561d 100644 --- a/packages/idempotency/src/types/IdempotencyRecord.ts +++ b/packages/idempotency/src/types/IdempotencyRecord.ts @@ -1,5 +1,5 @@ -import type { JSONValue } from '@aws-lambda-powertools/commons'; -import { IdempotencyRecordStatus } from '../constants'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; +import { IdempotencyRecordStatus } from '../constants.js'; type IdempotencyRecordStatusValue = (typeof IdempotencyRecordStatus)[keyof typeof IdempotencyRecordStatus]; @@ -13,4 +13,4 @@ type IdempotencyRecordOptions = { payloadHash?: string; }; -export { IdempotencyRecordStatusValue, IdempotencyRecordOptions }; +export type { IdempotencyRecordStatusValue, IdempotencyRecordOptions }; diff --git a/packages/idempotency/src/types/LRUCache.ts b/packages/idempotency/src/types/LRUCache.ts index 6ab31d260a..67ab035e60 100644 --- a/packages/idempotency/src/types/LRUCache.ts +++ b/packages/idempotency/src/types/LRUCache.ts @@ -5,4 +5,4 @@ type LRUCacheOptions = { maxSize: number; }; -export { LRUCacheOptions }; +export type { LRUCacheOptions }; diff --git a/packages/idempotency/src/types/index.ts b/packages/idempotency/src/types/index.ts index 741a6fa409..3f7d50ea34 100644 --- a/packages/idempotency/src/types/index.ts +++ b/packages/idempotency/src/types/index.ts @@ -1,5 +1,13 @@ -export * from './IdempotencyRecord'; -export * from './BasePersistenceLayer'; -export * from './IdempotencyOptions'; -export * from './DynamoDBPersistence'; -export * from './LRUCache'; +export type { + IdempotencyRecordOptions, + IdempotencyRecordStatusValue, +} from './IdempotencyRecord.js'; +export type { + BasePersistenceLayerInterface, + BasePersistenceLayerOptions, +} from './BasePersistenceLayer.js'; +export type { + IdempotencyConfigOptions, + IdempotencyLambdaHandlerOptions, + IdempotencyHandlerOptions, +} from './IdempotencyOptions.js'; diff --git a/packages/idempotency/tests/e2e/idempotentDecorator.test.FunctionCode.ts b/packages/idempotency/tests/e2e/idempotentDecorator.test.FunctionCode.ts index 8dd29c0b29..f55b445f8b 100644 --- a/packages/idempotency/tests/e2e/idempotentDecorator.test.FunctionCode.ts +++ b/packages/idempotency/tests/e2e/idempotentDecorator.test.FunctionCode.ts @@ -1,9 +1,9 @@ import type { Context } from 'aws-lambda'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; -import { idempotent } from '../../src'; -import { Logger } from '../../../logger'; -import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer'; -import { IdempotencyConfig } from '../../src/'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { idempotent } from '../../src/idempotencyDecorator'; +import { Logger } from '@aws-lambda-powertools/logger'; +import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer.js'; +import { IdempotencyConfig } from '../../src/IdempotencyConfig.js'; const IDEMPOTENCY_TABLE_NAME = process.env.IDEMPOTENCY_TABLE_NAME || 'table_name'; diff --git a/packages/idempotency/tests/e2e/idempotentDecorator.test.ts b/packages/idempotency/tests/e2e/idempotentDecorator.test.ts index ec740e0f03..83a863afaa 100644 --- a/packages/idempotency/tests/e2e/idempotentDecorator.test.ts +++ b/packages/idempotency/tests/e2e/idempotentDecorator.test.ts @@ -1,7 +1,7 @@ /** * Test idempotency decorator * - * @group e2e/idempotency + * @group e2e/idempotency/decorator */ import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { @@ -9,7 +9,7 @@ import { SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; import { ScanCommand } from '@aws-sdk/lib-dynamodb'; import { createHash } from 'node:crypto'; import { @@ -17,7 +17,7 @@ import { TestInvocationLogs, TestStack, } from '@aws-lambda-powertools/testing-utils'; -import { IdempotencyTestNodejsFunctionAndDynamoTable } from '../helpers/resources'; +import { IdempotencyTestNodejsFunctionAndDynamoTable } from '../helpers/resources.js'; import { join } from 'node:path'; import { Duration } from 'aws-cdk-lib'; import { AttributeType } from 'aws-cdk-lib/aws-dynamodb'; diff --git a/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.FunctionCode.ts b/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.FunctionCode.ts index 499eda7cca..b4aa178d56 100644 --- a/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.FunctionCode.ts +++ b/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.FunctionCode.ts @@ -1,7 +1,7 @@ import type { Context } from 'aws-lambda'; -import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer'; -import { makeHandlerIdempotent } from '../../src/middleware'; -import { IdempotencyConfig } from '../../src'; +import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer.js'; +import { makeHandlerIdempotent } from '../../src/middleware/makeHandlerIdempotent.js'; +import { IdempotencyConfig } from '../../src/IdempotencyConfig.js'; import { Logger } from '@aws-lambda-powertools/logger'; import middy from '@middy/core'; diff --git a/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts b/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts index 1af54e589d..bf67669ab6 100644 --- a/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts +++ b/packages/idempotency/tests/e2e/makeHandlerIdempotent.test.ts @@ -13,13 +13,13 @@ import { ScanCommand } from '@aws-sdk/lib-dynamodb'; import { Duration } from 'aws-cdk-lib'; import { createHash } from 'node:crypto'; import { join } from 'node:path'; -import { IdempotencyTestNodejsFunctionAndDynamoTable } from '../helpers/resources'; +import { IdempotencyTestNodejsFunctionAndDynamoTable } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; const ddb = new DynamoDBClient({}); diff --git a/packages/idempotency/tests/e2e/makeIdempotent.test.FunctionCode.ts b/packages/idempotency/tests/e2e/makeIdempotent.test.FunctionCode.ts index 9786ddea0e..c627184721 100644 --- a/packages/idempotency/tests/e2e/makeIdempotent.test.FunctionCode.ts +++ b/packages/idempotency/tests/e2e/makeIdempotent.test.FunctionCode.ts @@ -1,8 +1,8 @@ import type { Context } from 'aws-lambda'; -import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer'; -import { makeIdempotent } from '../../src'; +import { DynamoDBPersistenceLayer } from '../../src/persistence/DynamoDBPersistenceLayer.js'; +import { makeIdempotent } from '../../src/makeIdempotent.js'; import { Logger } from '@aws-lambda-powertools/logger'; -import { IdempotencyConfig } from '../../src'; +import { IdempotencyConfig } from '../../src/IdempotencyConfig.js'; const IDEMPOTENCY_TABLE_NAME = process.env.IDEMPOTENCY_TABLE_NAME || 'table_name'; diff --git a/packages/idempotency/tests/e2e/makeIdempotent.test.ts b/packages/idempotency/tests/e2e/makeIdempotent.test.ts index ff7c890f59..63da32a705 100644 --- a/packages/idempotency/tests/e2e/makeIdempotent.test.ts +++ b/packages/idempotency/tests/e2e/makeIdempotent.test.ts @@ -13,7 +13,7 @@ import { ScanCommand } from '@aws-sdk/lib-dynamodb'; import { AttributeType } from 'aws-cdk-lib/aws-dynamodb'; import { createHash } from 'node:crypto'; import { join } from 'node:path'; -import { IdempotencyTestNodejsFunctionAndDynamoTable } from '../helpers/resources'; +import { IdempotencyTestNodejsFunctionAndDynamoTable } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, diff --git a/packages/idempotency/tests/helpers/idempotencyUtils.ts b/packages/idempotency/tests/helpers/idempotencyUtils.ts index efda071436..8b8eb04f09 100644 --- a/packages/idempotency/tests/helpers/idempotencyUtils.ts +++ b/packages/idempotency/tests/helpers/idempotencyUtils.ts @@ -1,4 +1,4 @@ -import { BasePersistenceLayer } from '../../src/persistence'; +import { BasePersistenceLayer } from '../../src/persistence/BasePersistenceLayer.js'; /** * Dummy class to test the abstract class BasePersistenceLayer. diff --git a/packages/idempotency/tests/helpers/resources.ts b/packages/idempotency/tests/helpers/resources.ts index 75bc4276ed..3a732f5ba0 100644 --- a/packages/idempotency/tests/helpers/resources.ts +++ b/packages/idempotency/tests/helpers/resources.ts @@ -1,14 +1,14 @@ +import { + type TestStack, + concatenateResourceName, +} from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; +import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; import type { ExtraTestProps, TestDynamodbTableProps, TestNodejsFunctionProps, - TestStack, -} from '@aws-lambda-powertools/testing-utils'; -import { - concatenateResourceName, - TestDynamodbTable, - TestNodejsFunction, -} from '@aws-lambda-powertools/testing-utils'; +} from '@aws-lambda-powertools/testing-utils/types'; import { Construct } from 'constructs'; import { randomUUID } from 'node:crypto'; diff --git a/packages/idempotency/tests/unit/config/EnvironmentVariableService.test.ts b/packages/idempotency/tests/unit/EnvironmentVariableService.test.ts similarity index 92% rename from packages/idempotency/tests/unit/config/EnvironmentVariableService.test.ts rename to packages/idempotency/tests/unit/EnvironmentVariableService.test.ts index 85f44eb60a..a7176e7897 100644 --- a/packages/idempotency/tests/unit/config/EnvironmentVariableService.test.ts +++ b/packages/idempotency/tests/unit/EnvironmentVariableService.test.ts @@ -3,7 +3,7 @@ * * @group unit/idempotency/environment-variables-service */ -import { EnvironmentVariablesService } from '../../../src/config'; +import { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; describe('Class: EnvironmentVariableService', () => { const ENVIRONMENT_VARIABLES = process.env; diff --git a/packages/idempotency/tests/unit/IdempotencyConfig.test.ts b/packages/idempotency/tests/unit/IdempotencyConfig.test.ts index 17b6252947..35551bc6d8 100644 --- a/packages/idempotency/tests/unit/IdempotencyConfig.test.ts +++ b/packages/idempotency/tests/unit/IdempotencyConfig.test.ts @@ -3,13 +3,12 @@ * * @group unit/idempotency/config */ -import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons'; -import { IdempotencyConfig } from '../../src'; -import type { IdempotencyConfigOptions } from '../../src/types'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import { IdempotencyConfig } from '../../src/index.js'; +import type { IdempotencyConfigOptions } from '../../src/types/index.js'; describe('Class: IdempotencyConfig', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext.helloworldContext; beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/idempotency/tests/unit/IdempotencyHandler.test.ts b/packages/idempotency/tests/unit/IdempotencyHandler.test.ts index 1ebb14e0be..d9e4e0deb7 100644 --- a/packages/idempotency/tests/unit/IdempotencyHandler.test.ts +++ b/packages/idempotency/tests/unit/IdempotencyHandler.test.ts @@ -3,17 +3,17 @@ * * @group unit/idempotency/IdempotencyHandler */ +import { IdempotencyRecord } from '../../src/persistence/index.js'; +import { IdempotencyHandler } from '../../src/IdempotencyHandler.js'; import { + IdempotencyConfig, IdempotencyAlreadyInProgressError, IdempotencyInconsistentStateError, IdempotencyItemAlreadyExistsError, IdempotencyPersistenceLayerError, -} from '../../src/errors'; -import { IdempotencyRecord } from '../../src/persistence'; -import { IdempotencyHandler } from '../../src/IdempotencyHandler'; -import { IdempotencyConfig } from '../../src/'; -import { MAX_RETRIES, IdempotencyRecordStatus } from '../../src/constants'; -import { PersistenceLayerTestClass } from '../helpers/idempotencyUtils'; +} from '../../src/index.js'; +import { MAX_RETRIES, IdempotencyRecordStatus } from '../../src/constants.js'; +import { PersistenceLayerTestClass } from '../helpers/idempotencyUtils.js'; const mockFunctionToMakeIdempotent = jest.fn(); const mockFunctionPayloadToBeHashed = {}; diff --git a/packages/idempotency/tests/unit/idempotencyDecorator.test.ts b/packages/idempotency/tests/unit/idempotencyDecorator.test.ts index 1a293d8569..ebd8caacd6 100644 --- a/packages/idempotency/tests/unit/idempotencyDecorator.test.ts +++ b/packages/idempotency/tests/unit/idempotencyDecorator.test.ts @@ -3,20 +3,22 @@ * * @group unit/idempotency/decorator */ - -import { BasePersistenceLayer, IdempotencyRecord } from '../../src/persistence'; -import { idempotent } from '../../src/'; -import type { IdempotencyRecordOptions } from '../../src/types'; import { + BasePersistenceLayer, + IdempotencyRecord, +} from '../../src/persistence/index.js'; +import { + idempotent, + IdempotencyConfig, IdempotencyAlreadyInProgressError, IdempotencyInconsistentStateError, IdempotencyItemAlreadyExistsError, IdempotencyPersistenceLayerError, -} from '../../src/errors'; -import { IdempotencyConfig } from '../../src'; +} from '../../src/index.js'; +import type { IdempotencyRecordOptions } from '../../src/types/index.js'; import { Context } from 'aws-lambda'; -import { helloworldContext } from '@aws-lambda-powertools/commons/lib/samples/resources/contexts'; -import { IdempotencyRecordStatus } from '../../src/constants'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import { IdempotencyRecordStatus } from '../../src/constants.js'; const mockSaveInProgress = jest .spyOn(BasePersistenceLayer.prototype, 'saveInProgress') @@ -28,8 +30,6 @@ const mockGetRecord = jest .spyOn(BasePersistenceLayer.prototype, 'getRecord') .mockImplementation(); -const dummyContext = helloworldContext; - const mockConfig: IdempotencyConfig = new IdempotencyConfig({}); class PersistenceLayerTestClass extends BasePersistenceLayer { @@ -45,7 +45,10 @@ class TestinClassWithLambdaHandler { @idempotent({ persistenceStore: new PersistenceLayerTestClass(), }) - public testing(record: Record, _context: Context): string { + public async testing( + record: Record, + _context: Context + ): Promise { functionalityToDecorate(record); return 'Hi'; @@ -53,7 +56,10 @@ class TestinClassWithLambdaHandler { } class TestingClassWithFunctionDecorator { - public handler(record: Record, context: Context): string { + public async handler( + record: Record, + context: Context + ): Promise { mockConfig.registerLambdaContext(context); return this.proccessRecord(record, 'bar'); @@ -64,7 +70,10 @@ class TestingClassWithFunctionDecorator { config: mockConfig, dataIndexArgument: 0, }) - public proccessRecord(record: Record, _foo: string): string { + public async proccessRecord( + record: Record, + _foo: string + ): Promise { functionalityToDecorate(record); return 'Processed Record'; @@ -81,42 +90,45 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = describe('When wrapping a function with no previous executions', () => { beforeEach(async () => { - await classWithFunctionDecorator.handler(inputRecord, dummyContext); + await classWithFunctionDecorator.handler(inputRecord, context); }); test('Then it will save the record to INPROGRESS', () => { - expect(mockSaveInProgress).toBeCalledWith( + expect(mockSaveInProgress).toHaveBeenCalledWith( inputRecord, - dummyContext.getRemainingTimeInMillis() + context.getRemainingTimeInMillis() ); }); test('Then it will call the function that was decorated', () => { - expect(functionalityToDecorate).toBeCalledWith(inputRecord); + expect(functionalityToDecorate).toHaveBeenCalledWith(inputRecord); }); test('Then it will save the record to COMPLETED with function return value', () => { - expect(mockSaveSuccess).toBeCalledWith(inputRecord, 'Processed Record'); + expect(mockSaveSuccess).toHaveBeenCalledWith( + inputRecord, + 'Processed Record' + ); }); }); describe('When wrapping a handler function with no previous executions', () => { beforeEach(async () => { - await classWithLambdaHandler.testing(inputRecord, dummyContext); + await classWithLambdaHandler.testing(inputRecord, context); }); test('Then it will save the record to INPROGRESS', () => { - expect(mockSaveInProgress).toBeCalledWith( + expect(mockSaveInProgress).toHaveBeenCalledWith( inputRecord, - dummyContext.getRemainingTimeInMillis() + context.getRemainingTimeInMillis() ); }); test('Then it will call the function that was decorated', () => { - expect(functionalityToDecorate).toBeCalledWith(inputRecord); + expect(functionalityToDecorate).toHaveBeenCalledWith(inputRecord); }); test('Then it will save the record to COMPLETED with function return value', () => { - expect(mockSaveSuccess).toBeCalledWith(inputRecord, 'Hi'); + expect(mockSaveSuccess).toHaveBeenCalledWith(inputRecord, 'Hi'); }); }); @@ -134,25 +146,25 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = new IdempotencyRecord(idempotencyOptions) ); try { - await classWithLambdaHandler.testing(inputRecord, dummyContext); + await classWithLambdaHandler.testing(inputRecord, context); } catch (e) { resultingError = e as Error; } }); test('Then it will attempt to save the record to INPROGRESS', () => { - expect(mockSaveInProgress).toBeCalledWith( + expect(mockSaveInProgress).toHaveBeenCalledWith( inputRecord, - dummyContext.getRemainingTimeInMillis() + context.getRemainingTimeInMillis() ); }); test('Then it will get the previous execution record', () => { - expect(mockGetRecord).toBeCalledWith(inputRecord); + expect(mockGetRecord).toHaveBeenCalledWith(inputRecord); }); test('Then it will not call the function that was decorated', () => { - expect(functionalityToDecorate).not.toBeCalled(); + expect(functionalityToDecorate).not.toHaveBeenCalled(); }); test('Then an IdempotencyAlreadyInProgressError is thrown', () => { @@ -174,25 +186,25 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = new IdempotencyRecord(idempotencyOptions) ); try { - await classWithLambdaHandler.testing(inputRecord, dummyContext); + await classWithLambdaHandler.testing(inputRecord, context); } catch (e) { resultingError = e as Error; } }); test('Then it will attempt to save the record to INPROGRESS', () => { - expect(mockSaveInProgress).toBeCalledWith( + expect(mockSaveInProgress).toHaveBeenCalledWith( inputRecord, - dummyContext.getRemainingTimeInMillis() + context.getRemainingTimeInMillis() ); }); test('Then it will get the previous execution record', () => { - expect(mockGetRecord).toBeCalledWith(inputRecord); + expect(mockGetRecord).toHaveBeenCalledWith(inputRecord); }); test('Then it will not call the function that was decorated', () => { - expect(functionalityToDecorate).not.toBeCalled(); + expect(functionalityToDecorate).not.toHaveBeenCalled(); }); test('Then an IdempotencyInconsistentStateError is thrown', () => { @@ -214,22 +226,22 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = mockGetRecord.mockResolvedValue( new IdempotencyRecord(idempotencyOptions) ); - await classWithLambdaHandler.testing(inputRecord, dummyContext); + await classWithLambdaHandler.testing(inputRecord, context); }); test('Then it will attempt to save the record to INPROGRESS', () => { - expect(mockSaveInProgress).toBeCalledWith( + expect(mockSaveInProgress).toHaveBeenCalledWith( inputRecord, - dummyContext.getRemainingTimeInMillis() + context.getRemainingTimeInMillis() ); }); test('Then it will get the previous execution record', () => { - expect(mockGetRecord).toBeCalledWith(inputRecord); + expect(mockGetRecord).toHaveBeenCalledWith(inputRecord); }); test('Then it will not call decorated functionality', () => { - expect(functionalityToDecorate).not.toBeCalledWith(inputRecord); + expect(functionalityToDecorate).not.toHaveBeenCalledWith(inputRecord); }); }); @@ -237,7 +249,7 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = class TestinClassWithLambdaHandlerWithConfig { @idempotent({ persistenceStore: new PersistenceLayerTestClass(), - config: new IdempotencyConfig({ lambdaContext: dummyContext }), + config: new IdempotencyConfig({ lambdaContext: context }), }) public testing(record: Record): string { functionalityToDecorate(record); @@ -259,9 +271,9 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = }); test('Then it will attempt to save the record to INPROGRESS', () => { - expect(mockSaveInProgress).toBeCalledWith( + expect(mockSaveInProgress).toHaveBeenCalledWith( inputRecord, - dummyContext.getRemainingTimeInMillis() + context.getRemainingTimeInMillis() ); }); @@ -276,12 +288,12 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = class TestingClassWithIdempotencyDisabled { @idempotent({ persistenceStore: new PersistenceLayerTestClass(), - config: new IdempotencyConfig({ lambdaContext: dummyContext }), + config: new IdempotencyConfig({ lambdaContext: context }), }) - public testing( + public async testing( record: Record, _context: Context - ): string { + ): Promise { functionalityToDecorate(record); return 'Hi'; @@ -289,12 +301,12 @@ describe('Given a class with a function to decorate', (classWithLambdaHandler = } const classWithoutIdempotencyDisabled = new TestingClassWithIdempotencyDisabled(); - await classWithoutIdempotencyDisabled.testing(inputRecord, dummyContext); + await classWithoutIdempotencyDisabled.testing(inputRecord, context); }); test('Then it will skip ipdemotency', async () => { - expect(mockSaveInProgress).not.toBeCalled(); - expect(mockSaveSuccess).not.toBeCalled(); + expect(mockSaveInProgress).not.toHaveBeenCalled(); + expect(mockSaveSuccess).not.toHaveBeenCalled(); }); afterAll(() => { diff --git a/packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts b/packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts index db064e6f38..32854e1bb4 100644 --- a/packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts +++ b/packages/idempotency/tests/unit/makeHandlerIdempotent.test.ts @@ -3,19 +3,19 @@ * * @group unit/idempotency/makeHandlerIdempotent */ -import { makeHandlerIdempotent } from '../../src/middleware'; -import { helloworldContext as dummyContext } from '@aws-lambda-powertools/commons/lib/samples/resources/contexts'; -import { Custom as dummyEvent } from '@aws-lambda-powertools/commons/lib/samples/resources/events'; -import { IdempotencyRecord } from '../../src/persistence'; +import { makeHandlerIdempotent } from '../../src/middleware/makeHandlerIdempotent.js'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import { IdempotencyRecord } from '../../src/persistence/index.js'; import { IdempotencyInconsistentStateError, IdempotencyItemAlreadyExistsError, IdempotencyPersistenceLayerError, -} from '../../src/errors'; -import { IdempotencyConfig } from '../../src/'; + IdempotencyConfig, + IdempotencyRecordStatus, +} from '../../src/index.js'; import middy from '@middy/core'; -import { MAX_RETRIES, IdempotencyRecordStatus } from '../../src/constants'; -import { PersistenceLayerTestClass } from '../helpers/idempotencyUtils'; +import { MAX_RETRIES } from '../../src/constants.js'; +import { PersistenceLayerTestClass } from '../helpers/idempotencyUtils.js'; import type { Context } from 'aws-lambda'; const mockIdempotencyOptions = { @@ -25,8 +25,10 @@ const remainingTImeInMillis = 1234; describe('Middleware: makeHandlerIdempotent', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext; - const event = dummyEvent.CustomEvent; + const event = { + foo: 'bar', + bar: 'baz', + }; beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/idempotency/tests/unit/makeIdempotent.test.ts b/packages/idempotency/tests/unit/makeIdempotent.test.ts index d46bc7ad6b..40e5a3605b 100644 --- a/packages/idempotency/tests/unit/makeIdempotent.test.ts +++ b/packages/idempotency/tests/unit/makeIdempotent.test.ts @@ -3,18 +3,18 @@ * * @group unit/idempotency/makeIdempotent */ -import { IdempotencyRecord } from '../../src/persistence'; -import { makeIdempotent } from '../../src'; +import { IdempotencyRecord } from '../../src/persistence/index.js'; import { + makeIdempotent, IdempotencyInconsistentStateError, IdempotencyItemAlreadyExistsError, IdempotencyPersistenceLayerError, -} from '../../src/errors'; -import { IdempotencyConfig } from '../../src'; -import { helloworldContext as dummyContext } from '@aws-lambda-powertools/commons/lib/samples/resources/contexts'; -import { Custom as dummyEvent } from '@aws-lambda-powertools/commons/lib/samples/resources/events'; -import { MAX_RETRIES, IdempotencyRecordStatus } from '../../src/constants'; -import { PersistenceLayerTestClass } from '../helpers/idempotencyUtils'; + IdempotencyConfig, + IdempotencyRecordStatus, +} from '../../src/index.js'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import { MAX_RETRIES } from '../../src/constants.js'; +import { PersistenceLayerTestClass } from '../helpers/idempotencyUtils.js'; import type { Context } from 'aws-lambda'; const mockIdempotencyOptions = { @@ -24,8 +24,10 @@ const remainingTImeInMillis = 1234; describe('Function: makeIdempotent', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext; - const event = dummyEvent.CustomEvent; + const event = { + foo: 'bar', + bar: 'baz', + }; beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts b/packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts index cb1943237e..a245a1f582 100644 --- a/packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts +++ b/packages/idempotency/tests/unit/persistence/BasePersistenceLayer.test.ts @@ -4,17 +4,19 @@ * @group unit/idempotency/persistence/base */ import { createHash } from 'node:crypto'; -import { ContextExamples as dummyContext } from '@aws-lambda-powertools/commons'; -import { IdempotencyConfig, IdempotencyRecordStatus } from '../../../src'; -import { - BasePersistenceLayer, - IdempotencyRecord, -} from '../../../src/persistence'; +import context from '@aws-lambda-powertools/testing-utils/context'; import { + IdempotencyConfig, + IdempotencyRecordStatus, IdempotencyItemAlreadyExistsError, IdempotencyValidationError, -} from '../../../src/errors'; -import type { IdempotencyConfigOptions } from '../../../src/types'; + IdempotencyKeyError, +} from '../../../src/index.js'; +import { + BasePersistenceLayer, + IdempotencyRecord, +} from '../../../src/persistence/index.js'; +import type { IdempotencyConfigOptions } from '../../../src/types/index.js'; jest.mock('node:crypto', () => ({ createHash: jest.fn().mockReturnValue({ @@ -25,7 +27,6 @@ jest.mock('node:crypto', () => ({ describe('Class: BasePersistenceLayer', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext.helloworldContext; class PersistenceLayerTestClass extends BasePersistenceLayer { public _deleteRecord = jest.fn(); @@ -297,7 +298,9 @@ describe('Class: BasePersistenceLayer', () => { await expect( persistenceLayer.getRecord({ foo: { bar: [] } }) ).rejects.toThrow( - new Error('No data found to create a hashed idempotency_key') + new IdempotencyKeyError( + 'No data found to create a hashed idempotency_key' + ) ); }); diff --git a/packages/idempotency/tests/unit/persistence/DynamoDbPersistenceLayer.test.ts b/packages/idempotency/tests/unit/persistence/DynamoDbPersistenceLayer.test.ts index 4a0195c19e..29e8930161 100644 --- a/packages/idempotency/tests/unit/persistence/DynamoDbPersistenceLayer.test.ts +++ b/packages/idempotency/tests/unit/persistence/DynamoDbPersistenceLayer.test.ts @@ -3,14 +3,14 @@ * * @group unit/idempotency/persistence/dynamodb */ -import { DynamoDBPersistenceLayer } from '../../../src/persistence/DynamoDBPersistenceLayer'; +import { DynamoDBPersistenceLayer } from '../../../src/persistence/DynamoDBPersistenceLayer.js'; +import { IdempotencyRecord } from '../../../src/persistence/index.js'; +import type { DynamoDBPersistenceOptions } from '../../../src/types/DynamoDBPersistence.js'; import { + IdempotencyRecordStatus, IdempotencyItemAlreadyExistsError, IdempotencyItemNotFoundError, -} from '../../../src/errors'; -import { IdempotencyRecord } from '../../../src/persistence'; -import type { DynamoDBPersistenceOptions } from '../../../src/types'; -import { IdempotencyRecordStatus } from '../../../src'; +} from '../../../src/index.js'; import { ConditionalCheckFailedException, DynamoDBClient, diff --git a/packages/idempotency/tests/unit/persistence/IdempotencyRecord.test.ts b/packages/idempotency/tests/unit/persistence/IdempotencyRecord.test.ts index 00776ec246..38728bcef9 100644 --- a/packages/idempotency/tests/unit/persistence/IdempotencyRecord.test.ts +++ b/packages/idempotency/tests/unit/persistence/IdempotencyRecord.test.ts @@ -3,10 +3,12 @@ * * @group unit/idempotency/persistence/idempotencyRecord */ -import { IdempotencyInvalidStatusError } from '../../../src/errors'; -import { IdempotencyRecord } from '../../../src/persistence'; -import { IdempotencyRecordStatus } from '../../../src'; -import type { IdempotencyRecordStatusValue } from '../../../src/types'; +import { IdempotencyRecord } from '../../../src/persistence/IdempotencyRecord.js'; +import { + IdempotencyRecordStatus, + IdempotencyInvalidStatusError, +} from '../../../src/index.js'; +import type { IdempotencyRecordStatusValue } from '../../../src/types/index.js'; const mockIdempotencyKey = '123'; const mockData = undefined; diff --git a/packages/idempotency/tests/unit/persistence/LRUCache.test.ts b/packages/idempotency/tests/unit/persistence/LRUCache.test.ts index 83a4e445ff..5732c31a45 100644 --- a/packages/idempotency/tests/unit/persistence/LRUCache.test.ts +++ b/packages/idempotency/tests/unit/persistence/LRUCache.test.ts @@ -3,7 +3,7 @@ * * @group unit/idempotency/persistence/lru-cache */ -import { LRUCache } from '../../../src/persistence/LRUCache'; +import { LRUCache } from '../../../src/persistence/LRUCache.js'; describe('Class: LRUMap', () => { describe('Method: add', () => { diff --git a/packages/idempotency/tsconfig.esm.json b/packages/idempotency/tsconfig.esm.json new file mode 100644 index 0000000000..123291b0cf --- /dev/null +++ b/packages/idempotency/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/esm.json" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/idempotency/tsconfig.json b/packages/idempotency/tsconfig.json index 1cb9d72773..f216927295 100644 --- a/packages/idempotency/tsconfig.json +++ b/packages/idempotency/tsconfig.json @@ -1,8 +1,9 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./lib", + "outDir": "./lib/cjs", "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" }, "include": [ "./src/**/*" diff --git a/packages/logger/README.md b/packages/logger/README.md index 98f0df68a6..e457d14357 100644 --- a/packages/logger/README.md +++ b/packages/logger/README.md @@ -118,4 +118,4 @@ Credits for the Powertools for AWS Lambda (TypeScript) idea go to [DAZN](https:/ ## License -This library is licensed under the MIT-0 License. See the LICENSE file. +This library is licensed under the MIT-0 License. See the LICENSE file. \ No newline at end of file diff --git a/packages/logger/jest.config.js b/packages/logger/jest.config.cjs similarity index 84% rename from packages/logger/jest.config.js rename to packages/logger/jest.config.cjs index 31c2a6a370..deb2a416a5 100644 --- a/packages/logger/jest.config.js +++ b/packages/logger/jest.config.cjs @@ -4,7 +4,9 @@ module.exports = { color: 'cyan', }, runner: 'groups', - preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, @@ -14,7 +16,7 @@ module.exports = { roots: ['/src', '/tests'], testPathIgnorePatterns: ['/node_modules/'], testEnvironment: 'node', - coveragePathIgnorePatterns: ['/node_modules/', '/types/'], + coveragePathIgnorePatterns: ['/node_modules/', 'src/types/index.ts'], coverageThreshold: { global: { statements: 100, diff --git a/packages/logger/package.json b/packages/logger/package.json index 271d038c09..7b2c46ee79 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -13,16 +13,16 @@ "test": "npm run test:unit", "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", "jest": "jest --detectOpenHandles --verbose", - "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e", "test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e", "test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e", "test:e2e": "jest --group=e2e", "watch": "jest --watch --group=unit", - "build": "tsc --build --force", + "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", - "prebuild": "rimraf ./lib", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { @@ -30,8 +30,41 @@ }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/logger#readme", "license": "MIT-0", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } + }, + "./middleware": { + "import": "./lib/esm/middleware/middy.js", + "require": "./lib/cjs/middleware/middy.js" + }, + "./types": { + "import": "./lib/esm/types/index.js", + "require": "./lib/cjs/types/index.js" + } + }, + "typesVersions": { + "*": { + "middleware": [ + "lib/cjs/middleware/middy.d.ts", + "lib/esm/middleware/middy.d.ts" + ], + "types": [ + "lib/cjs/types/index.d.ts", + "lib/esm/types/index.d.ts" + ] + } + }, + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "devDependencies": { "@aws-lambda-powertools/testing-utils": "file:../testing", "@types/lodash.merge": "^4.6.9" @@ -67,4 +100,4 @@ "serverless", "nodejs" ] -} +} \ No newline at end of file diff --git a/packages/logger/src/Logger.ts b/packages/logger/src/Logger.ts index 7f7338bc4d..3a8859e447 100644 --- a/packages/logger/src/Logger.ts +++ b/packages/logger/src/Logger.ts @@ -1,27 +1,31 @@ -import { randomInt } from 'node:crypto'; -import { Console } from 'node:console'; -import { format } from 'node:util'; -import type { Context, Handler } from 'aws-lambda'; import { Utility } from '@aws-lambda-powertools/commons'; -import { LogFormatterInterface, PowertoolLogFormatter } from './formatter'; -import { LogItem } from './log'; +import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types'; +import type { Context, Handler } from 'aws-lambda'; import merge from 'lodash.merge'; -import { ConfigServiceInterface, EnvironmentVariablesService } from './config'; -import { LogJsonIndent } from './types'; +import { format } from 'node:util'; +import { Console } from 'node:console'; +import { randomInt } from 'node:crypto'; +import { EnvironmentVariablesService } from './config/EnvironmentVariablesService.js'; +import { LogJsonIndent } from './constants.js'; +import { LogItem } from './formatter/LogItem.js'; +import { PowertoolsLogFormatter } from './formatter/PowertoolsLogFormatter.js'; +import type { ConfigServiceInterface } from './types/ConfigServiceInterface.js'; import type { - ClassThatLogs, Environment, - HandlerMethodDecorator, - LambdaFunctionContext, LogAttributes, + LogLevel, + LogLevelThresholds, + LogFormatterInterface, +} from './types/Log.js'; +import type { + LogFunction, ConstructorOptions, + InjectLambdaContextOptions, LogItemExtraInput, LogItemMessage, - LogLevel, - LogLevelThresholds, - PowertoolLogData, - HandlerOptions, -} from './types'; + LoggerInterface, + PowertoolsLogData, +} from './types/Logger.js'; /** * ## Intro @@ -54,7 +58,8 @@ import type { * * @example * ```typescript - * import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger'; + * import { Logger } from '@aws-lambda-powertools/logger'; + * import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware'; * import middy from '@middy/core'; * * const logger = new Logger(); @@ -73,7 +78,7 @@ import type { * @example * ```typescript * import { Logger } from '@aws-lambda-powertools/logger'; - * import { LambdaInterface } from '@aws-lambda-powertools/commons'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; * * const logger = new Logger(); * @@ -109,7 +114,7 @@ import type { * @implements {ClassThatLogs} * @see https://docs.powertools.aws.dev/lambda/typescript/latest/core/logger/ */ -class Logger extends Utility implements ClassThatLogs { +class Logger extends Utility implements LoggerInterface { /** * Console instance used to print logs. * @@ -153,11 +158,9 @@ class Logger extends Utility implements ClassThatLogs { SILENT: 28, }; - private logsSampled = false; + private persistentLogAttributes: LogAttributes = {}; - private persistentLogAttributes?: LogAttributes = {}; - - private powertoolLogData: PowertoolLogData = {}; + private powertoolsLogData: PowertoolsLogData = {}; /** * Log level used by the current instance of Logger. @@ -187,17 +190,15 @@ class Logger extends Utility implements ClassThatLogs { * @returns {void} */ public addContext(context: Context): void { - const lambdaContext: Partial = { - invokedFunctionArn: context.invokedFunctionArn, - coldStart: this.getColdStart(), - awsRequestId: context.awsRequestId, - memoryLimitInMB: Number(context.memoryLimitInMB), - functionName: context.functionName, - functionVersion: context.functionVersion, - }; - - this.addToPowertoolLogData({ - lambdaContext, + this.addToPowertoolsLogData({ + lambdaContext: { + invokedFunctionArn: context.invokedFunctionArn, + coldStart: this.getColdStart(), + awsRequestId: context.awsRequestId, + memoryLimitInMB: context.memoryLimitInMB, + functionName: context.functionName, + functionVersion: context.functionVersion, + }, }); } @@ -229,22 +230,27 @@ class Logger extends Utility implements ClassThatLogs { * @returns {Logger} */ public createChild(options: ConstructorOptions = {}): Logger { - const parentsOptions = { - logLevel: this.getLevelName(), - customConfigService: this.getCustomConfigService(), - logFormatter: this.getLogFormatter(), - }; - const parentsPowertoolsLogData = this.getPowertoolLogData(); const childLogger = this.createLogger( - merge(parentsOptions, parentsPowertoolsLogData, options) + // Merge parent logger options with options passed to createChild, + // the latter having precedence. + merge( + {}, + { + logLevel: this.getLevelName(), + serviceName: this.powertoolsLogData.serviceName, + sampleRateValue: this.powertoolsLogData.sampleRateValue, + logFormatter: this.getLogFormatter(), + customConfigService: this.getCustomConfigService(), + environment: this.powertoolsLogData.environment, + persistentLogAttributes: this.persistentLogAttributes, + }, + options + ) ); - - const parentsPersistentLogAttributes = this.getPersistentLogAttributes(); - childLogger.addPersistentLogAttributes(parentsPersistentLogAttributes); - - if (parentsPowertoolsLogData.lambdaContext) { - childLogger.addContext(parentsPowertoolsLogData.lambdaContext as Context); - } + if (this.powertoolsLogData.lambdaContext) + childLogger.addContext( + this.powertoolsLogData.lambdaContext as unknown as Context + ); return childLogger; } @@ -306,15 +312,6 @@ class Logger extends Utility implements ClassThatLogs { return this.logEvent; } - /** - * It returns a boolean value, if true all the logs will be printed. - * - * @returns {boolean} - */ - public getLogsSampled(): boolean { - return this.logsSampled; - } - /** * It returns the persistent log attributes, which are the attributes * that will be logged in all log items. @@ -323,7 +320,7 @@ class Logger extends Utility implements ClassThatLogs { * @returns {LogAttributes} */ public getPersistentLogAttributes(): LogAttributes { - return this.persistentLogAttributes as LogAttributes; + return this.persistentLogAttributes; } /** @@ -350,7 +347,7 @@ class Logger extends Utility implements ClassThatLogs { * @example * ```typescript * import { Logger } from '@aws-lambda-powertools/logger'; - * import { LambdaInterface } from '@aws-lambda-powertools/commons'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; * * const logger = new Logger(); * @@ -369,7 +366,9 @@ class Logger extends Utility implements ClassThatLogs { * @see https://www.typescriptlang.org/docs/handbook/decorators.html#method-decorators * @returns {HandlerMethodDecorator} */ - public injectLambdaContext(options?: HandlerOptions): HandlerMethodDecorator { + public injectLambdaContext( + options?: InjectLambdaContextOptions + ): HandlerMethodDecorator { return (_target, _propertyKey, descriptor) => { /** * The descriptor.value is the method this decorator decorates, it cannot be undefined. @@ -417,7 +416,7 @@ class Logger extends Utility implements ClassThatLogs { public static injectLambdaContextAfterOrOnError( logger: Logger, initialPersistentAttributes: LogAttributes, - options?: HandlerOptions + options?: InjectLambdaContextOptions ): void { if (options && options.clearState === true) { logger.setPersistentLogAttributes(initialPersistentAttributes); @@ -428,7 +427,7 @@ class Logger extends Utility implements ClassThatLogs { logger: Logger, event: unknown, context: Context, - options?: HandlerOptions + options?: InjectLambdaContextOptions ): void { logger.addContext(context); @@ -447,22 +446,19 @@ class Logger extends Utility implements ClassThatLogs { * @returns {void} */ public logEventIfEnabled(event: unknown, overwriteValue?: boolean): void { - if (!this.shouldLogEvent(overwriteValue)) { - return; - } + if (!this.shouldLogEvent(overwriteValue)) return; this.info('Lambda invocation event', { event }); } /** - * If the sample rate feature is enabled, the calculation that determines whether the logs - * will actually be printed or not for this invocation is done when the Logger class is - * initialized. - * This method will repeat that calculation (with possible different outcome). + * This method allows recalculating the initial sampling decision for changing + * the log level to DEBUG based on a sample rate value used during initialization, + * potentially yielding a different outcome. * * @returns {void} */ public refreshSampleRateCalculation(): void { - this.setLogsSampled(); + this.setInitialSampleRate(this.powertoolsLogData.sampleRateValue); } /** @@ -482,11 +478,11 @@ class Logger extends Utility implements ClassThatLogs { * @returns {void} */ public removePersistentLogAttributes(keys: string[]): void { - keys.forEach((key) => { + for (const key of keys) { if (this.persistentLogAttributes && key in this.persistentLogAttributes) { delete this.persistentLogAttributes[key]; } - }); + } } /** @@ -517,19 +513,6 @@ class Logger extends Utility implements ClassThatLogs { this.persistentLogAttributes = attributes; } - /** - * It sets the user-provided sample rate value. - * - * @param {number} [sampleRateValue] - * @returns {void} - */ - public setSampleRateValue(sampleRateValue?: number): void { - this.powertoolLogData.sampleRateValue = - sampleRateValue || - this.getCustomConfigService()?.getSampleRateValue() || - this.getEnvVarsService().getSampleRateValue(); - } - /** * It checks whether the current Lambda invocation event should be printed in the logs or not. * @@ -557,49 +540,40 @@ class Logger extends Utility implements ClassThatLogs { } /** - * Creates a new Logger instance. + * Factory method for instantiating logger instances. Used by `createChild` method. + * Important for customization and subclassing. It allows subclasses, like `MyOwnLogger`, + * to override its behavior while keeping the main business logic in `createChild` intact. * - * @param {ConstructorOptions} [options] - * @returns {Logger} + * @example + * ```typescript + * // MyOwnLogger subclass + * class MyOwnLogger extends Logger { + * protected createLogger(options?: ConstructorOptions): MyOwnLogger { + * return new MyOwnLogger(options); + * } + * // No need to re-implement business logic from `createChild` and keep track on changes + * public createChild(options?: ConstructorOptions): MyOwnLogger { + * return super.createChild(options) as MyOwnLogger; + * } + * } + * ``` + * + * @param {ConstructorOptions} [options] Logger configuration options. + * @returns {Logger} A new logger instance. */ protected createLogger(options?: ConstructorOptions): Logger { return new Logger(options); } - /** - * Decides whether the current log item should be printed or not. - * - * The decision is based on the log level and the sample rate value. - * A log item will be printed if: - * 1. The log level is greater than or equal to the Logger's log level. - * 2. The log level is less than the Logger's log level, but the - * current sampling value is set to `true`. - * - * @param {number} logLevel - * @returns {boolean} - * @protected - */ - protected shouldPrint(logLevel: number): boolean { - if (logLevel >= this.logLevel) { - return true; - } - - return this.getLogsSampled(); - } - /** * It stores information that is printed in all log items. * - * @param {Partial} attributesArray + * @param {Partial} attributes * @private * @returns {void} */ - private addToPowertoolLogData( - ...attributesArray: Array> - ): void { - attributesArray.forEach((attributes: Partial) => { - merge(this.powertoolLogData, attributes); - }); + private addToPowertoolsLogData(attributes: Partial): void { + merge(this.powertoolsLogData, attributes); } private awsLogLevelShortCircuit(selectedLogLevel?: string): boolean { @@ -650,19 +624,16 @@ class Logger extends Utility implements ClassThatLogs { message: typeof input === 'string' ? input : input.message, xRayTraceId: this.envVarsService.getXrayTraceId(), }, - this.getPowertoolLogData() + this.getPowertoolsLogData() ); - const logItem = new LogItem({ - baseAttributes: this.getLogFormatter().formatAttributes( - unformattedBaseAttributes - ), - persistentAttributes: this.getPersistentLogAttributes(), - }); - - // Add ephemeral attributes + let additionalLogAttributes: LogAttributes = {}; + additionalLogAttributes = merge( + additionalLogAttributes, + this.getPersistentLogAttributes() + ); if (typeof input !== 'string') { - logItem.addAttributes(input); + additionalLogAttributes = merge(additionalLogAttributes, input); } extraInput.forEach((item: Error | LogAttributes | string) => { const attributes: LogAttributes = @@ -672,9 +643,14 @@ class Logger extends Utility implements ClassThatLogs { ? { extra: item } : item; - logItem.addAttributes(attributes); + additionalLogAttributes = merge(additionalLogAttributes, attributes); }); + const logItem = this.getLogFormatter().formatAttributes( + unformattedBaseAttributes, + additionalLogAttributes + ); + return logItem; } @@ -718,15 +694,15 @@ class Logger extends Utility implements ClassThatLogs { * @returns - The name of the log level */ private getLogLevelNameFromNumber(logLevel: number): Uppercase { - const found = Object.entries(this.logLevelThresholds).find( - ([key, value]) => { - if (value === logLevel) { - return key; - } + let found; + for (const [key, value] of Object.entries(this.logLevelThresholds)) { + if (value === logLevel) { + found = key; + break; } - )!; + } - return found[0] as Uppercase; + return found as Uppercase; } /** @@ -736,8 +712,8 @@ class Logger extends Utility implements ClassThatLogs { * @private * @returns {LogAttributes} */ - private getPowertoolLogData(): PowertoolLogData { - return this.powertoolLogData; + private getPowertoolsLogData(): PowertoolsLogData { + return this.powertoolsLogData; } /** @@ -774,20 +750,6 @@ class Logger extends Utility implements ClassThatLogs { }; } - /** - * It returns the numeric sample rate value. - * - * @private - * @returns {number} - */ - private getSampleRateValue(): number { - if (!this.powertoolLogData.sampleRateValue) { - this.setSampleRateValue(); - } - - return this.powertoolLogData.sampleRateValue as number; - } - /** * It returns true and type guards the log level if a given log level is valid. * @@ -801,6 +763,23 @@ class Logger extends Utility implements ClassThatLogs { return typeof logLevel === 'string' && logLevel in this.logLevelThresholds; } + /** + * It returns true and type guards the sample rate value if a given value is valid. + * + * @param sampleRateValue + * @private + * @returns {boolean} + */ + private isValidSampleRate( + sampleRateValue?: number + ): sampleRateValue is number { + return ( + typeof sampleRateValue === 'number' && + 0 <= sampleRateValue && + sampleRateValue <= 1 + ); + } + /** * It prints a given log with given log level. * @@ -815,7 +794,7 @@ class Logger extends Utility implements ClassThatLogs { logLevel === 24 ? 'error' : (this.getLogLevelNameFromNumber(logLevel).toLowerCase() as keyof Omit< - ClassThatLogs, + LogFunction, 'critical' >); @@ -841,13 +820,12 @@ class Logger extends Utility implements ClassThatLogs { input: LogItemMessage, extraInput: LogItemExtraInput ): void { - if (!this.shouldPrint(logLevel)) { - return; + if (logLevel >= this.logLevel) { + this.printLog( + logLevel, + this.createAndPopulateLogItem(logLevel, input, extraInput) + ); } - this.printLog( - logLevel, - this.createAndPopulateLogItem(logLevel, input, extraInput) - ); } /** @@ -933,6 +911,37 @@ class Logger extends Utility implements ClassThatLogs { } } + /** + * It sets sample rate value with the following prioprity: + * 1. Constructor value + * 2. Custom config service value + * 3. Environment variable value + * 4. Default value (zero) + * + * @private + * @param {number} [sampleRateValue] + * @returns {void} + */ + private setInitialSampleRate(sampleRateValue?: number): void { + this.powertoolsLogData.sampleRateValue = 0; + const constructorValue = sampleRateValue; + const customConfigValue = + this.getCustomConfigService()?.getSampleRateValue(); + const envVarsValue = this.getEnvVarsService().getSampleRateValue(); + for (const value of [constructorValue, customConfigValue, envVarsValue]) { + if (this.isValidSampleRate(value)) { + this.powertoolsLogData.sampleRateValue = value; + + if (value && randomInt(0, 100) / 100 <= value) { + this.setLogLevel('DEBUG'); + this.debug('Setting log level to DEBUG due to sampling rate'); + } + + return; + } + } + } + /** * If the log event feature is enabled via env variable, it sets a property that tracks whether * the event passed to the Lambda function handler should be logged or not. @@ -955,7 +964,9 @@ class Logger extends Utility implements ClassThatLogs { * @returns {void} */ private setLogFormatter(logFormatter?: LogFormatterInterface): void { - this.logFormatter = logFormatter || new PowertoolLogFormatter(); + this.logFormatter = + logFormatter ?? + new PowertoolsLogFormatter({ envVarsService: this.getEnvVarsService() }); } /** @@ -971,20 +982,6 @@ class Logger extends Utility implements ClassThatLogs { } } - /** - * If the sample rate feature is enabled, it sets a property that tracks whether this Lambda function invocation - * will print logs or not. - * - * @private - * @returns {void} - */ - private setLogsSampled(): void { - const sampleRateValue = this.getSampleRateValue(); - this.logsSampled = - sampleRateValue !== undefined && - (sampleRateValue === 1 || randomInt(0, 100) / 100 <= sampleRateValue); - } - /** * It configures the Logger instance settings that will affect the Logger's behaviour * and the content of all logs. @@ -1004,18 +1001,16 @@ class Logger extends Utility implements ClassThatLogs { environment, } = options; + // order is important, EnvVarsService() is used by other methods this.setEnvVarsService(); - // order is important, it uses EnvVarsService() this.setConsole(); this.setCustomConfigService(customConfigService); this.setInitialLogLevel(logLevel); - this.setSampleRateValue(sampleRateValue); - this.setLogsSampled(); this.setLogFormatter(logFormatter); - this.setPowertoolLogData(serviceName, environment); + this.setPowertoolsLogData(serviceName, environment); + this.setInitialSampleRate(sampleRateValue); this.setLogEvent(); this.setLogIndentation(); - this.addPersistentLogAttributes(persistentLogAttributes); return this; @@ -1030,27 +1025,24 @@ class Logger extends Utility implements ClassThatLogs { * @private * @returns {void} */ - private setPowertoolLogData( + private setPowertoolsLogData( serviceName?: string, environment?: Environment, persistentLogAttributes: LogAttributes = {} ): void { - this.addToPowertoolLogData( - { - awsRegion: this.getEnvVarsService().getAwsRegion(), - environment: - environment || - this.getCustomConfigService()?.getCurrentEnvironment() || - this.getEnvVarsService().getCurrentEnvironment(), - sampleRateValue: this.getSampleRateValue(), - serviceName: - serviceName || - this.getCustomConfigService()?.getServiceName() || - this.getEnvVarsService().getServiceName() || - this.getDefaultServiceName(), - }, - persistentLogAttributes - ); + this.addToPowertoolsLogData({ + awsRegion: this.getEnvVarsService().getAwsRegion(), + environment: + environment || + this.getCustomConfigService()?.getCurrentEnvironment() || + this.getEnvVarsService().getCurrentEnvironment(), + serviceName: + serviceName || + this.getCustomConfigService()?.getServiceName() || + this.getEnvVarsService().getServiceName() || + this.getDefaultServiceName(), + }); + this.addPersistentLogAttributes(persistentLogAttributes); } } diff --git a/packages/logger/src/config/EnvironmentVariablesService.ts b/packages/logger/src/config/EnvironmentVariablesService.ts index 47955efcb7..55188ee093 100644 --- a/packages/logger/src/config/EnvironmentVariablesService.ts +++ b/packages/logger/src/config/EnvironmentVariablesService.ts @@ -1,4 +1,4 @@ -import { ConfigServiceInterface } from './ConfigServiceInterface'; +import { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; import { EnvironmentVariablesService as CommonEnvironmentVariablesService } from '@aws-lambda-powertools/commons'; /** @@ -30,6 +30,7 @@ class EnvironmentVariablesService private logLevelVariableLegacy = 'LOG_LEVEL'; private memoryLimitInMBVariable = 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE'; private sampleRateValueVariable = 'POWERTOOLS_LOGGER_SAMPLE_RATE'; + private tzVariable = 'TZ'; /** * It returns the value of the `AWS_LAMBDA_LOG_LEVEL` environment variable. @@ -125,13 +126,24 @@ class EnvironmentVariablesService /** * It returns the value of the POWERTOOLS_LOGGER_SAMPLE_RATE environment variable. * - * @returns {string|undefined} + * @returns {number|undefined} */ public getSampleRateValue(): number | undefined { const value = this.get(this.sampleRateValueVariable); return value && value.length > 0 ? Number(value) : undefined; } + + /** + * It returns the value of the `TZ` environment variable or `UTC` if it is not set. + * + * @returns {string} + */ + public getTimezone(): string { + const value = this.get(this.tzVariable); + + return value.length > 0 ? value : 'UTC'; + } } export { EnvironmentVariablesService }; diff --git a/packages/logger/src/config/index.ts b/packages/logger/src/config/index.ts deleted file mode 100644 index 11fd37677e..0000000000 --- a/packages/logger/src/config/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ConfigServiceInterface'; -export * from './EnvironmentVariablesService'; diff --git a/packages/logger/src/constants.ts b/packages/logger/src/constants.ts new file mode 100644 index 0000000000..90a26baa24 --- /dev/null +++ b/packages/logger/src/constants.ts @@ -0,0 +1,17 @@ +/** + * The indent level for JSON logs. + * + * By default Logger will use the `LogJsonIndent.COMPACT` indent level, which + * produces logs on a single line. This is the most efficient option for + * CloudWatch Logs. + * + * When enabling the `POWERTOOLS_DEV` environment variable, Logger will use the + * `LogJsonIndent.PRETTY` indent level, which indents the JSON logs for easier + * reading. + */ +const LogJsonIndent = { + PRETTY: 4, + COMPACT: 0, +} as const; + +export { LogJsonIndent }; diff --git a/packages/logger/src/formatter/LogFormatter.ts b/packages/logger/src/formatter/LogFormatter.ts index 0a00fe55f8..dd874732c4 100644 --- a/packages/logger/src/formatter/LogFormatter.ts +++ b/packages/logger/src/formatter/LogFormatter.ts @@ -1,5 +1,11 @@ -import { LogFormatterInterface } from '.'; -import { LogAttributes, UnformattedAttributes } from '../types'; +import type { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js'; +import type { + LogAttributes, + LogFormatterInterface, + LogFormatterOptions, +} from '../types/Log.js'; +import type { UnformattedAttributes } from '../types/Logger.js'; +import { LogItem } from './LogItem.js'; /** * Typeguard to monkey patch Error to add a cause property. @@ -25,15 +31,27 @@ const isErrorWithCause = ( * @implements {LogFormatterInterface} */ abstract class LogFormatter implements LogFormatterInterface { + /** + * EnvironmentVariablesService instance. + * If set, it allows to access environment variables. + */ + protected envVarsService?: EnvironmentVariablesService; + + public constructor(options?: LogFormatterOptions) { + this.envVarsService = options?.envVarsService; + } + /** * It formats key-value pairs of log attributes. * * @param {UnformattedAttributes} attributes - * @returns {LogAttributes} + * @param {LogAttributes} additionalLogAttributes + * @returns {LogItem} */ public abstract formatAttributes( - attributes: UnformattedAttributes - ): LogAttributes; + attributes: UnformattedAttributes, + additionalLogAttributes: LogAttributes + ): LogItem; /** * It formats a given Error parameter. diff --git a/packages/logger/src/formatter/LogFormatterInterface.ts b/packages/logger/src/formatter/LogFormatterInterface.ts deleted file mode 100644 index b6a771b84e..0000000000 --- a/packages/logger/src/formatter/LogFormatterInterface.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { LogAttributes, UnformattedAttributes } from '../types'; - -/** - * @interface - */ -interface LogFormatterInterface { - /** - * It formats key-value pairs of log attributes. - * - * @param {UnformattedAttributes} attributes - * @returns {PowertoolLog} - */ - formatAttributes(attributes: UnformattedAttributes): LogAttributes; - - /** - * It formats a given Error parameter. - * - * @param {Error} error - * @returns {LogAttributes} - */ - formatError(error: Error): LogAttributes; -} - -export { LogFormatterInterface }; diff --git a/packages/logger/src/log/LogItem.ts b/packages/logger/src/formatter/LogItem.ts similarity index 72% rename from packages/logger/src/log/LogItem.ts rename to packages/logger/src/formatter/LogItem.ts index 1c562ec171..7ddacc61f8 100644 --- a/packages/logger/src/log/LogItem.ts +++ b/packages/logger/src/formatter/LogItem.ts @@ -1,24 +1,19 @@ import merge from 'lodash.merge'; -import { LogItemInterface } from '.'; -import { LogAttributes } from '../types'; +import type { LogAttributes, LogItemInterface } from '../types/Log.js'; class LogItem implements LogItemInterface { private attributes: LogAttributes = {}; - public constructor(params: { - baseAttributes: LogAttributes; - persistentAttributes: LogAttributes; - }) { + public constructor(params: { attributes: LogAttributes }) { // Add attributes in the log item in this order: // - Base attributes supported by the Powertool by default - // - Persistent attributes provided by developer, not formatted + // - Persistent attributes provided by developer, not formatted (done later) // - Ephemeral attributes provided as parameters for a single log item (done later) - this.addAttributes(params.baseAttributes); - this.addAttributes(params.persistentAttributes); + this.addAttributes(params.attributes); } - public addAttributes(attributes: LogAttributes): LogItem { - this.attributes = merge(this.attributes, attributes); + public addAttributes(attributes: LogAttributes): this { + merge(this.attributes, attributes); return this; } diff --git a/packages/logger/src/formatter/PowertoolLogFormatter.ts b/packages/logger/src/formatter/PowertoolsLogFormatter.ts similarity index 51% rename from packages/logger/src/formatter/PowertoolLogFormatter.ts rename to packages/logger/src/formatter/PowertoolsLogFormatter.ts index 0fa77928e2..80c0406bbb 100644 --- a/packages/logger/src/formatter/PowertoolLogFormatter.ts +++ b/packages/logger/src/formatter/PowertoolsLogFormatter.ts @@ -1,23 +1,28 @@ -import { LogFormatter } from '.'; -import { UnformattedAttributes } from '../types'; -import { PowertoolLog } from '../types/formats'; +import type { LogAttributes, PowertoolsLog } from '../types/Log.js'; +import type { UnformattedAttributes } from '../types/Logger.js'; +import { LogFormatter } from './LogFormatter.js'; +import { LogItem } from './LogItem.js'; /** * This class is used to transform a set of log key-value pairs - * in the AWS Lambda Powertools' default structure log format. + * in the Powertools for AWS Lambda default structure log format. * * @class * @extends {LogFormatter} */ -class PowertoolLogFormatter extends LogFormatter { +class PowertoolsLogFormatter extends LogFormatter { /** * It formats key-value pairs of log attributes. * * @param {UnformattedAttributes} attributes - * @returns {PowertoolLog} + * @param {LogAttributes} additionalLogAttributes + * @returns {LogItem} */ - public formatAttributes(attributes: UnformattedAttributes): PowertoolLog { - return { + public formatAttributes( + attributes: UnformattedAttributes, + additionalLogAttributes: LogAttributes + ): LogItem { + const baseAttributes: PowertoolsLog = { cold_start: attributes.lambdaContext?.coldStart, function_arn: attributes.lambdaContext?.invokedFunctionArn, function_memory_size: attributes.lambdaContext?.memoryLimitInMB, @@ -30,7 +35,11 @@ class PowertoolLogFormatter extends LogFormatter { timestamp: this.formatTimestamp(attributes.timestamp), xray_trace_id: attributes.xRayTraceId, }; + const powertoolsLogItem = new LogItem({ attributes: baseAttributes }); + powertoolsLogItem.addAttributes(additionalLogAttributes); + + return powertoolsLogItem; } } -export { PowertoolLogFormatter }; +export { PowertoolsLogFormatter }; diff --git a/packages/logger/src/formatter/index.ts b/packages/logger/src/formatter/index.ts deleted file mode 100644 index 1f7f14d2ab..0000000000 --- a/packages/logger/src/formatter/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './LogFormatter'; -export * from './LogFormatterInterface'; -export * from './PowertoolLogFormatter'; diff --git a/packages/logger/src/helpers.ts b/packages/logger/src/helpers.ts deleted file mode 100644 index dc1e94d45a..0000000000 --- a/packages/logger/src/helpers.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Logger } from '.'; -import { ConstructorOptions } from './types'; - -/** - * Create a new logger instance with the given options. - * - * @deprecated - This function will be removed in the next major release. Use the Logger class directly instead. - */ -const createLogger = (options: ConstructorOptions = {}): Logger => - new Logger(options); - -export { createLogger }; diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index caa720368d..1fe46bcf9b 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,4 +1,3 @@ -export * from './helpers'; -export * from './Logger'; -export * from './middleware'; -export * from './formatter'; +export { Logger } from './Logger.js'; +export { LogFormatter } from './formatter/LogFormatter.js'; +export { LogItem } from './formatter/LogItem.js'; diff --git a/packages/logger/src/log/LogItemInterface.ts b/packages/logger/src/log/LogItemInterface.ts deleted file mode 100644 index cb6113b9d5..0000000000 --- a/packages/logger/src/log/LogItemInterface.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { LogAttributes } from '../types'; - -interface LogItemInterface { - addAttributes(attributes: LogAttributes): void; - - getAttributes(): LogAttributes; -} - -export { LogItemInterface }; diff --git a/packages/logger/src/log/index.ts b/packages/logger/src/log/index.ts deleted file mode 100644 index 6a3bee0c47..0000000000 --- a/packages/logger/src/log/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './LogItem'; -export * from './LogItemInterface'; diff --git a/packages/logger/src/middleware/index.ts b/packages/logger/src/middleware/index.ts deleted file mode 100644 index cfe9900b37..0000000000 --- a/packages/logger/src/middleware/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './middy'; diff --git a/packages/logger/src/middleware/middy.ts b/packages/logger/src/middleware/middy.ts index f68ea4a8e6..4e970e55a9 100644 --- a/packages/logger/src/middleware/middy.ts +++ b/packages/logger/src/middleware/middy.ts @@ -1,10 +1,11 @@ -import { Logger } from '../Logger'; -import { HandlerOptions, LogAttributes } from '../types'; -import { LOGGER_KEY } from '@aws-lambda-powertools/commons/lib/middleware'; +import { Logger } from '../Logger.js'; +import type { LogAttributes } from '../types/Log.js'; +import type { InjectLambdaContextOptions } from '../types/Logger.js'; +import { LOGGER_KEY } from '@aws-lambda-powertools/commons'; import type { MiddlewareLikeObj, MiddyLikeRequest, -} from '@aws-lambda-powertools/commons'; +} from '@aws-lambda-powertools/commons/types'; /** * A middy middleware that helps emitting CloudWatch EMF metrics in your logs. @@ -13,7 +14,8 @@ import type { * * @example * ```typescript - * import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger'; + * import { Logger } from '@aws-lambda-powertools/logger'; + * import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware'; * import middy from '@middy/core'; * * @@ -32,7 +34,7 @@ import type { */ const injectLambdaContext = ( target: Logger | Logger[], - options?: HandlerOptions + options?: InjectLambdaContextOptions ): MiddlewareLikeObj => { const loggers = target instanceof Array ? target : [target]; const persistentAttributes: LogAttributes[] = []; diff --git a/packages/logger/src/config/ConfigServiceInterface.ts b/packages/logger/src/types/ConfigServiceInterface.ts similarity index 71% rename from packages/logger/src/config/ConfigServiceInterface.ts rename to packages/logger/src/types/ConfigServiceInterface.ts index 27c76b19e8..eea0b0200a 100644 --- a/packages/logger/src/config/ConfigServiceInterface.ts +++ b/packages/logger/src/types/ConfigServiceInterface.ts @@ -1,3 +1,5 @@ +import type { ConfigServiceInterface as ConfigServiceBaseInterface } from '@aws-lambda-powertools/commons/types'; + /** * Interface ConfigServiceInterface * @@ -5,15 +7,7 @@ * @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime * @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables */ -interface ConfigServiceInterface { - /** - * It returns the value of an environment variable that has given name. - * - * @param {string} name - * @returns {string} - */ - get(name: string): string; - +interface ConfigServiceInterface extends ConfigServiceBaseInterface { /** * It returns the value of the `AWS_LAMBDA_LOG_LEVEL` environment variable. * @@ -58,28 +52,6 @@ interface ConfigServiceInterface { * @returns {string|undefined} */ getSampleRateValue(): number | undefined; - - /** - * It returns the value of the POWERTOOLS_SERVICE_NAME environment variable. - * - * @returns {string} - */ - getServiceName(): string; - - /** - * It returns the value of the POWERTOOLS_DEV environment variable. - * - * @returns {boolean} - */ - isDevMode(): boolean; - - /** - * It returns true if the string value represents a boolean true value. - * - * @param {string} value - * @returns boolean - */ - isValueTrue(value: string): boolean; } -export { ConfigServiceInterface }; +export type { ConfigServiceInterface }; diff --git a/packages/logger/src/types/Log.ts b/packages/logger/src/types/Log.ts index 931ef10027..e415ee2544 100644 --- a/packages/logger/src/types/Log.ts +++ b/packages/logger/src/types/Log.ts @@ -1,3 +1,7 @@ +import type { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js'; +import type { LogItem } from '../formatter/LogItem.js'; +import type { UnformattedAttributes } from './Logger.js'; + type LogLevelDebug = 'DEBUG'; type LogLevelInfo = 'INFO'; type LogLevelWarn = 'WARN'; @@ -32,6 +36,145 @@ type LogAttributesWithMessage = LogAttributes & { type Environment = 'dev' | 'local' | 'staging' | 'prod' | string; +type PowertoolsLog = LogAttributes & { + /** + * Timestamp of actual log statement. + * + * @example "2020-05-24 18:17:33,774" + */ + timestamp?: string; + + /** + * Log level + * + * @example "INFO" + */ + level?: LogLevel; + + /** + * Service name defined. + * + * @example "payment" + */ + service: string; + + /** + * The value of the logging sampling rate in percentage. + * + * @example 0.1 + */ + sampling_rate?: number; + + /** + * Log statement value. Unserializable JSON values will be cast to string. + * + * @example "Collecting payment" + */ + message?: string; + + /** + * X-Ray Trace ID set by the Lambda runtime. + * + * @example "1-5759e988-bd862e3fe1be46a994272793" + */ + xray_trace_id?: string; + + /** + * Indicates whether the current execution experienced a cold start. + * + * @example false + */ + cold_start?: boolean; + + /** + * The name of the Lambda function. + * + * @example "example-powertools-HelloWorldFunction-1P1Z6B39FLU73" + */ + lambda_function_name?: string; + + /** + * The memory size of the Lambda function. + * + * Description: + * Example: 128 + */ + lambda_function_memory_size?: number; + + /** + * lambda_function_arn + * + * Description: The ARN of the Lambda function. + * Example: "arn:aws:lambda:eu-west-1:012345678910:function:example-powertools-HelloWorldFunction-1P1Z6B39FLU73" + */ + lambda_function_arn?: string; + + /** + * lambda_request_id + * + * Description: The request ID of the current invocation. + * Example: "899856cb-83d1-40d7-8611-9e78f15f32f4" + */ + lambda_request_id?: string; +}; + +interface LogItemInterface { + addAttributes(attributes: LogAttributes): void; + getAttributes(): LogAttributes; + prepareForPrint(): void; + removeEmptyKeys(attributes: LogAttributes): LogAttributes; + setAttributes(attributes: LogAttributes): void; +} + +type LogFormatterOptions = { + /** + * EnvironmentVariablesService instance. + * If set, it gives the LogFormatter access to environment variables. + */ + envVarsService?: EnvironmentVariablesService; +}; + +/** + * @interface + */ +interface LogFormatterInterface { + /** + * It formats key-value pairs of log attributes. + * + * @param {UnformattedAttributes} attributes + * @param {LogAttributes} additionalLogAttributes + * @returns {LogItem} + */ + formatAttributes( + attributes: UnformattedAttributes, + additionalLogAttributes: LogAttributes + ): LogItem; + + /** + * It formats a given Error parameter. + * + * @param {Error} error + * @returns {LogAttributes} + */ + formatError(error: Error): LogAttributes; + + /** + * It formats a date into a string in simplified extended ISO format (ISO 8601). + * + * @param {Date} now + * @returns {string} + */ + formatTimestamp(now: Date): string; + + /** + * It returns a string containing the location of an error, given a particular stack trace. + * + * @param stack + * @returns {string} + */ + getCodeLocation(stack?: string): string; +} + export type { LogAttributesWithMessage, LogAttributeValue, @@ -39,4 +182,8 @@ export type { LogLevelThresholds, LogAttributes, LogLevel, + PowertoolsLog, + LogItemInterface, + LogFormatterOptions, + LogFormatterInterface, }; diff --git a/packages/logger/src/types/Logger.ts b/packages/logger/src/types/Logger.ts index d5d464fe17..fdeb41d4e3 100644 --- a/packages/logger/src/types/Logger.ts +++ b/packages/logger/src/types/Logger.ts @@ -1,26 +1,22 @@ -import { - AsyncHandler, - LambdaInterface, - SyncHandler, -} from '@aws-lambda-powertools/commons'; -import { Handler } from 'aws-lambda'; -import { ConfigServiceInterface } from '../config'; -import { LogFormatterInterface } from '../formatter'; -import { +import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types'; +import type { ConfigServiceInterface } from './ConfigServiceInterface.js'; +import type { Environment, LogAttributes, LogAttributesWithMessage, LogLevel, -} from './Log'; + LogFormatterInterface, +} from './Log.js'; +import type { Context } from 'aws-lambda'; -type ClassThatLogs = { +type LogFunction = { [key in Exclude, 'silent'>]: ( input: LogItemMessage, ...extraInput: LogItemExtraInput ) => void; }; -type HandlerOptions = { +type InjectLambdaContextOptions = { logEvent?: boolean; clearState?: boolean; }; @@ -35,32 +31,28 @@ type ConstructorOptions = { environment?: Environment; }; -type LambdaFunctionContext = { - functionName: string; - memoryLimitInMB: number; - functionVersion: string; +type LambdaFunctionContext = Pick< + Context, + | 'functionName' + | 'memoryLimitInMB' + | 'functionVersion' + | 'invokedFunctionArn' + | 'awsRequestId' +> & { coldStart: boolean; - invokedFunctionArn: string; - awsRequestId: string; }; -type PowertoolLogData = LogAttributes & { +type PowertoolsLogData = LogAttributes & { environment?: Environment; serviceName: string; - sampleRateValue?: number; - lambdaFunctionContext: LambdaFunctionContext; + sampleRateValue: number; + lambdaContext?: LambdaFunctionContext; xRayTraceId?: string; awsRegion: string; }; -type UnformattedAttributes = { - environment?: Environment; +type UnformattedAttributes = PowertoolsLogData & { error?: Error; - serviceName: string; - sampleRateValue?: number; - lambdaContext?: LambdaFunctionContext; - xRayTraceId?: string; - awsRegion: string; logLevel: LogLevel; timestamp: Date; message: string; @@ -69,27 +61,39 @@ type UnformattedAttributes = { type LogItemMessage = string | LogAttributesWithMessage; type LogItemExtraInput = [Error | string] | LogAttributes[]; -type HandlerMethodDecorator = ( - target: LambdaInterface, - propertyKey: string | symbol, - descriptor: - | TypedPropertyDescriptor> - | TypedPropertyDescriptor> -) => void; +type LoggerInterface = { + addContext(context: Context): void; + addPersistentLogAttributes(attributes?: LogAttributes): void; + appendKeys(attributes?: LogAttributes): void; + createChild(options?: ConstructorOptions): LoggerInterface; + critical(input: LogItemMessage, ...extraInput: LogItemExtraInput): void; + debug(input: LogItemMessage, ...extraInput: LogItemExtraInput): void; + error(input: LogItemMessage, ...extraInput: LogItemExtraInput): void; + getLevelName(): Uppercase; + getLogEvent(): boolean; + getPersistentLogAttributes(): LogAttributes; + info(input: LogItemMessage, ...extraInput: LogItemExtraInput): void; + injectLambdaContext( + options?: InjectLambdaContextOptions + ): HandlerMethodDecorator; + logEventIfEnabled(event: unknown, overwriteValue?: boolean): void; + refreshSampleRateCalculation(): void; + removeKeys(keys?: string[]): void; + removePersistentLogAttributes(keys?: string[]): void; + setLogLevel(logLevel: LogLevel): void; + setPersistentLogAttributes(attributes?: LogAttributes): void; + shouldLogEvent(overwriteValue?: boolean): boolean; + warn(input: LogItemMessage, ...extraInput: LogItemExtraInput): void; +}; -export { - ClassThatLogs, +export type { + LogFunction, + LoggerInterface, LogItemMessage, LogItemExtraInput, - HandlerMethodDecorator, LambdaFunctionContext, UnformattedAttributes, - PowertoolLogData, + PowertoolsLogData, ConstructorOptions, - HandlerOptions, + InjectLambdaContextOptions, }; - -export const enum LogJsonIndent { - PRETTY = 4, - COMPACT = 0, -} diff --git a/packages/logger/src/types/formats/PowertoolLog.ts b/packages/logger/src/types/formats/PowertoolLog.ts deleted file mode 100644 index add42d298c..0000000000 --- a/packages/logger/src/types/formats/PowertoolLog.ts +++ /dev/null @@ -1,93 +0,0 @@ -import type { LogAttributes, LogLevel } from '..'; - -type PowertoolLog = LogAttributes & { - /** - * timestamp - * - * Description: Timestamp of actual log statement. - * Example: "2020-05-24 18:17:33,774" - */ - timestamp?: string; - - /** - * level - * - * Description: Logging level - * Example: "INFO" - */ - level?: LogLevel; - - /** - * service - * - * Description: Service name defined. - * Example: "payment" - */ - service: string; - - /** - * sampling_rate - * - * Description: The value of the logging sampling rate in percentage. - * Example: 0.1 - */ - sampling_rate?: number; - - /** - * message - * - * Description: Log statement value. Unserializable JSON values will be cast to string. - * Example: "Collecting payment" - */ - message?: string; - - /** - * xray_trace_id - * - * Description: X-Ray Trace ID when Lambda function has enabled Tracing. - * Example: "1-5759e988-bd862e3fe1be46a994272793" - */ - xray_trace_id?: string; - - /** - * cold_start - * - * Description: Indicates whether the current execution experienced a cold start. - * Example: false - */ - cold_start?: boolean; - - /** - * lambda_function_name - * - * Description: The name of the Lambda function. - * Example: "example-powertools-HelloWorldFunction-1P1Z6B39FLU73" - */ - lambda_function_name?: string; - - /** - * lambda_function_memory_size - * - * Description: The memory size of the Lambda function. - * Example: 128 - */ - lambda_function_memory_size?: number; - - /** - * lambda_function_arn - * - * Description: The ARN of the Lambda function. - * Example: "arn:aws:lambda:eu-west-1:012345678910:function:example-powertools-HelloWorldFunction-1P1Z6B39FLU73" - */ - lambda_function_arn?: string; - - /** - * lambda_request_id - * - * Description: The request ID of the current invocation. - * Example: "899856cb-83d1-40d7-8611-9e78f15f32f4" - */ - lambda_request_id?: string; -}; - -export type { PowertoolLog }; diff --git a/packages/logger/src/types/formats/index.ts b/packages/logger/src/types/formats/index.ts deleted file mode 100644 index 5462610cd6..0000000000 --- a/packages/logger/src/types/formats/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './PowertoolLog'; diff --git a/packages/logger/src/types/index.ts b/packages/logger/src/types/index.ts index dc74795384..e18a8806e0 100644 --- a/packages/logger/src/types/index.ts +++ b/packages/logger/src/types/index.ts @@ -1,2 +1,17 @@ -export * from './Log'; -export * from './Logger'; +export type { + LogAttributesWithMessage, + LogAttributeValue, + Environment, + LogLevelThresholds, + LogAttributes, + LogLevel, +} from './Log.js'; +export type { + LogItemMessage, + LogItemExtraInput, + LambdaFunctionContext, + UnformattedAttributes, + PowertoolsLogData, + ConstructorOptions, + InjectLambdaContextOptions, +} from './Logger.js'; diff --git a/packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts b/packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts index 9107bf5453..01c3b11804 100644 --- a/packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts +++ b/packages/logger/tests/e2e/basicFeatures.middy.test.FunctionCode.ts @@ -1,6 +1,7 @@ -import { injectLambdaContext, Logger } from '../../src'; -import { Context, APIGatewayAuthorizerResult } from 'aws-lambda'; -import { TestEvent, TestOutput } from '../helpers/types'; +import { Logger } from '../../src/index.js'; +import { injectLambdaContext } from '../../src/middleware/middy.js'; +import type { Context, APIGatewayAuthorizerResult } from 'aws-lambda'; +import type { TestEvent, TestOutput } from '../helpers/types.js'; import middy from '@middy/core'; const PERSISTENT_KEY = process.env.PERSISTENT_KEY || 'persistentKey'; diff --git a/packages/logger/tests/e2e/basicFeatures.middy.test.ts b/packages/logger/tests/e2e/basicFeatures.middy.test.ts index 5901064aff..5f7a67eb35 100644 --- a/packages/logger/tests/e2e/basicFeatures.middy.test.ts +++ b/packages/logger/tests/e2e/basicFeatures.middy.test.ts @@ -10,7 +10,7 @@ import { } from '@aws-lambda-powertools/testing-utils'; import type { APIGatewayAuthorizerResult } from 'aws-lambda'; import { join } from 'node:path'; -import { LoggerTestNodejsFunction } from '../helpers/resources'; +import { LoggerTestNodejsFunction } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, @@ -19,7 +19,7 @@ import { TEST_CASE_TIMEOUT, XRAY_TRACE_ID_REGEX, commonEnvironmentVars, -} from './constants'; +} from './constants.js'; describe(`Logger E2E tests, basic functionalities middy usage`, () => { const testStack = new TestStack({ diff --git a/packages/logger/tests/e2e/childLogger.manual.test.FunctionCode.ts b/packages/logger/tests/e2e/childLogger.manual.test.FunctionCode.ts index f2d32e36f0..82c2b3f0c2 100644 --- a/packages/logger/tests/e2e/childLogger.manual.test.FunctionCode.ts +++ b/packages/logger/tests/e2e/childLogger.manual.test.FunctionCode.ts @@ -1,7 +1,7 @@ -import { Logger } from '../../src'; -import { Context } from 'aws-lambda'; -import { LogLevel } from '../../src/types'; -import { TestEvent, TestOutput } from '../helpers/types'; +import { Logger } from '../../src/index.js'; +import type { Context } from 'aws-lambda'; +import type { LogLevel } from '../../src/types/index.js'; +import { TestEvent, TestOutput } from '../helpers/types.js'; const PERSISTENT_KEY = process.env.PERSISTENT_KEY || 'persistentKey'; const PERSISTENT_VALUE = process.env.ERSISTENT_VALUE || 'persistentValue'; diff --git a/packages/logger/tests/e2e/childLogger.manual.test.ts b/packages/logger/tests/e2e/childLogger.manual.test.ts index 454eab8a81..037b3ac81d 100644 --- a/packages/logger/tests/e2e/childLogger.manual.test.ts +++ b/packages/logger/tests/e2e/childLogger.manual.test.ts @@ -9,7 +9,7 @@ import { TestStack, } from '@aws-lambda-powertools/testing-utils'; import { join } from 'node:path'; -import { LoggerTestNodejsFunction } from '../helpers/resources'; +import { LoggerTestNodejsFunction } from '../helpers/resources.js'; import { commonEnvironmentVars, RESOURCE_NAME_PREFIX, @@ -17,7 +17,7 @@ import { STACK_OUTPUT_LOG_GROUP, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; describe(`Logger E2E tests, child logger`, () => { const testStack = new TestStack({ diff --git a/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.FunctionCode.ts b/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.FunctionCode.ts index 3f816ff4e1..2d10943d61 100644 --- a/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.FunctionCode.ts +++ b/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.FunctionCode.ts @@ -1,6 +1,7 @@ -import { injectLambdaContext, Logger } from '../../src'; -import { TestEvent, TestOutput } from '../helpers/types'; -import { Context } from 'aws-lambda'; +import { Logger } from '../../src/index.js'; +import { injectLambdaContext } from '../../src/middleware/middy.js'; +import type { TestEvent, TestOutput } from '../helpers/types.js'; +import type { Context } from 'aws-lambda'; import middy from '@middy/core'; const logger = new Logger(); diff --git a/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.ts b/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.ts index 5b87f0675f..902c41c09e 100644 --- a/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.ts +++ b/packages/logger/tests/e2e/logEventEnvVarSetting.middy.test.ts @@ -9,14 +9,14 @@ import { TestStack, } from '@aws-lambda-powertools/testing-utils'; import { join } from 'node:path'; -import { LoggerTestNodejsFunction } from '../helpers/resources'; +import { LoggerTestNodejsFunction } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, STACK_OUTPUT_LOG_GROUP, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; describe(`Logger E2E tests, log event via env var setting with middy`, () => { const testStack = new TestStack({ diff --git a/packages/logger/tests/e2e/sampleRate.decorator.test.FunctionCode.ts b/packages/logger/tests/e2e/sampleRate.decorator.test.FunctionCode.ts index 810e739bdf..89c7e4833f 100644 --- a/packages/logger/tests/e2e/sampleRate.decorator.test.FunctionCode.ts +++ b/packages/logger/tests/e2e/sampleRate.decorator.test.FunctionCode.ts @@ -1,7 +1,7 @@ -import { Logger } from '../../src'; -import { TestEvent, TestOutput } from '../helpers/types'; -import { Context } from 'aws-lambda'; -import { LambdaInterface } from '@aws-lambda-powertools/commons'; +import { Logger } from '../../src/index.js'; +import type { TestEvent, TestOutput } from '../helpers/types'; +import type { Context } from 'aws-lambda'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const SAMPLE_RATE = parseFloat(process.env.SAMPLE_RATE || '0.1'); const LOG_MSG = process.env.LOG_MSG || 'Hello World'; diff --git a/packages/logger/tests/e2e/sampleRate.decorator.test.ts b/packages/logger/tests/e2e/sampleRate.decorator.test.ts index 7694a1bf18..d6db70c8c0 100644 --- a/packages/logger/tests/e2e/sampleRate.decorator.test.ts +++ b/packages/logger/tests/e2e/sampleRate.decorator.test.ts @@ -10,14 +10,14 @@ import { } from '@aws-lambda-powertools/testing-utils'; import { randomUUID } from 'node:crypto'; import { join } from 'node:path'; -import { LoggerTestNodejsFunction } from '../helpers/resources'; +import { LoggerTestNodejsFunction } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, STACK_OUTPUT_LOG_GROUP, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; describe(`Logger E2E tests, sample rate and injectLambdaContext()`, () => { const testStack = new TestStack({ @@ -81,12 +81,18 @@ describe(`Logger E2E tests, sample rate and injectLambdaContext()`, () => { if (logMessages.length === 1 && logMessages[0].includes('ERROR')) { countNotSampled++; - } else if (logMessages.length === 4) { + } else if ( + (logMessages.length === 5 && + logMessages[0].includes( + 'Setting log level to DEBUG due to sampling rate' + )) || + logMessages.length === 4 + ) { countSampled++; } else { console.error(`Log group ${logGroupName} contains missing log`); throw new Error( - 'Sampled log should have either 1 error log or 4 logs of all levels' + 'Sampled log should have either 1 error log or 5 logs of all levels' ); } } diff --git a/packages/logger/tests/helpers/resources.ts b/packages/logger/tests/helpers/resources.ts index 95e7a34074..deef695c0a 100644 --- a/packages/logger/tests/helpers/resources.ts +++ b/packages/logger/tests/helpers/resources.ts @@ -1,10 +1,10 @@ -import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import type { TestStack } from '@aws-lambda-powertools/testing-utils'; import { CfnOutput } from 'aws-cdk-lib'; import type { - TestNodejsFunctionProps, ExtraTestProps, -} from '@aws-lambda-powertools/testing-utils'; + TestNodejsFunctionProps, +} from '@aws-lambda-powertools/testing-utils/types'; import { commonEnvironmentVars } from '../e2e/constants'; interface LoggerExtraTestProps extends ExtraTestProps { diff --git a/packages/logger/tests/tsconfig.json b/packages/logger/tests/tsconfig.json index 5654b3e15f..45ba862a85 100644 --- a/packages/logger/tests/tsconfig.json +++ b/packages/logger/tests/tsconfig.json @@ -1,11 +1,8 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "rootDir": "../", - "noEmit": true - }, - "include": [ - "../src/**/*", - "./**/*", - ] -} \ No newline at end of file + "extends": "../tsconfig.json", + "compilerOptions": { + "rootDir": "../", + "noEmit": true + }, + "include": ["../src/**/*", "./**/*"] +} diff --git a/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/logger/tests/unit/EnvironmentVariablesService.test.ts similarity index 88% rename from packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts rename to packages/logger/tests/unit/EnvironmentVariablesService.test.ts index 7c1c4de428..1d40af9e9f 100644 --- a/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/logger/tests/unit/EnvironmentVariablesService.test.ts @@ -1,9 +1,9 @@ /** * Test Logger EnvironmentVariablesService class * - * @group unit/logger/all + * @group unit/logger/config */ -import { EnvironmentVariablesService } from '../../../src/config'; +import { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; describe('Class: EnvironmentVariablesService', () => { const ENVIRONMENT_VARIABLES = process.env; @@ -193,4 +193,29 @@ describe('Class: EnvironmentVariablesService', () => { expect(value).toEqual(0.01); }); }); + + describe('Method: getTimezone', () => { + it('returns the value of the TZ environment variable when set', () => { + // Prepare + process.env.TZ = 'Europe/London'; + const service = new EnvironmentVariablesService(); + + // Act + const value = service.getTimezone(); + + // Assess + expect(value).toEqual('Europe/London'); + }); + + it('returns the default UTC value when no TZ is set', () => { + // Prepare + const service = new EnvironmentVariablesService(); + + // Act + const value = service.getTimezone(); + + // Assess + expect(value).toEqual('UTC'); + }); + }); }); diff --git a/packages/logger/tests/unit/Logger.test.ts b/packages/logger/tests/unit/Logger.test.ts index 1c0d1e8582..b5f8ae462a 100644 --- a/packages/logger/tests/unit/Logger.test.ts +++ b/packages/logger/tests/unit/Logger.test.ts @@ -1,39 +1,46 @@ /** * Test Logger class * - * @group unit/logger/all + * @group unit/logger/logger */ -import { - ContextExamples as dummyContext, - Events as dummyEvent, - LambdaInterface, -} from '@aws-lambda-powertools/commons'; -import { createLogger, Logger } from '../../src'; -import { EnvironmentVariablesService } from '../../src/config'; -import { PowertoolLogFormatter } from '../../src/formatter'; -import { - ClassThatLogs, - LogJsonIndent, +import context from '@aws-lambda-powertools/testing-utils/context'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { Logger, LogFormatter } from '../../src/index.js'; +import { ConfigServiceInterface } from '../../src/types/ConfigServiceInterface.js'; +import { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; +import { PowertoolsLogFormatter } from '../../src/formatter/PowertoolsLogFormatter.js'; +import { LogLevelThresholds, LogLevel } from '../../src/types/Log.js'; +import type { + LogFunction, ConstructorOptions, - LogLevelThresholds, - LogLevel, -} from '../../src/types'; +} from '../../src/types/Logger.js'; +import { LogJsonIndent } from '../../src/constants.js'; import type { Context } from 'aws-lambda'; -import { Console } from 'console'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); const getConsoleMethod = ( method: string -): keyof Omit => +): keyof Omit => method === 'critical' ? 'error' - : (method.toLowerCase() as keyof Omit); + : (method.toLowerCase() as keyof Omit); +jest.mock('node:console', () => ({ + ...jest.requireActual('node:console'), + Console: jest.fn().mockImplementation(() => ({ + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + })), +})); describe('Class: Logger', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext.helloworldContext; - const event = dummyEvent.Custom.CustomEvent; + const event = { + foo: 'bar', + bar: 'baz', + }; const logLevelThresholds: LogLevelThresholds = { DEBUG: 8, INFO: 12, @@ -48,6 +55,431 @@ describe('Class: Logger', () => { process.env = { ...ENVIRONMENT_VARIABLES }; }); + describe('Method: constructor', () => { + test('when no constructor parameters are set, returns a Logger instance with the options set in the environment variables', () => { + // Prepare + const loggerOptions = undefined; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: {}, + powertoolsLogData: { + sampleRateValue: 0, + awsRegion: 'eu-west-1', + environment: '', + serviceName: 'hello-world', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + defaultServiceName: 'service_undefined', + logLevel: 8, + logFormatter: expect.any(PowertoolsLogFormatter), + }) + ); + }); + + test('when no parameters are set, returns a Logger instance with the correct properties', () => { + // Prepare + const loggerOptions: ConstructorOptions = { + logLevel: 'WARN', + serviceName: 'my-lambda-service', + sampleRateValue: 1, + logFormatter: new PowertoolsLogFormatter(), + customConfigService: new EnvironmentVariablesService(), + persistentLogAttributes: { + awsAccountId: '123456789', + }, + environment: 'prod', + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual({ + coldStart: true, + defaultServiceName: 'service_undefined', + customConfigService: expect.any(EnvironmentVariablesService), + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: 0, + logFormatter: expect.any(PowertoolsLogFormatter), + logLevel: 8, // 100% sample rate value changes log level to DEBUG + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), + logLevelThresholds: { + ...logLevelThresholds, + }, + persistentLogAttributes: { + awsAccountId: '123456789', + }, + powertoolsLogData: { + awsRegion: 'eu-west-1', + environment: 'prod', + sampleRateValue: 1, + serviceName: 'my-lambda-service', + }, + }); + }); + + test('when no constructor parameters and no environment variables are set, returns a Logger instance with the default properties', () => { + // Prepare + const loggerOptions = undefined; + delete process.env.POWERTOOLS_SERVICE_NAME; + delete process.env.POWERTOOLS_LOG_LEVEL; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual({ + coldStart: true, + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: 0, + logFormatter: expect.any(PowertoolsLogFormatter), + logLevel: 12, + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), + logLevelThresholds: { + ...logLevelThresholds, + }, + persistentLogAttributes: {}, + powertoolsLogData: { + awsRegion: 'eu-west-1', + environment: '', + sampleRateValue: 0, + serviceName: 'service_undefined', + }, + }); + }); + + test('when a custom logFormatter is passed, returns a Logger instance with the correct properties', () => { + // Prepare + const loggerOptions: ConstructorOptions = { + logFormatter: expect.any(LogFormatter), + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: {}, + powertoolsLogData: { + sampleRateValue: 0, + awsRegion: 'eu-west-1', + environment: '', + serviceName: 'hello-world', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + logLevel: 8, + logFormatter: expect.any(LogFormatter), + }) + ); + }); + + test('when a custom serviceName is passed, returns a Logger instance with the correct properties', () => { + // Prepare + const loggerOptions: ConstructorOptions = { + serviceName: 'my-backend-service', + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: {}, + powertoolsLogData: { + sampleRateValue: 0, + awsRegion: 'eu-west-1', + environment: '', + serviceName: 'my-backend-service', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + logLevel: 8, + logFormatter: expect.any(PowertoolsLogFormatter), + }) + ); + }); + + test('when a custom uppercase logLevel is passed, returns a Logger instance with the correct properties', () => { + // Prepare + const loggerOptions: ConstructorOptions = { + logLevel: 'ERROR', + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: {}, + powertoolsLogData: { + sampleRateValue: 0, + awsRegion: 'eu-west-1', + environment: '', + serviceName: 'hello-world', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + logLevel: 20, + logFormatter: expect.any(PowertoolsLogFormatter), + }) + ); + }); + + test('when a custom lowercase logLevel is passed, returns a Logger instance with the correct properties', () => { + // Prepare + const loggerOptions: ConstructorOptions = { + logLevel: 'warn', + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: {}, + powertoolsLogData: { + sampleRateValue: 0, + awsRegion: 'eu-west-1', + environment: '', + serviceName: 'hello-world', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + logLevel: 16, + logFormatter: expect.any(PowertoolsLogFormatter), + }) + ); + }); + + test('when no log level is set, returns a Logger instance with INFO level', () => { + // Prepare + const loggerOptions: ConstructorOptions = {}; + delete process.env.POWERTOOLS_LOG_LEVEL; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual({ + coldStart: true, + customConfigService: undefined, + defaultServiceName: 'service_undefined', + envVarsService: expect.any(EnvironmentVariablesService), + logEvent: false, + logIndentation: 0, + logFormatter: expect.any(PowertoolsLogFormatter), + logLevel: 12, + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), + logLevelThresholds: { + ...logLevelThresholds, + }, + persistentLogAttributes: {}, + powertoolsLogData: { + awsRegion: 'eu-west-1', + environment: '', + sampleRateValue: 0, + serviceName: 'hello-world', + }, + }); + }); + + test('when a custom sampleRateValue is passed, returns a Logger instance with the correct properties', () => { + // Prepare + const loggerOptions: ConstructorOptions = { + sampleRateValue: 1, + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: {}, + powertoolsLogData: { + sampleRateValue: 1, + awsRegion: 'eu-west-1', + environment: '', + serviceName: 'hello-world', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + logLevel: 8, + logFormatter: expect.any(PowertoolsLogFormatter), + }) + ); + }); + + test('when a custom customConfigService is passed, returns a Logger instance with the correct properties', () => { + const configService: ConfigServiceInterface = { + get(name: string): string { + return `a-string-from-${name}`; + }, + getAwsLogLevel() { + return 'INFO'; + }, + getCurrentEnvironment(): string { + return 'dev'; + }, + getLogEvent(): boolean { + return true; + }, + getLogLevel(): string { + return 'INFO'; + }, + getSampleRateValue(): number | undefined { + return undefined; + }, + getServiceName(): string { + return 'my-backend-service'; + }, + getXrayTraceId(): string | undefined { + return undefined; + }, + isDevMode(): boolean { + return false; + }, + isValueTrue(): boolean { + return true; + }, + }; + // Prepare + const loggerOptions: ConstructorOptions = { + customConfigService: configService, + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: {}, + powertoolsLogData: { + sampleRateValue: 0, + awsRegion: 'eu-west-1', + environment: 'dev', + serviceName: 'my-backend-service', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: configService, + logLevel: 12, + logFormatter: expect.any(PowertoolsLogFormatter), + }) + ); + }); + + test('when custom persistentLogAttributes is passed, returns a Logger instance with the correct properties', () => { + // Prepare + const loggerOptions: ConstructorOptions = { + persistentLogAttributes: { + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + logger: { + name: 'aws-lambda-powertool-typescript', + version: '0.2.4', + }, + }, + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: { + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + logger: { + name: 'aws-lambda-powertool-typescript', + version: '0.2.4', + }, + }, + powertoolsLogData: { + sampleRateValue: 0, + awsRegion: 'eu-west-1', + environment: '', + serviceName: 'hello-world', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + logLevel: 8, + logFormatter: expect.any(PowertoolsLogFormatter), + }) + ); + }); + + test('when a custom environment is passed, returns a Logger instance with the correct properties', () => { + // Prepare + const loggerOptions: ConstructorOptions = { + environment: 'dev', + }; + + // Act + const logger = new Logger(loggerOptions); + + // Assess + expect(logger).toBeInstanceOf(Logger); + expect(logger).toEqual( + expect.objectContaining({ + persistentLogAttributes: {}, + powertoolsLogData: { + sampleRateValue: 0, + awsRegion: 'eu-west-1', + environment: 'dev', + serviceName: 'hello-world', + }, + envVarsService: expect.any(EnvironmentVariablesService), + customConfigService: undefined, + logLevel: 8, + logFormatter: expect.any(PowertoolsLogFormatter), + }) + ); + }); + }); + describe.each([ [ 'debug', @@ -65,7 +497,7 @@ describe('Class: Logger', () => { ['error', 'DOES', true, 'DOES', true, 'DOES', true, 'DOES', true], ['critical', 'DOES', true, 'DOES', true, 'DOES', true, 'DOES', true], ])( - 'Method: %p', + 'Method:', ( method: string, debugAction, @@ -77,18 +509,18 @@ describe('Class: Logger', () => { errorAction, errorPrints ) => { - const methodOfLogger = method as keyof ClassThatLogs; + const methodOfLogger = method as keyof LogFunction; describe('Feature: log level', () => { test(`when the level is DEBUG, it ${debugAction} print to stdout`, () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(method)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(method) + ); // Act logger[methodOfLogger]('foo'); @@ -100,6 +532,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: methodOfLogger.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -110,13 +543,13 @@ describe('Class: Logger', () => { test(`when the log level is INFO, it ${infoAction} print to stdout`, () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'INFO', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act logger[methodOfLogger]('foo'); @@ -128,6 +561,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: methodOfLogger.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -138,13 +572,13 @@ describe('Class: Logger', () => { test(`when the log level is WARN, it ${warnAction} print to stdout`, () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'WARN', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act logger[methodOfLogger]('foo'); @@ -156,6 +590,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: methodOfLogger.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -166,13 +601,13 @@ describe('Class: Logger', () => { test(`when the log level is ERROR, it ${errorAction} print to stdout`, () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'ERROR', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act logger[methodOfLogger]('foo'); @@ -184,6 +619,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: methodOfLogger.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -194,13 +630,13 @@ describe('Class: Logger', () => { test('when the log level is SILENT, it DOES NOT print to stdout', () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'SILENT', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act logger[methodOfLogger]('foo'); @@ -212,10 +648,10 @@ describe('Class: Logger', () => { // Prepare process.env.POWERTOOLS_LOG_LEVEL = methodOfLogger.toUpperCase(); const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act logger[methodOfLogger]('foo'); @@ -226,6 +662,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: methodOfLogger.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -234,45 +671,49 @@ describe('Class: Logger', () => { }); }); - describe('Feature: sample rate', () => { + describe('Feature: sampling debug logs', () => { test('when the log level is higher and the current Lambda invocation IS NOT sampled for logging, it DOES NOT print to stdout', () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'SILENT', sampleRateValue: 0, }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act if (logger[methodOfLogger]) { logger[methodOfLogger]('foo'); } // Assess + expect(logger.level).toBe(28); + expect(logger.getLevelName()).toBe('SILENT'); expect(consoleSpy).toBeCalledTimes(0); }); - test('when the log level is higher and the current Lambda invocation IS sampled for logging, it DOES print to stdout', () => { + test('when the log level is higher and the current Lambda invocation IS sampled for logging, it DOES print to stdout and changes log level to DEBUG', () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'SILENT', sampleRateValue: 1, }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act if (logger[methodOfLogger]) { logger[methodOfLogger]('foo'); } // Assess - expect(consoleSpy).toBeCalledTimes(1); + expect(logger.level).toBe(8); + expect(logger.getLevelName()).toBe('DEBUG'); + expect(consoleSpy).toBeCalledTimes(method === 'debug' ? 2 : 1); expect(consoleSpy).toHaveBeenNthCalledWith( - 1, + method === 'debug' ? 2 : 1, JSON.stringify({ level: method.toUpperCase(), message: 'foo', @@ -292,11 +733,11 @@ describe('Class: Logger', () => { ' log', () => { // Prepare - const logger: Logger = createLogger(); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const logger = new Logger(); + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act if (logger[methodOfLogger]) { logger[methodOfLogger]('foo'); @@ -309,6 +750,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: method.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -323,15 +765,14 @@ describe('Class: Logger', () => { ' log', () => { // Prepare - const logger: Logger & { addContext: (context: Context) => void } = - createLogger({ - logLevel: 'DEBUG', - }); + const logger = new Logger({ + logLevel: 'DEBUG', + }); logger.addContext(context); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act if (logger[methodOfLogger]) { logger[methodOfLogger]('foo'); @@ -345,11 +786,12 @@ describe('Class: Logger', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: method.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -362,13 +804,13 @@ describe('Class: Logger', () => { describe('Feature: ephemeral log attributes', () => { test('when added, they should appear in that log item only', () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); interface NestedObject { bool: boolean; str: string; @@ -434,6 +876,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: method.toUpperCase(), message: 'A log item without extra parameters', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -445,6 +888,7 @@ describe('Class: Logger', () => { level: method.toUpperCase(), message: 'A log item with a string as first parameter, and an object as second parameter', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -457,6 +901,7 @@ describe('Class: Logger', () => { level: method.toUpperCase(), message: 'A log item with a string as first parameter, and objects as other parameters', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -469,6 +914,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: method.toUpperCase(), message: 'A log item with an object as first parameters', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -481,6 +927,7 @@ describe('Class: Logger', () => { level: method.toUpperCase(), message: 'A log item with a string as first parameter, and an error as second parameter', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -500,6 +947,7 @@ describe('Class: Logger', () => { level: method.toUpperCase(), message: 'A log item with a string as first parameter, and an error with custom key as second parameter', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -517,6 +965,7 @@ describe('Class: Logger', () => { level: method.toUpperCase(), message: 'A log item with a string as first parameter, and a string as second parameter', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -529,6 +978,7 @@ describe('Class: Logger', () => { level: method.toUpperCase(), message: 'A log item with a string as first parameter, and an inline object as second parameter', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -541,6 +991,7 @@ describe('Class: Logger', () => { level: method.toUpperCase(), message: 'A log item with a string as first parameter, and an arbitrary object as second parameter', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -568,17 +1019,17 @@ describe('Class: Logger', () => { describe('Feature: persistent log attributes', () => { test('when persistent log attributes are added to the Logger instance, they should appear in all logs printed by the instance', () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'DEBUG', persistentLogAttributes: { aws_account_id: '123456789012', aws_region: 'eu-west-1', }, }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act if (logger[methodOfLogger]) { logger[methodOfLogger]('foo'); @@ -591,6 +1042,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: method.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -604,13 +1056,13 @@ describe('Class: Logger', () => { describe('Feature: X-Ray Trace ID injection', () => { test('when the `_X_AMZN_TRACE_ID` environment variable is set it parses it correctly and adds the Trace ID to the log', () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act if (logger[methodOfLogger]) { logger[methodOfLogger]('foo'); @@ -623,6 +1075,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: method.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -633,13 +1086,13 @@ describe('Class: Logger', () => { test('when the `_X_AMZN_TRACE_ID` environment variable is NOT set it parses it correctly and adds the Trace ID to the log', () => { // Prepare delete process.env._X_AMZN_TRACE_ID; - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); - + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); // Act if (logger[methodOfLogger]) { logger[methodOfLogger]('foo'); @@ -652,6 +1105,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: method.toUpperCase(), message: 'foo', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', }) @@ -662,12 +1116,13 @@ describe('Class: Logger', () => { describe('Feature: handle safely unexpected errors', () => { test('when a logged item references itself, the logger ignores the keys that cause a circular reference', () => { // Prepare - const logger: Logger = createLogger({ + const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); const circularObject = { foo: 'bar', self: {}, @@ -693,6 +1148,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: method.toUpperCase(), message: 'A log with a circular reference', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -706,9 +1162,7 @@ describe('Class: Logger', () => { test('when a logged item has BigInt value, it does not throw TypeError', () => { // Prepare const logger = new Logger(); - jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); + jest.spyOn(logger['console'], getConsoleMethod(methodOfLogger)); const message = `This is an ${methodOfLogger} log with BigInt value`; const logItem = { value: BigInt(42) }; const errorMessage = 'Do not know how to serialize a BigInt'; @@ -722,9 +1176,10 @@ describe('Class: Logger', () => { test('when a logged item has a BigInt value, it prints the log with value as a string', () => { // Prepare const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); const message = `This is an ${methodOfLogger} log with BigInt value`; const logItem = { value: BigInt(42) }; @@ -738,6 +1193,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: methodOfLogger.toUpperCase(), message: message, + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -749,9 +1205,10 @@ describe('Class: Logger', () => { test('when a logged item has empty string, null, or undefined values, it removes it', () => { // Prepare const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], getConsoleMethod(methodOfLogger)) - .mockImplementation(); + const consoleSpy = jest.spyOn( + logger['console'], + getConsoleMethod(methodOfLogger) + ); const message = `This is an ${methodOfLogger} log with empty, null, and undefined values`; const logItem = { value: 42, @@ -770,6 +1227,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: methodOfLogger.toUpperCase(), message: message, + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -782,7 +1240,7 @@ describe('Class: Logger', () => { ); describe('Method: addContext', () => { - test('when called during a cold start invocation, it populates the logger PowertoolLogData object with coldStart set to TRUE', () => { + test('when called during a cold start invocation, it populates the logger powertoolsLogData object with coldStart set to TRUE', () => { // Prepare const logger = new Logger(); @@ -790,37 +1248,18 @@ describe('Class: Logger', () => { logger.addContext(context); // Assess - expect(logger).toEqual({ - console: expect.any(Console), - coldStart: false, // This is now false because the `coldStart` attribute has been already accessed once by the `addContext` method - customConfigService: undefined, - defaultServiceName: 'service_undefined', - envVarsService: expect.any(EnvironmentVariablesService), - logEvent: false, - logIndentation: 0, - logFormatter: expect.any(PowertoolLogFormatter), - logLevel: 8, - logLevelThresholds: { - ...logLevelThresholds, - }, - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - awsRegion: 'eu-west-1', - environment: '', - lambdaContext: { - awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', - coldStart: true, - functionName: 'foo-bar-function', - functionVersion: '$LATEST', - invokedFunctionArn: - 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - memoryLimitInMB: 128, - }, - sampleRateValue: undefined, - serviceName: 'hello-world', - }, - }); + expect(logger).toEqual( + expect.objectContaining({ + coldStart: false, // This is now false because the `coldStart` attribute has been already accessed once by the `addContext` method + powertoolsLogData: expect.objectContaining({ + lambdaContext: expect.objectContaining({ + coldStart: true, + }), + sampleRateValue: 0, + serviceName: 'hello-world', + }), + }) + ); }); test('when called with a context object, the object is not mutated', () => { @@ -867,7 +1306,7 @@ describe('Class: Logger', () => { // Assess expect(logger).toEqual( expect.objectContaining({ - powertoolLogData: expect.objectContaining({ + powertoolsLogData: expect.objectContaining({ lambdaContext: expect.objectContaining({ awsRequestId: context2.awsRequestId, }), @@ -1045,9 +1484,7 @@ describe('Class: Logger', () => { // Prepare const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); + const consoleSpy = jest.spyOn(logger['console'], 'info'); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext() public async handler( @@ -1075,11 +1512,12 @@ describe('Class: Logger', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'This is an INFO log with some context', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -1090,9 +1528,7 @@ describe('Class: Logger', () => { test('it captures Lambda context information and adds it in the printed logs', async () => { // Prepare const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); + const consoleSpy = jest.spyOn(logger['console'], 'info'); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext() public async handler( @@ -1117,6 +1553,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: 'INFO', message: 'An INFO log without context!', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -1128,11 +1565,12 @@ describe('Class: Logger', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'This is an INFO log with some context', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -1144,9 +1582,7 @@ describe('Class: Logger', () => { // Prepare const expectedReturnValue = 'Lambda invoked!'; const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); + const consoleSpy = jest.spyOn(logger['console'], 'info'); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext() public async handler( @@ -1174,6 +1610,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: 'INFO', message: 'An INFO log without context!', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -1185,11 +1622,12 @@ describe('Class: Logger', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'This is an INFO log with some context', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -1206,7 +1644,6 @@ describe('Class: Logger', () => { biz: 'baz', }, }); - jest.spyOn(logger['console'], 'debug').mockImplementation(); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext({ clearState: true }) public async handler( @@ -1252,7 +1689,6 @@ describe('Class: Logger', () => { biz: 'baz', }, }); - jest.spyOn(logger['console'], 'debug').mockImplementation(); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext({ clearState: true }) public async handler( @@ -1294,9 +1730,7 @@ describe('Class: Logger', () => { const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); + const consoleSpy = jest.spyOn(logger['console'], 'info'); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext({ logEvent: true }) public async handler( @@ -1320,18 +1754,18 @@ describe('Class: Logger', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'Lambda invocation event', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - key1: 'value1', - key2: 'value2', - key3: 'value3', + foo: 'bar', + bar: 'baz', }, }) ); @@ -1343,10 +1777,7 @@ describe('Class: Logger', () => { const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); - + const consoleSpy = jest.spyOn(logger['console'], 'info'); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext() public async handler( @@ -1370,18 +1801,18 @@ describe('Class: Logger', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'Lambda invocation event', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - key1: 'value1', - key2: 'value2', - key3: 'value3', + foo: 'bar', + bar: 'baz', }, }) ); @@ -1392,10 +1823,7 @@ describe('Class: Logger', () => { const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); - + const consoleSpy = jest.spyOn(logger['console'], 'info'); class LambdaFunction implements LambdaInterface { private readonly memberVariable: string; @@ -1431,11 +1859,12 @@ describe('Class: Logger', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'memberVariable:someValue', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -1452,9 +1881,7 @@ describe('Class: Logger', () => { const logger = new Logger({ logLevel: 'DEBUG', }); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); + const consoleSpy = jest.spyOn(logger['console'], 'info'); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext() public async handler( @@ -1494,9 +1921,7 @@ describe('Class: Logger', () => { version: '1.0.0', }, }); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); + const consoleSpy = jest.spyOn(logger['console'], 'info'); class LambdaFunction implements LambdaInterface { @logger.injectLambdaContext({ clearState: true, logEvent: true }) public async handler( @@ -1548,29 +1973,6 @@ describe('Class: Logger', () => { }); }); - describe('Method: refreshSampleRateCalculation', () => { - test('it recalculates whether the current Lambda invocation logs will be printed or not', () => { - // Prepare - const logger = new Logger({ - logLevel: 'ERROR', - sampleRateValue: 0.1, // 10% probability - }); - let logsSampledCount = 0; - - // Act - for (let i = 0; i < 1000; i++) { - logger.refreshSampleRateCalculation(); - if (logger.getLogsSampled() === true) { - logsSampledCount++; - } - } - - // Assess - expect(logsSampledCount > 50).toBe(true); - expect(logsSampledCount < 150).toBe(true); - }); - }); - describe('Method: createChild', () => { test('child and grandchild loggers should have all the options of its ancestor', () => { // Prepare @@ -1594,44 +1996,52 @@ describe('Class: Logger', () => { expect(parentLogger === grandchildLogger).toBe(false); expect(parentLogger).toEqual({ - console: expect.any(Console), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), coldStart: true, customConfigService: undefined, defaultServiceName: 'service_undefined', envVarsService: expect.any(EnvironmentVariablesService), logEvent: false, logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), + logFormatter: expect.any(PowertoolsLogFormatter), logLevel: 8, logLevelThresholds: { ...logLevelThresholds, }, - logsSampled: false, persistentLogAttributes: {}, - powertoolLogData: { + powertoolsLogData: { awsRegion: 'eu-west-1', environment: '', - sampleRateValue: undefined, + sampleRateValue: 0, serviceName: 'parent-service-name', }, }); expect(childLogger).toEqual({ - console: expect.any(Console), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), coldStart: true, customConfigService: undefined, defaultServiceName: 'service_undefined', envVarsService: expect.any(EnvironmentVariablesService), logEvent: false, logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), + logFormatter: expect.any(PowertoolsLogFormatter), logLevel: 8, logLevelThresholds: { ...logLevelThresholds, }, - logsSampled: true, persistentLogAttributes: {}, - powertoolLogData: { + powertoolsLogData: { awsRegion: 'eu-west-1', environment: '', sampleRateValue: 1, @@ -1640,21 +2050,25 @@ describe('Class: Logger', () => { }); expect(grandchildLogger).toEqual({ - console: expect.any(Console), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), coldStart: true, customConfigService: undefined, defaultServiceName: 'service_undefined', envVarsService: expect.any(EnvironmentVariablesService), logEvent: false, logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), + logFormatter: expect.any(PowertoolsLogFormatter), logLevel: 8, logLevelThresholds: { ...logLevelThresholds, }, - logsSampled: true, persistentLogAttributes: {}, - powertoolLogData: { + powertoolsLogData: { awsRegion: 'eu-west-1', environment: '', sampleRateValue: 1, @@ -1682,7 +2096,7 @@ describe('Class: Logger', () => { ); const optionsWithSampleRateEnabled = { - sampleRateValue: 1, // 100% probability to make sure that the logs are sampled + sampleRateValue: 1, }; const childLoggerWithSampleRateEnabled = parentLogger.createChild( optionsWithSampleRateEnabled @@ -1699,77 +2113,94 @@ describe('Class: Logger', () => { expect(parentLogger === childLogger).toBe(false); expect(childLogger).toEqual({ ...parentLogger, - console: expect.any(Console), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), }); expect(parentLogger === childLoggerWithPermanentAttributes).toBe(false); expect(parentLogger === childLoggerWithSampleRateEnabled).toBe(false); expect(parentLogger === childLoggerWithErrorLogLevel).toBe(false); expect(parentLogger).toEqual({ - console: expect.any(Console), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), coldStart: true, customConfigService: undefined, defaultServiceName: 'service_undefined', envVarsService: expect.any(EnvironmentVariablesService), logEvent: false, logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), + logFormatter: expect.any(PowertoolsLogFormatter), logLevel: 8, logLevelThresholds: { ...logLevelThresholds, }, - logsSampled: false, persistentLogAttributes: {}, - powertoolLogData: { + powertoolsLogData: { awsRegion: 'eu-west-1', environment: '', - sampleRateValue: undefined, + sampleRateValue: 0, serviceName: 'hello-world', }, }); expect(childLoggerWithPermanentAttributes).toEqual({ - console: expect.any(Console), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), coldStart: true, customConfigService: undefined, defaultServiceName: 'service_undefined', envVarsService: expect.any(EnvironmentVariablesService), logEvent: false, logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), + logFormatter: expect.any(PowertoolsLogFormatter), logLevel: 8, logLevelThresholds: { ...logLevelThresholds, }, - logsSampled: false, persistentLogAttributes: { extra: 'This is an attribute that will be logged only by the child logger', }, - powertoolLogData: { + powertoolsLogData: { awsRegion: 'eu-west-1', environment: '', - sampleRateValue: undefined, + sampleRateValue: 0, serviceName: 'hello-world', }, }); expect(childLoggerWithSampleRateEnabled).toEqual({ - console: expect.any(Console), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), coldStart: true, customConfigService: undefined, defaultServiceName: 'service_undefined', envVarsService: expect.any(EnvironmentVariablesService), logEvent: false, logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), + logFormatter: expect.any(PowertoolsLogFormatter), logLevel: 8, logLevelThresholds: { ...logLevelThresholds, }, - logsSampled: true, persistentLogAttributes: {}, - powertoolLogData: { + powertoolsLogData: { awsRegion: 'eu-west-1', environment: '', sampleRateValue: 1, @@ -1778,24 +2209,28 @@ describe('Class: Logger', () => { }); expect(childLoggerWithErrorLogLevel).toEqual({ - console: expect.any(Console), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), coldStart: true, customConfigService: undefined, defaultServiceName: 'service_undefined', envVarsService: expect.any(EnvironmentVariablesService), logEvent: false, logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), + logFormatter: expect.any(PowertoolsLogFormatter), logLevel: 20, logLevelThresholds: { ...logLevelThresholds, }, - logsSampled: false, persistentLogAttributes: {}, - powertoolLogData: { + powertoolsLogData: { awsRegion: 'eu-west-1', environment: '', - sampleRateValue: undefined, + sampleRateValue: 0, serviceName: 'hello-world', }, }); @@ -1803,7 +2238,6 @@ describe('Class: Logger', () => { test('child logger should have same keys in persistentLogAttributes as its parent', () => { // Prepare - const INDENTATION = LogJsonIndent.COMPACT; const parentLogger = new Logger(); const childLogger = parentLogger.createChild(); @@ -1821,88 +2255,25 @@ describe('Class: Logger', () => { childLoggerWithKeys.removeKeys(['test_key']); // Assess - expect(childLogger).toEqual({ - console: expect.any(Console), - coldStart: true, - customConfigService: undefined, - defaultServiceName: 'service_undefined', - envVarsService: expect.any(EnvironmentVariablesService), - logEvent: false, - logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), - logLevel: 8, - logLevelThresholds: { - ...logLevelThresholds, - }, - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - awsRegion: 'eu-west-1', - environment: '', - sampleRateValue: undefined, - serviceName: 'hello-world', - }, - }); + expect(childLogger.getPersistentLogAttributes()).toEqual({}); - expect(childLoggerWithKeys).toEqual({ - console: expect.any(Console), - coldStart: true, - customConfigService: undefined, - defaultServiceName: 'service_undefined', - envVarsService: expect.any(EnvironmentVariablesService), - logEvent: false, - logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), - logLevel: 8, - logLevelThresholds: { - ...logLevelThresholds, - }, - logsSampled: false, - persistentLogAttributes: { - aws_account_id: '123456789012', - aws_region: 'eu-west-1', - logger: { - name: 'aws-lambda-powertool-typescript', - version: '0.2.4', - }, - }, - powertoolLogData: { - awsRegion: 'eu-west-1', - environment: '', - sampleRateValue: undefined, - serviceName: 'hello-world', + expect(childLoggerWithKeys.getPersistentLogAttributes()).toEqual({ + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + logger: { + name: 'aws-lambda-powertool-typescript', + version: '0.2.4', }, }); - expect(parentLogger).toEqual({ - console: expect.any(Console), - coldStart: true, - customConfigService: undefined, - defaultServiceName: 'service_undefined', - envVarsService: expect.any(EnvironmentVariablesService), - logEvent: false, - logIndentation: INDENTATION, - logFormatter: expect.any(PowertoolLogFormatter), - logLevel: 8, - logLevelThresholds: { - ...logLevelThresholds, - }, - logsSampled: false, - persistentLogAttributes: { - aws_account_id: '123456789012', - aws_region: 'eu-west-1', - logger: { - name: 'aws-lambda-powertool-typescript', - version: '0.2.4', - }, - test_key: 'key-for-test', - }, - powertoolLogData: { - awsRegion: 'eu-west-1', - environment: '', - sampleRateValue: undefined, - serviceName: 'hello-world', + expect(parentLogger.getPersistentLogAttributes()).toEqual({ + aws_account_id: '123456789012', + aws_region: 'eu-west-1', + logger: { + name: 'aws-lambda-powertool-typescript', + version: '0.2.4', }, + test_key: 'key-for-test', }); }); @@ -1915,42 +2286,31 @@ describe('Class: Logger', () => { const childLoggerWithContext = parentLogger.createChild(); // Assess - expect(childLoggerWithContext).toEqual({ - console: expect.any(Console), - coldStart: false, // This is now false because the `coldStart` attribute has been already accessed once by the `addContext` method - customConfigService: undefined, - defaultServiceName: 'service_undefined', - envVarsService: expect.any(EnvironmentVariablesService), - logEvent: false, - logIndentation: 0, - logFormatter: expect.any(PowertoolLogFormatter), - logLevel: 8, - logLevelThresholds: { - ...logLevelThresholds, - }, - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - awsRegion: 'eu-west-1', - environment: '', - lambdaContext: { - awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', - coldStart: true, - functionName: 'foo-bar-function', - functionVersion: '$LATEST', - invokedFunctionArn: - 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - memoryLimitInMB: 128, + expect(childLoggerWithContext).toEqual( + expect.objectContaining({ + coldStart: false, // This is now false because the `coldStart` attribute has been already accessed once by the `addContext` method + powertoolsLogData: { + awsRegion: 'eu-west-1', + environment: '', + lambdaContext: { + awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', + coldStart: true, + functionName: 'foo-bar-function', + functionVersion: '$LATEST', + invokedFunctionArn: + 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', + memoryLimitInMB: '128', + }, + sampleRateValue: 0, + serviceName: 'hello-world', }, - sampleRateValue: undefined, - serviceName: 'hello-world', - }, - }); + }) + ); }); test('child logger should have the same logFormatter as its parent', () => { // Prepare - class MyCustomLogFormatter extends PowertoolLogFormatter {} + class MyCustomLogFormatter extends PowertoolsLogFormatter {} const parentLogger = new Logger({ logFormatter: new MyCustomLogFormatter(), }); @@ -1968,7 +2328,7 @@ describe('Class: Logger', () => { test('child logger with custom logFormatter in options should have provided logFormatter', () => { // Prepare - class MyCustomLogFormatter extends PowertoolLogFormatter {} + class MyCustomLogFormatter extends PowertoolsLogFormatter {} const parentLogger = new Logger(); // Act @@ -1979,7 +2339,7 @@ describe('Class: Logger', () => { // Assess expect(parentLogger).toEqual( expect.objectContaining({ - logFormatter: expect.any(PowertoolLogFormatter), + logFormatter: expect.any(PowertoolsLogFormatter), }) ); @@ -1992,13 +2352,13 @@ describe('Class: Logger', () => { test('child logger should have exact same attributes as the parent logger created with all non-default options', () => { // Prepare - class MyCustomLogFormatter extends PowertoolLogFormatter {} + class MyCustomLogFormatter extends PowertoolsLogFormatter {} class MyCustomEnvironmentVariablesService extends EnvironmentVariablesService {} const options: ConstructorOptions = { logLevel: 'ERROR', serviceName: 'test-service-name', - sampleRateValue: 0.77, + sampleRateValue: 1, logFormatter: new MyCustomLogFormatter(), customConfigService: new MyCustomEnvironmentVariablesService(), persistentLogAttributes: { @@ -2015,8 +2375,12 @@ describe('Class: Logger', () => { // Assess expect(childLogger).toEqual({ ...parentLogger, - console: expect.any(Console), - logsSampled: expect.any(Boolean), + console: expect.objectContaining({ + debug: expect.any(Function), + error: expect.any(Function), + info: expect.any(Function), + warn: expect.any(Function), + }), }); expect(childLogger).toEqual( @@ -2037,10 +2401,7 @@ describe('Class: Logger', () => { test('When the feature is disabled, it DOES NOT log the event', () => { // Prepare const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); - + const consoleSpy = jest.spyOn(logger['console'], 'info'); // Act logger.logEventIfEnabled(event); @@ -2054,10 +2415,7 @@ describe('Class: Logger', () => { something: 'happened!', }; const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); - + const consoleSpy = jest.spyOn(logger['console'], 'info'); // Act logger.logEventIfEnabled(event, true); @@ -2068,6 +2426,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: 'INFO', message: 'Lambda invocation event', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -2085,9 +2444,7 @@ describe('Class: Logger', () => { process.env.POWERTOOLS_DEV = 'true'; const INDENTATION = LogJsonIndent.PRETTY; const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); + const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); // Act logger.info('Message with pretty identation'); @@ -2100,6 +2457,7 @@ describe('Class: Logger', () => { { level: 'INFO', message: 'Message with pretty identation', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -2113,10 +2471,7 @@ describe('Class: Logger', () => { test('when the `POWERTOOLS_DEV` env var is NOT SET it makes log output as one-liner', () => { // Prepare const logger = new Logger(); - const consoleSpy = jest - .spyOn(logger['console'], 'info') - .mockImplementation(); - + const consoleSpy = jest.spyOn(logger['console'], 'info'); // Act logger.info('Message without pretty identation'); @@ -2127,6 +2482,7 @@ describe('Class: Logger', () => { JSON.stringify({ level: 'INFO', message: 'Message without pretty identation', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', @@ -2199,4 +2555,333 @@ describe('Class: Logger', () => { ); }); }); + + describe('Feature: Sampling debug logs', () => { + test('when sample rate is set in constructor, it DOES change log level to DEBUG', () => { + // Prepare & Act + const logger: Logger = new Logger({ + logLevel: 'ERROR', + sampleRateValue: 1, + }); + + // Assess + expect(logger.level).toBe(8); + expect(logger.getLevelName()).toBe('DEBUG'); + }); + + test('when sample rate is set in custom config service, it DOES change log level to DEBUG', () => { + // Prepare & Act + class MyCustomEnvironmentVariablesService extends EnvironmentVariablesService { + private sampleRateValue = 1; + public getSampleRateValue(): number { + return this.sampleRateValue; + } + } + + const loggerOptions: ConstructorOptions = { + logLevel: 'ERROR', + customConfigService: new MyCustomEnvironmentVariablesService(), + }; + const logger: Logger = new Logger(loggerOptions); + + // Assess + expect(logger.level).toBe(8); + expect(logger.getLevelName()).toBe('DEBUG'); + }); + + test('when sample rate is set in environmental variable, it DOES change log level to DEBUG', () => { + // Prepare & Act + process.env.POWERTOOLS_LOGGER_SAMPLE_RATE = '1'; + + const logger: Logger = new Logger({ + logLevel: 'ERROR', + }); + + // Assess + expect(logger.level).toBe(8); + expect(logger.getLevelName()).toBe('DEBUG'); + }); + + test('when sample rate is disabled it DOES NOT changes log level to DEBUG', () => { + // Prepare & Act + const logger: Logger = new Logger({ + logLevel: 'ERROR', + sampleRateValue: 0, + }); + + // Assess + expect(logger.level).toBe(20); + expect(logger.getLevelName()).toBe('ERROR'); + }); + + test('when sample rate is set in constructor, custom config, and environmental variable, it should prioritize constructor value', () => { + // Prepare + process.env.POWERTOOLS_LOGGER_SAMPLE_RATE = '0.5'; + + class MyCustomEnvironmentVariablesService extends EnvironmentVariablesService { + private sampleRateValue = 0.75; + public getSampleRateValue(): number { + return this.sampleRateValue; + } + } + const loggerOptions: ConstructorOptions = { + sampleRateValue: 1, + customConfigService: new MyCustomEnvironmentVariablesService(), + }; + const logger: Logger = new Logger(loggerOptions); + const consoleSpy = jest.spyOn(logger['console'], 'info'); + // Act + logger.info('foo'); + + // Assess + expect(consoleSpy).toBeCalledTimes(1); + expect(consoleSpy).toHaveBeenNthCalledWith( + 1, + JSON.stringify({ + level: 'INFO', + message: 'foo', + sampling_rate: 1, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + }); + + test('when sample rate is set in custom config and environmental variable, it should prioritize custom config value', () => { + // Prepare + process.env.POWERTOOLS_LOGGER_SAMPLE_RATE = '0.75'; + + class MyCustomEnvironmentVariablesService extends EnvironmentVariablesService { + private sampleRateValue = 1; + public getSampleRateValue(): number { + return this.sampleRateValue; + } + } + const loggerOptions: ConstructorOptions = { + customConfigService: new MyCustomEnvironmentVariablesService(), + }; + const logger: Logger = new Logger(loggerOptions); + const consoleSpy = jest.spyOn(logger['console'], 'info'); + // Act + logger.info('foo'); + + // Assess + expect(consoleSpy).toBeCalledTimes(1); + expect(consoleSpy).toHaveBeenNthCalledWith( + 1, + JSON.stringify({ + level: 'INFO', + message: 'foo', + sampling_rate: 1, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + }); + + test('when sample rate is set in environmental variable, it should use POWERTOOLS_LOGGER_SAMPLE_RATE value', () => { + // Prepare + process.env.POWERTOOLS_LOGGER_SAMPLE_RATE = '1'; + const logger: Logger = new Logger(); + const consoleSpy = jest.spyOn(logger['console'], 'debug'); + // Act + logger.debug('foo'); + + // Assess + expect(consoleSpy).toBeCalledTimes(2); + expect(consoleSpy).toHaveBeenNthCalledWith( + 1, + JSON.stringify({ + level: 'DEBUG', + message: 'Setting log level to DEBUG due to sampling rate', + sampling_rate: 1, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + expect(consoleSpy).toHaveBeenNthCalledWith( + 2, + JSON.stringify({ + level: 'DEBUG', + message: 'foo', + sampling_rate: 1, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + }); + + test('when sample rate is set in custom config service, it should use custom config service value', () => { + // Prepare + class MyCustomEnvironmentVariablesService extends EnvironmentVariablesService { + private sampleRateValue = 1; + public getSampleRateValue(): number { + return this.sampleRateValue; + } + } + const loggerOptions: ConstructorOptions = { + customConfigService: new MyCustomEnvironmentVariablesService(), + }; + + const logger: Logger = new Logger(loggerOptions); + const consoleSpy = jest.spyOn(logger['console'], 'info'); + // Act + logger.info('foo'); + + // Assess + expect(consoleSpy).toBeCalledTimes(1); + expect(consoleSpy).toHaveBeenNthCalledWith( + 1, + JSON.stringify({ + level: 'INFO', + message: 'foo', + sampling_rate: 1, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + }); + + test('when sample rate in constructor is out of expected range, it should be ignored', () => { + // Prepare + const logger: Logger = new Logger({ + logLevel: 'INFO', + sampleRateValue: 42, + }); + const consoleSpy = jest.spyOn(logger['console'], 'info'); + // Act + logger.info('foo'); + + // Assess + expect(consoleSpy).toBeCalledTimes(1); + expect(consoleSpy).toHaveBeenNthCalledWith( + 1, + JSON.stringify({ + level: 'INFO', + message: 'foo', + sampling_rate: 0, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + }); + + test('when sample rate in custom config service is out of expected range, it should be ignored', () => { + // Prepare + class MyCustomEnvironmentVariablesService extends EnvironmentVariablesService { + private sampleRateValue = 42; + public getSampleRateValue(): number { + return this.sampleRateValue; + } + } + const loggerOptions: ConstructorOptions = { + logLevel: 'INFO', + customConfigService: new MyCustomEnvironmentVariablesService(), + }; + + const logger: Logger = new Logger(loggerOptions); + const consoleSpy = jest.spyOn(logger['console'], 'info'); + // Act + logger.info('foo'); + + // Assess + expect(consoleSpy).toBeCalledTimes(1); + expect(consoleSpy).toHaveBeenNthCalledWith( + 1, + JSON.stringify({ + level: 'INFO', + message: 'foo', + sampling_rate: 0, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + }); + + test('when sample rate in environmental variable is out of expected range, it should be ignored', () => { + // Prepare + process.env.POWERTOOLS_LOGGER_SAMPLE_RATE = '42'; + const logger: Logger = new Logger({ + logLevel: 'INFO', + }); + const consoleSpy = jest.spyOn(logger['console'], 'info'); + // Act + logger.info('foo'); + + // Assess + expect(consoleSpy).toBeCalledTimes(1); + expect(consoleSpy).toHaveBeenNthCalledWith( + 1, + JSON.stringify({ + level: 'INFO', + message: 'foo', + sampling_rate: 0, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + }); + + describe('Method: refreshSampleRateCalculation', () => { + test('when sample rate calculation is refreshed, it DOES NOT overwrite the sample rate value', () => { + // Prepare + const logger = new Logger({ + logLevel: 'INFO', + sampleRateValue: 1, + }); + const consoleSpy = jest.spyOn(logger['console'], 'info'); + // Act + logger.refreshSampleRateCalculation(); + logger.info('foo'); + + // Assess + expect(consoleSpy).toBeCalledTimes(1); + expect(consoleSpy).toHaveBeenNthCalledWith( + 1, + JSON.stringify({ + level: 'INFO', + message: 'foo', + sampling_rate: 1, + service: 'hello-world', + timestamp: '2016-06-20T12:08:10.000Z', + xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', + }) + ); + }); + + test('when sample rate calculation is refreshed, it respects probability sampling and change log level to DEBUG ', () => { + // Prepare + const logger = new Logger({ + logLevel: 'ERROR', + sampleRateValue: 0.1, // 10% probability + }); + + let logLevelChangedToDebug = 0; + const numOfIterations = 1000; + const minExpected = numOfIterations * 0.05; // Min expected based on 5% probability + const maxExpected = numOfIterations * 0.15; // Max expected based on 15% probability + + // Act + for (let i = 0; i < numOfIterations; i++) { + logger.refreshSampleRateCalculation(); + if (logger.getLevelName() === 'DEBUG') { + logLevelChangedToDebug++; + logger.setLogLevel('ERROR'); + } + } + + // Assess + expect(logLevelChangedToDebug).toBeGreaterThanOrEqual(minExpected); + expect(logLevelChangedToDebug).toBeLessThanOrEqual(maxExpected); + }); + }); + }); }); diff --git a/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts b/packages/logger/tests/unit/formatter/PowertoolsLogFormatter.test.ts similarity index 71% rename from packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts rename to packages/logger/tests/unit/formatter/PowertoolsLogFormatter.test.ts index 9176ce8d8d..d3c1f289cb 100644 --- a/packages/logger/tests/unit/formatter/PowertoolLogFormatter.test.ts +++ b/packages/logger/tests/unit/formatter/PowertoolsLogFormatter.test.ts @@ -1,13 +1,15 @@ /** * Test Logger formatter * - * @group unit/logger/all + * @group unit/logger/logFormatter */ import { AssertionError } from 'node:assert'; -import { PowertoolLogFormatter } from '../../../src/formatter'; -import { UnformattedAttributes } from '../../../src/types'; +import { PowertoolsLogFormatter } from '../../../src/formatter/PowertoolsLogFormatter.js'; +import { LogItem } from '../../../src/index.js'; +import type { UnformattedAttributes } from '../../../src/types/Logger.js'; +import type { LogAttributes } from '../../../src/types/Log.js'; -describe('Class: PowertoolLogFormatter', () => { +describe('Class: PowertoolsLogFormatter', () => { const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); @@ -18,9 +20,9 @@ describe('Class: PowertoolLogFormatter', () => { describe('Method: formatAttributes', () => { test('when optional parameters DO NOT have a value set, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); const unformattedAttributes: UnformattedAttributes = { - sampleRateValue: undefined, + sampleRateValue: 0, awsRegion: 'eu-west-1', environment: '', serviceName: 'hello-world', @@ -29,12 +31,16 @@ describe('Class: PowertoolLogFormatter', () => { timestamp: new Date(), message: 'This is a WARN log', }; + const additionalLogAttributes: LogAttributes = {}; // Act - const value = formatter.formatAttributes(unformattedAttributes); + const value = formatter.formatAttributes( + unformattedAttributes, + additionalLogAttributes + ); // Assess - expect(value).toEqual({ + expect(value.getAttributes()).toEqual({ cold_start: undefined, function_arn: undefined, function_memory_size: undefined, @@ -42,16 +48,17 @@ describe('Class: PowertoolLogFormatter', () => { function_request_id: undefined, level: 'WARN', message: 'This is a WARN log', - sampling_rate: undefined, + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', }); + expect(value).toBeInstanceOf(LogItem); }); test('when optional parameters DO have a value set, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); const unformattedAttributes: UnformattedAttributes = { sampleRateValue: 0.25, awsRegion: 'eu-west-1', @@ -64,7 +71,7 @@ describe('Class: PowertoolLogFormatter', () => { error: new Error('Something happened!'), lambdaContext: { functionName: 'my-lambda-function', - memoryLimitInMB: 123, + memoryLimitInMB: '123', functionVersion: '1.23.3', coldStart: true, invokedFunctionArn: @@ -72,15 +79,19 @@ describe('Class: PowertoolLogFormatter', () => { awsRequestId: 'abcdefg123456789', }, }; + const additionalLogAttributes: LogAttributes = {}; // Act - const value = formatter.formatAttributes(unformattedAttributes); + const value = formatter.formatAttributes( + unformattedAttributes, + additionalLogAttributes + ); // Assess - expect(value).toEqual({ + expect(value.getAttributes()).toEqual({ cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:Example', - function_memory_size: 123, + function_memory_size: '123', function_name: 'my-lambda-function', function_request_id: 'abcdefg123456789', level: 'WARN', @@ -96,45 +107,37 @@ describe('Class: PowertoolLogFormatter', () => { describe('Method: formatError', () => { test('when an error of type Error is passed, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act & Assess const formattedError = formatter.formatError(new Error('Ouch!')); expect(formattedError).toEqual({ - location: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+$/ - ), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+$/), message: 'Ouch!', name: 'Error', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), }); }); test('when an error of type ReferenceError is passed, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act & Assess const formattedReferenceError = formatter.formatError( new ReferenceError('doesNotExist is not defined') ); expect(formattedReferenceError).toEqual({ - location: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+$/ - ), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+$/), message: 'doesNotExist is not defined', name: 'ReferenceError', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), }); }); test('when an error of type AssertionError is passed, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act & Assess const formattedAssertionError = formatter.formatError( @@ -147,109 +150,97 @@ describe('Class: PowertoolLogFormatter', () => { ); expect(formattedAssertionError).toEqual({ location: expect.stringMatching( - /(node:)*internal\/assert\/assertion_error(.js)*:[0-9]+$/ + /(node:)*internal\/assert\/assertion_error(.js)*:\d+$/ ), message: expect.stringMatching(/Expected values to be strictly equal/), name: 'AssertionError', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), }); }); test('when an error of type RangeError is passed, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act & Assess const formattedRangeError = formatter.formatError( new RangeError('The argument must be between 10 and 20') ); expect(formattedRangeError).toEqual({ - location: expect.stringMatching(/PowertoolLogFormatter.test.ts:[0-9]+/), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+/), message: 'The argument must be between 10 and 20', name: 'RangeError', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), }); }); test('when an error of type ReferenceError is passed, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act & Assess const formattedError = formatter.formatError( new ReferenceError('foo is not defined') ); expect(formattedError).toEqual({ - location: expect.stringMatching(/PowertoolLogFormatter.test.ts:[0-9]+/), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+/), message: 'foo is not defined', name: 'ReferenceError', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), }); }); test('when an error of type SyntaxError is passed, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act & Assess const formattedSyntaxError = formatter.formatError( new SyntaxError(`Unexpected identifier 'bar'`) ); expect(formattedSyntaxError).toEqual({ - location: expect.stringMatching(/PowertoolLogFormatter.test.ts:[0-9]+/), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+/), message: `Unexpected identifier 'bar'`, name: 'SyntaxError', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), }); }); test('when an error of type TypeError is passed, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act & Assess const formattedTypeError = formatter.formatError( new TypeError(`Cannot read property 'foo' of null`) ); expect(formattedTypeError).toEqual({ - location: expect.stringMatching(/PowertoolLogFormatter.test.ts:[0-9]+/), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+/), message: expect.stringMatching(/Cannot read propert/), name: 'TypeError', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), }); }); test('when an error of type URIError is passed, it returns an object with expected structure and values', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act & Assess const formattedURIError = formatter.formatError( new URIError('URI malformed') ); expect(formattedURIError).toEqual({ - location: expect.stringMatching(/PowertoolLogFormatter.test.ts:[0-9]+/), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+/), message: 'URI malformed', name: 'URIError', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), }); }); test('when an error with cause of type Error is formatted, the cause key is included and formatted', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); class ErrorWithCause extends Error { public cause?: Error; public constructor(message: string, options?: { cause: Error }) { @@ -265,20 +256,16 @@ describe('Class: PowertoolLogFormatter', () => { // Assess expect(formattedURIError).toEqual({ - location: expect.stringMatching(/PowertoolLogFormatter.test.ts:[0-9]+/), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+/), message: 'foo', name: 'Error', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), cause: { - location: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+/ - ), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+/), message: 'bar', name: 'Error', stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ + /PowertoolsLogFormatter.test.ts:\d+:\d+/ ), }, }); @@ -286,7 +273,7 @@ describe('Class: PowertoolLogFormatter', () => { test('when an error with cause of type other than Error is formatted, the cause key is included as-is', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); class ErrorWithCause extends Error { public cause?: unknown; public constructor(message: string, options?: { cause: unknown }) { @@ -302,12 +289,10 @@ describe('Class: PowertoolLogFormatter', () => { // Assess expect(formattedURIError).toEqual({ - location: expect.stringMatching(/PowertoolLogFormatter.test.ts:[0-9]+/), + location: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+/), message: 'foo', name: 'Error', - stack: expect.stringMatching( - /PowertoolLogFormatter.test.ts:[0-9]+:[0-9]+/ - ), + stack: expect.stringMatching(/PowertoolsLogFormatter.test.ts:\d+:\d+/), cause: 'bar', }); }); @@ -316,7 +301,7 @@ describe('Class: PowertoolLogFormatter', () => { describe('Method: formatTimestamp', () => { test('it returns a datetime value ISO 8601 compliant', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); // Act const timestamp = formatter.formatTimestamp(new Date()); @@ -329,7 +314,7 @@ describe('Class: PowertoolLogFormatter', () => { describe('Method: getCodeLocation', () => { test('when the stack IS present, it returns a datetime value ISO 8601 compliant', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); const stack = 'Error: Things keep happening!\n' + ' at /home/foo/bar/file-that-threw-the-error.ts:22:5\n' + @@ -344,7 +329,7 @@ describe('Class: PowertoolLogFormatter', () => { test('when the stack IS NOT present, it returns a datetime value ISO 8601 compliant', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); const stack = undefined; // Act @@ -356,7 +341,7 @@ describe('Class: PowertoolLogFormatter', () => { test('when the stack IS NOT present, it returns a datetime value ISO 8601 compliant', () => { // Prepare - const formatter = new PowertoolLogFormatter(); + const formatter = new PowertoolsLogFormatter(); const stack = 'A weird stack trace...'; // Act diff --git a/packages/logger/tests/unit/helpers.test.ts b/packages/logger/tests/unit/helpers.test.ts deleted file mode 100644 index 39597f3be1..0000000000 --- a/packages/logger/tests/unit/helpers.test.ts +++ /dev/null @@ -1,453 +0,0 @@ -/** - * Test Logger helpers - * - * @group unit/logger/all - */ -import { Console } from 'console'; -import { - ConfigServiceInterface, - EnvironmentVariablesService, -} from '../../src/config'; -import { LogFormatter, PowertoolLogFormatter } from '../../src/formatter'; -import { ConstructorOptions, LogLevelThresholds } from '../../src/types'; -import { createLogger, Logger } from './../../src'; - -describe('Helper: createLogger function', () => { - const ENVIRONMENT_VARIABLES = process.env; - const logLevelThresholds: LogLevelThresholds = { - DEBUG: 8, - INFO: 12, - WARN: 16, - ERROR: 20, - CRITICAL: 24, - SILENT: 28, - }; - - beforeEach(() => { - jest.resetModules(); - process.env = { ...ENVIRONMENT_VARIABLES }; - }); - - afterAll(() => { - process.env = ENVIRONMENT_VARIABLES; - }); - - describe('LoggerOptions constructor parameters', () => { - test('when no constructor parameters are set, returns a Logger instance with the options set in the environment variables', () => { - // Prepare - const loggerOptions = undefined; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: '', - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - defaultServiceName: 'service_undefined', - logLevel: 8, - logFormatter: expect.any(PowertoolLogFormatter), - }) - ); - }); - - test('when no parameters are set, returns a Logger instance with the correct properties', () => { - // Prepare - const loggerOptions: ConstructorOptions = { - logLevel: 'WARN', - serviceName: 'my-lambda-service', - sampleRateValue: 1, - logFormatter: new PowertoolLogFormatter(), - customConfigService: new EnvironmentVariablesService(), - persistentLogAttributes: { - awsAccountId: '123456789', - }, - environment: 'prod', - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual({ - coldStart: true, - defaultServiceName: 'service_undefined', - customConfigService: expect.any(EnvironmentVariablesService), - envVarsService: expect.any(EnvironmentVariablesService), - logEvent: false, - logIndentation: 0, - logFormatter: expect.any(PowertoolLogFormatter), - logLevel: 16, - console: expect.any(Console), - logLevelThresholds: { - ...logLevelThresholds, - }, - logsSampled: true, - persistentLogAttributes: { - awsAccountId: '123456789', - }, - powertoolLogData: { - awsRegion: 'eu-west-1', - environment: 'prod', - sampleRateValue: 1, - serviceName: 'my-lambda-service', - }, - }); - }); - - test('when no constructor parameters and no environment variables are set, returns a Logger instance with the default properties', () => { - // Prepare - const loggerOptions = undefined; - delete process.env.POWERTOOLS_SERVICE_NAME; - delete process.env.POWERTOOLS_LOG_LEVEL; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual({ - coldStart: true, - customConfigService: undefined, - defaultServiceName: 'service_undefined', - envVarsService: expect.any(EnvironmentVariablesService), - logEvent: false, - logIndentation: 0, - logFormatter: expect.any(PowertoolLogFormatter), - logLevel: 12, - console: expect.any(Console), - logLevelThresholds: { - ...logLevelThresholds, - }, - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - awsRegion: 'eu-west-1', - environment: '', - sampleRateValue: undefined, - serviceName: 'service_undefined', - }, - }); - }); - - test('when a custom logFormatter is passed, returns a Logger instance with the correct properties', () => { - // Prepare - const loggerOptions: ConstructorOptions = { - logFormatter: expect.any(LogFormatter), - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: '', - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 8, - logFormatter: expect.any(LogFormatter), - }) - ); - }); - - test('when a custom serviceName is passed, returns a Logger instance with the correct properties', () => { - // Prepare - const loggerOptions: ConstructorOptions = { - serviceName: 'my-backend-service', - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: '', - serviceName: 'my-backend-service', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 8, - logFormatter: {}, - }) - ); - }); - - test('when a custom uppercase logLevel is passed, returns a Logger instance with the correct properties', () => { - // Prepare - const loggerOptions: ConstructorOptions = { - logLevel: 'ERROR', - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: '', - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 20, - logFormatter: expect.any(PowertoolLogFormatter), - }) - ); - }); - - test('when a custom lowercase logLevel is passed, returns a Logger instance with the correct properties', () => { - // Prepare - const loggerOptions: ConstructorOptions = { - logLevel: 'warn', - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: '', - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 16, - logFormatter: expect.any(PowertoolLogFormatter), - }) - ); - }); - - test('when no log level is set, returns a Logger instance with INFO level', () => { - // Prepare - const loggerOptions: ConstructorOptions = {}; - delete process.env.POWERTOOLS_LOG_LEVEL; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual({ - coldStart: true, - customConfigService: undefined, - defaultServiceName: 'service_undefined', - envVarsService: expect.any(EnvironmentVariablesService), - logEvent: false, - logIndentation: 0, - logFormatter: expect.any(PowertoolLogFormatter), - logLevel: 12, - console: expect.any(Console), - logLevelThresholds: { - ...logLevelThresholds, - }, - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - awsRegion: 'eu-west-1', - environment: '', - sampleRateValue: undefined, - serviceName: 'hello-world', - }, - }); - }); - - test('when a custom sampleRateValue is passed, returns a Logger instance with the correct properties', () => { - // Prepare - const loggerOptions: ConstructorOptions = { - sampleRateValue: 1, - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: true, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: 1, - awsRegion: 'eu-west-1', - environment: '', - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 8, - logFormatter: {}, - }) - ); - }); - - test('when a custom customConfigService is passed, returns a Logger instance with the correct properties', () => { - const configService: ConfigServiceInterface = { - get(name: string): string { - return `a-string-from-${name}`; - }, - getAwsLogLevel(): string { - return 'INFO'; - }, - getCurrentEnvironment(): string { - return 'dev'; - }, - getLogEvent(): boolean { - return true; - }, - getLogLevel(): string { - return 'INFO'; - }, - getSampleRateValue(): number | undefined { - return undefined; - }, - getServiceName(): string { - return 'my-backend-service'; - }, - isDevMode(): boolean { - return false; - }, - isValueTrue(): boolean { - return true; - }, - }; - // Prepare - const loggerOptions: ConstructorOptions = { - customConfigService: configService, - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: 'dev', - serviceName: 'my-backend-service', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: configService, - logLevel: 12, - logFormatter: {}, - }) - ); - }); - - test('when custom persistentLogAttributes is passed, returns a Logger instance with the correct properties', () => { - // Prepare - const loggerOptions: ConstructorOptions = { - persistentLogAttributes: { - aws_account_id: '123456789012', - aws_region: 'eu-west-1', - logger: { - name: 'aws-lambda-powertool-typescript', - version: '0.2.4', - }, - }, - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: { - aws_account_id: '123456789012', - aws_region: 'eu-west-1', - logger: { - name: 'aws-lambda-powertool-typescript', - version: '0.2.4', - }, - }, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: '', - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 8, - logFormatter: {}, - }) - ); - }); - - test('when a custom environment is passed, returns a Logger instance with the correct properties', () => { - // Prepare - const loggerOptions: ConstructorOptions = { - environment: 'dev', - }; - - // Act - const logger = createLogger(loggerOptions); - - // Assess - expect(logger).toBeInstanceOf(Logger); - expect(logger).toEqual( - expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: 'dev', - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 8, - logFormatter: {}, - }) - ); - }); - }); -}); diff --git a/packages/logger/tests/unit/middleware/middy.test.ts b/packages/logger/tests/unit/middleware/middy.test.ts index 8a9e09e261..532b923c03 100644 --- a/packages/logger/tests/unit/middleware/middy.test.ts +++ b/packages/logger/tests/unit/middleware/middy.test.ts @@ -1,31 +1,25 @@ /** * Test Logger middleware * - * @group unit/logger/all + * @group unit/logger/middleware */ -import { - ContextExamples as dummyContext, - Events as dummyEvent, -} from '@aws-lambda-powertools/commons'; -import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware'; -import { - ConfigServiceInterface, - EnvironmentVariablesService, -} from '../../../src/config'; -import { injectLambdaContext } from '../../../src/middleware/middy'; -import { Logger } from './../../../src'; +import { cleanupMiddlewares } from '@aws-lambda-powertools/commons'; +import context from '@aws-lambda-powertools/testing-utils/context'; import middy from '@middy/core'; -import { PowertoolLogFormatter } from '../../../src/formatter'; -import { Console } from 'console'; -import { Context } from 'aws-lambda'; +import type { Context } from 'aws-lambda'; +import { injectLambdaContext } from '../../../src/middleware/middy.js'; +import { ConfigServiceInterface } from '../../../src/types/ConfigServiceInterface.js'; +import { Logger } from './../../../src/Logger.js'; const mockDate = new Date(1466424490000); const dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => mockDate); describe('Middy middleware', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext.helloworldContext; - const event = dummyEvent.Custom.CustomEvent; + const event = { + foo: 'bar', + bar: 'baz', + }; beforeEach(() => { jest.resetModules(); @@ -55,12 +49,7 @@ describe('Middy middleware', () => { // Assess expect(logger).toEqual( expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: '', + powertoolsLogData: expect.objectContaining({ lambdaContext: { awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', coldStart: true, @@ -68,14 +57,9 @@ describe('Middy middleware', () => { functionVersion: '$LATEST', invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - memoryLimitInMB: 128, + memoryLimitInMB: '128', }, - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 8, - logFormatter: expect.any(PowertoolLogFormatter), + }), }) ); }); @@ -94,12 +78,7 @@ describe('Middy middleware', () => { // Assess const expectation = expect.objectContaining({ - logsSampled: false, - persistentLogAttributes: {}, - powertoolLogData: { - sampleRateValue: undefined, - awsRegion: 'eu-west-1', - environment: '', + powertoolsLogData: expect.objectContaining({ lambdaContext: { awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e812345678', coldStart: true, @@ -107,15 +86,9 @@ describe('Middy middleware', () => { functionVersion: '$LATEST', invokedFunctionArn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - memoryLimitInMB: 128, + memoryLimitInMB: '128', }, - serviceName: 'hello-world', - }, - envVarsService: expect.any(EnvironmentVariablesService), - customConfigService: undefined, - logLevel: 8, - logFormatter: expect.any(PowertoolLogFormatter), - console: expect.any(Console), + }), }); expect(logger).toEqual(expectation); expect(anotherLogger).toEqual(expectation); @@ -224,9 +197,7 @@ describe('Middy middleware', () => { }; }; const handler = middy( - ( - event: typeof dummyEvent.Custom.CustomEvent & { idx: number } - ): void => { + (event: { foo: string; bar: string } & { idx: number }): void => { // Add a key only at the first invocation, so we can check that it's cleared if (event.idx === 0) { logger.appendKeys({ @@ -280,18 +251,18 @@ describe('Middy middleware', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'Lambda invocation event', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - key1: 'value1', - key2: 'value2', - key3: 'value3', + foo: 'bar', + bar: 'baz', }, }) ); @@ -321,6 +292,9 @@ describe('Middy middleware', () => { getServiceName(): string { return 'my-backend-service'; }, + getXrayTraceId(): string | undefined { + return undefined; + }, isDevMode(): boolean { return false; }, @@ -350,18 +324,18 @@ describe('Middy middleware', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'Lambda invocation event', + sampling_rate: 0, service: 'my-backend-service', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - key1: 'value1', - key2: 'value2', - key3: 'value3', + foo: 'bar', + bar: 'baz', }, }) ); @@ -389,18 +363,18 @@ describe('Middy middleware', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'Lambda invocation event', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', event: { - key1: 'value1', - key2: 'value2', - key3: 'value3', + foo: 'bar', + bar: 'baz', }, }) ); @@ -428,11 +402,12 @@ describe('Middy middleware', () => { cold_start: true, function_arn: 'arn:aws:lambda:eu-west-1:123456789012:function:foo-bar-function', - function_memory_size: 128, + function_memory_size: '128', function_name: 'foo-bar-function', function_request_id: 'c6af9ac6-7b61-11e6-9a41-93e812345678', level: 'INFO', message: 'This is an INFO log', + sampling_rate: 0, service: 'hello-world', timestamp: '2016-06-20T12:08:10.000Z', xray_trace_id: '1-5759e988-bd862e3fe1be46a994272793', diff --git a/packages/logger/tsconfig.esm.json b/packages/logger/tsconfig.esm.json new file mode 100644 index 0000000000..123291b0cf --- /dev/null +++ b/packages/logger/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/esm.json" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/logger/tsconfig.json b/packages/logger/tsconfig.json index 1cb9d72773..d56a564ef6 100644 --- a/packages/logger/tsconfig.json +++ b/packages/logger/tsconfig.json @@ -1,10 +1,11 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./lib", - "rootDir": "./src", - }, - "include": [ - "./src/**/*" - ], + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./lib/cjs/", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" + }, + "include": [ + "./src/**/*" + ] } \ No newline at end of file diff --git a/packages/logger/typedoc.json b/packages/logger/typedoc.json index 3a1fad0dcf..bf0eba2a17 100644 --- a/packages/logger/typedoc.json +++ b/packages/logger/typedoc.json @@ -1,8 +1,5 @@ { "extends": ["../../typedoc.base.json"], - "entryPoints": [ - "./src/index.ts", - "./src/types" - ], + "entryPoints": ["./src/index.ts", "./src/types"], "readme": "README.md" -} \ No newline at end of file +} diff --git a/packages/metrics/jest.config.js b/packages/metrics/jest.config.cjs similarity index 86% rename from packages/metrics/jest.config.js rename to packages/metrics/jest.config.cjs index 3c1879fc3b..073bb4ee90 100644 --- a/packages/metrics/jest.config.js +++ b/packages/metrics/jest.config.cjs @@ -5,6 +5,9 @@ module.exports = { }, runner: 'groups', preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, @@ -14,7 +17,7 @@ module.exports = { roots: ['/src', '/tests'], testPathIgnorePatterns: ['/node_modules/'], testEnvironment: 'node', - coveragePathIgnorePatterns: ['/node_modules/'], + coveragePathIgnorePatterns: ['/node_modules/', '/types/'], coverageThreshold: { global: { statements: 100, diff --git a/packages/metrics/package.json b/packages/metrics/package.json index 2d52e3383a..cea4402ab8 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -12,17 +12,16 @@ "scripts": { "test": "npm run test:unit", "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", - "jest": "jest --detectOpenHandles --verbose", - "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e", "test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e", "test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e", "test:e2e": "jest --group=e2e", "watch": "jest --group=unit --watch ", - "build": "tsc --build --force", + "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", - "prebuild": "rimraf ./lib", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { @@ -30,8 +29,41 @@ }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/metrics#readme", "license": "MIT-0", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } + }, + "./middleware": { + "import": "./lib/esm/middleware/middy.js", + "require": "./lib/cjs/middleware/middy.js" + }, + "./types": { + "import": "./lib/esm/types/index.js", + "require": "./lib/cjs/types/index.js" + } + }, + "typesVersions": { + "*": { + "middleware": [ + "lib/cjs/middleware/middy.d.ts", + "lib/esm/middleware/middy.d.ts" + ], + "types": [ + "lib/cjs/types/index.d.ts", + "lib/esm/types/index.d.ts" + ] + } + }, + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "devDependencies": { "@aws-lambda-powertools/testing-utils": "file:../testing", "@aws-sdk/client-cloudwatch": "^3.515.0", @@ -67,4 +99,4 @@ "serverless", "nodejs" ] -} +} \ No newline at end of file diff --git a/packages/metrics/src/Metrics.ts b/packages/metrics/src/Metrics.ts index f968d9d70b..5b828379b3 100644 --- a/packages/metrics/src/Metrics.ts +++ b/packages/metrics/src/Metrics.ts @@ -1,30 +1,29 @@ import type { Callback, Context, Handler } from 'aws-lambda'; import { Console } from 'node:console'; import { Utility } from '@aws-lambda-powertools/commons'; -import type { MetricsInterface } from './MetricsInterface'; -import { - type ConfigServiceInterface, - EnvironmentVariablesService, -} from './config'; +import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types'; +import { EnvironmentVariablesService } from './config/EnvironmentVariablesService.js'; import { MAX_DIMENSION_COUNT, MAX_METRICS_SIZE, DEFAULT_NAMESPACE, COLD_START_METRIC, MAX_METRIC_VALUES_SIZE, -} from './constants'; + MetricUnit as MetricUnits, + MetricResolution as MetricResolutions, +} from './constants.js'; import { - MetricsOptions, - Dimensions, - EmfOutput, - HandlerMethodDecorator, - StoredMetrics, - ExtraOptions, - MetricUnit, - MetricUnits, - MetricResolution, - MetricDefinition, -} from './types'; + type MetricsOptions, + type Dimensions, + type EmfOutput, + type StoredMetrics, + type ExtraOptions, + type MetricDefinition, + type ConfigServiceInterface, + type MetricsInterface, + type MetricUnit, + type MetricResolution, +} from './types/index.js'; /** * ## Intro @@ -50,7 +49,8 @@ import { * * @example * ```typescript - * import { Metrics, logMetrics } from '@aws-lambda-powertools/metrics'; + * import { Metrics } from '@aws-lambda-powertools/metrics'; + * import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; * import middy from '@middy/core'; * * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); @@ -72,8 +72,8 @@ import { * @example * * ```typescript - * import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; - * import { LambdaInterface } from '@aws-lambda-powertools/commons'; + * import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; * * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); * @@ -82,7 +82,7 @@ import { * ⁣@metrics.logMetrics({ captureColdStartMetric: true, throwOnEmptyMetrics: true }) * public handler(_event: unknown, _context: unknown): Promise { * // ... - * metrics.addMetric('test-metric', MetricUnits.Count, 10); + * metrics.addMetric('test-metric', MetricUnit.Count, 10); * // ... * } * } @@ -98,13 +98,13 @@ import { * @example * * ```typescript - * import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + * import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; * * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); * * export const handler = async (_event: unknown, __context: unknown): Promise => { * metrics.captureColdStartMetric(); - * metrics.addMetric('test-metric', MetricUnits.Count, 10); + * metrics.addMetric('test-metric', MetricUnit.Count, 10); * metrics.publishStoredMetrics(); * }; * ``` @@ -199,15 +199,15 @@ class Metrics extends Utility implements MetricsInterface { * or when calling {@link Metrics.publishStoredMetrics}. * * You can add a metric by specifying the metric name, unit, and value. For convenience, - * we provide a set of constants for the most common units in {@link MetricUnits}. + * we provide a set of constants for the most common units in {@link MetricUnit}. * * @example * ```typescript - * import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + * import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; * * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); * - * metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + * metrics.addMetric('successfulBooking', MetricUnit.Count, 1); * ``` * * Optionally, you can specify the metric resolution, which can be either `High` or `Standard`. @@ -216,11 +216,11 @@ class Metrics extends Utility implements MetricsInterface { * * @example * ```typescript - * import { Metrics, MetricUnits, MetricResolution } from '@aws-lambda-powertools/metrics'; + * import { Metrics, MetricUnit, MetricResolution } from '@aws-lambda-powertools/metrics'; * * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); * - * metrics.addMetric('successfulBooking', MetricUnits.Count, 1, MetricResolution.High); + * metrics.addMetric('successfulBooking', MetricUnit.Count, 1, MetricResolution.High); * ``` * * @param name - The metric name @@ -232,7 +232,7 @@ class Metrics extends Utility implements MetricsInterface { name: string, unit: MetricUnit, value: number, - resolution: MetricResolution = MetricResolution.Standard + resolution: MetricResolution = MetricResolutions.Standard ): void { this.storeMetric(name, unit, value, resolution); if (this.isSingleMetric) this.publishStoredMetrics(); @@ -309,15 +309,14 @@ class Metrics extends Utility implements MetricsInterface { * * ```typescript * import { Metrics } from '@aws-lambda-powertools/metrics'; - * import { LambdaInterface } from '@aws-lambda-powertools/commons'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; * * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); * * class Lambda implements LambdaInterface { - * * @metrics.logMetrics({ captureColdStartMetric: true }) * public handler(_event: unknown, __context: unknown): Promise { - * // ... + * // ... * } * } * @@ -380,12 +379,12 @@ class Metrics extends Utility implements MetricsInterface { * @example * * ```typescript - * import { Metrics, MetricUnits } from '@aws-lambda-powertools/metrics'; + * import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics'; * * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); // Sets metric namespace, and service as a metric dimension * * export const handler = async (_event: unknown, __context: unknown): Promise => { - * metrics.addMetric('test-metric', MetricUnits.Count, 10); + * metrics.addMetric('test-metric', MetricUnit.Count, 10); * metrics.publishStoredMetrics(); * }; * ``` @@ -424,7 +423,7 @@ class Metrics extends Utility implements MetricsInterface { ).map((metricDefinition) => ({ Name: metricDefinition.name, Unit: metricDefinition.unit, - ...(metricDefinition.resolution === MetricResolution.High + ...(metricDefinition.resolution === MetricResolutions.High ? { StorageResolution: metricDefinition.resolution } : {}), })); @@ -512,7 +511,7 @@ class Metrics extends Utility implements MetricsInterface { * ```typescript * const singleMetric = metrics.singleMetric(); * singleMetric.addDimension('InnerDimension', 'true'); - * singleMetric.addMetric('single-metric', MetricUnits.Percent, 50); + * singleMetric.addMetric('single-metric', MetricUnit.Percent, 50); * ``` * * @returns the Metrics @@ -738,4 +737,4 @@ class Metrics extends Utility implements MetricsInterface { } } -export { Metrics, MetricUnits, MetricResolution }; +export { Metrics }; diff --git a/packages/metrics/src/config/ConfigServiceInterface.ts b/packages/metrics/src/config/ConfigServiceInterface.ts deleted file mode 100644 index 72ddf7c4f4..0000000000 --- a/packages/metrics/src/config/ConfigServiceInterface.ts +++ /dev/null @@ -1,13 +0,0 @@ -interface ConfigServiceInterface { - get?(name: string): string; - getNamespace(): string; - getServiceName(): string; - /** - * It returns the value of the POWERTOOLS_DEV environment variable. - * - * @returns {boolean} - */ - isDevMode(): boolean; -} - -export { ConfigServiceInterface }; diff --git a/packages/metrics/src/config/EnvironmentVariablesService.ts b/packages/metrics/src/config/EnvironmentVariablesService.ts index 907036092d..7ec51b9e7f 100644 --- a/packages/metrics/src/config/EnvironmentVariablesService.ts +++ b/packages/metrics/src/config/EnvironmentVariablesService.ts @@ -1,4 +1,4 @@ -import type { ConfigServiceInterface } from './ConfigServiceInterface'; +import type { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; import { EnvironmentVariablesService as CommonEnvironmentVariablesService } from '@aws-lambda-powertools/commons'; class EnvironmentVariablesService @@ -7,6 +7,11 @@ class EnvironmentVariablesService { private namespaceVariable = 'POWERTOOLS_METRICS_NAMESPACE'; + /** + * It returns the value of the POWERTOOLS_METRICS_NAMESPACE environment variable. + * + * @returns {string} + */ public getNamespace(): string { return this.get(this.namespaceVariable); } diff --git a/packages/metrics/src/config/index.ts b/packages/metrics/src/config/index.ts deleted file mode 100644 index 11fd37677e..0000000000 --- a/packages/metrics/src/config/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ConfigServiceInterface'; -export * from './EnvironmentVariablesService'; diff --git a/packages/metrics/src/constants.ts b/packages/metrics/src/constants.ts index 5cdbf6a955..5dd25e2aee 100644 --- a/packages/metrics/src/constants.ts +++ b/packages/metrics/src/constants.ts @@ -4,10 +4,46 @@ const MAX_METRICS_SIZE = 100; const MAX_METRIC_VALUES_SIZE = 100; const MAX_DIMENSION_COUNT = 29; +const MetricUnit = { + Seconds: 'Seconds', + Microseconds: 'Microseconds', + Milliseconds: 'Milliseconds', + Bytes: 'Bytes', + Kilobytes: 'Kilobytes', + Megabytes: 'Megabytes', + Gigabytes: 'Gigabytes', + Terabytes: 'Terabytes', + Bits: 'Bits', + Kilobits: 'Kilobits', + Megabits: 'Megabits', + Gigabits: 'Gigabits', + Terabits: 'Terabits', + Percent: 'Percent', + Count: 'Count', + BytesPerSecond: 'Bytes/Second', + KilobytesPerSecond: 'Kilobytes/Second', + MegabytesPerSecond: 'Megabytes/Second', + GigabytesPerSecond: 'Gigabytes/Second', + TerabytesPerSecond: 'Terabytes/Second', + BitsPerSecond: 'Bits/Second', + KilobitsPerSecond: 'Kilobits/Second', + MegabitsPerSecond: 'Megabits/Second', + GigabitsPerSecond: 'Gigabits/Second', + TerabitsPerSecond: 'Terabits/Second', + CountPerSecond: 'Count/Second', +} as const; + +const MetricResolution = { + Standard: 60, + High: 1, +} as const; + export { COLD_START_METRIC, DEFAULT_NAMESPACE, MAX_METRICS_SIZE, MAX_METRIC_VALUES_SIZE, MAX_DIMENSION_COUNT, + MetricUnit, + MetricResolution, }; diff --git a/packages/metrics/src/index.ts b/packages/metrics/src/index.ts index 7af054bdae..3db8841fd4 100644 --- a/packages/metrics/src/index.ts +++ b/packages/metrics/src/index.ts @@ -1,3 +1,2 @@ -export * from './Metrics'; -export * from './MetricsInterface'; -export * from './middleware'; +export { Metrics } from './Metrics.js'; +export { MetricUnit, MetricResolution } from './constants.js'; diff --git a/packages/metrics/src/middleware/index.ts b/packages/metrics/src/middleware/index.ts deleted file mode 100644 index cfe9900b37..0000000000 --- a/packages/metrics/src/middleware/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './middy'; diff --git a/packages/metrics/src/middleware/middy.ts b/packages/metrics/src/middleware/middy.ts index 0da5c04828..57744b45df 100644 --- a/packages/metrics/src/middleware/middy.ts +++ b/packages/metrics/src/middleware/middy.ts @@ -1,10 +1,10 @@ -import { METRICS_KEY } from '@aws-lambda-powertools/commons/lib/middleware'; -import type { Metrics } from '../Metrics'; -import type { ExtraOptions } from '../types'; +import { METRICS_KEY } from '@aws-lambda-powertools/commons'; +import type { Metrics } from '../Metrics.js'; +import type { ExtraOptions } from '../types/Metrics.js'; import type { MiddlewareLikeObj, MiddyLikeRequest, -} from '@aws-lambda-powertools/commons'; +} from '@aws-lambda-powertools/commons/types'; /** * A middy middleware automating capture of metadata and annotations on segments or subsegments for a Lambda Handler. @@ -17,7 +17,8 @@ import type { * * @example * ```typescript - * import { Metrics, logMetrics } from '@aws-lambda-powertools/metrics'; + * import { Metrics } from '@aws-lambda-powertools/metrics'; + * import { logMetrics } from '@aws-lambda-powertools/metrics/middleware'; * import middy from '@middy/core'; * * const metrics = new Metrics({ namespace: 'serverlessAirline', serviceName: 'orders' }); diff --git a/packages/metrics/src/types/ConfigServiceInterface.ts b/packages/metrics/src/types/ConfigServiceInterface.ts new file mode 100644 index 0000000000..3f8557f399 --- /dev/null +++ b/packages/metrics/src/types/ConfigServiceInterface.ts @@ -0,0 +1,19 @@ +import type { ConfigServiceInterface as ConfigServiceBaseInterface } from '@aws-lambda-powertools/commons/types'; + +/** + * Interface ConfigServiceInterface + * + * @interface + * @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime + * @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables + */ +interface ConfigServiceInterface extends ConfigServiceBaseInterface { + /** + * It returns the value of the POWERTOOLS_METRICS_NAMESPACE environment variable. + * + * @returns {string} + */ + getNamespace(): string; +} + +export type { ConfigServiceInterface }; diff --git a/packages/metrics/src/types/MetricResolution.ts b/packages/metrics/src/types/MetricResolution.ts deleted file mode 100644 index 297025f3c2..0000000000 --- a/packages/metrics/src/types/MetricResolution.ts +++ /dev/null @@ -1,9 +0,0 @@ -const MetricResolution = { - Standard: 60, - High: 1, -} as const; - -type MetricResolution = - (typeof MetricResolution)[keyof typeof MetricResolution]; - -export { MetricResolution }; diff --git a/packages/metrics/src/types/MetricUnit.ts b/packages/metrics/src/types/MetricUnit.ts deleted file mode 100644 index ccbffac645..0000000000 --- a/packages/metrics/src/types/MetricUnit.ts +++ /dev/null @@ -1,85 +0,0 @@ -enum MetricUnits { - Seconds = 'Seconds', - Microseconds = 'Microseconds', - Milliseconds = 'Milliseconds', - Bytes = 'Bytes', - Kilobytes = 'Kilobytes', - Megabytes = 'Megabytes', - Gigabytes = 'Gigabytes', - Terabytes = 'Terabytes', - Bits = 'Bits', - Kilobits = 'Kilobits', - Megabits = 'Megabits', - Gigabits = 'Gigabits', - Terabits = 'Terabits', - Percent = 'Percent', - Count = 'Count', - BytesPerSecond = 'Bytes/Second', - KilobytesPerSecond = 'Kilobytes/Second', - MegabytesPerSecond = 'Megabytes/Second', - GigabytesPerSecond = 'Gigabytes/Second', - TerabytesPerSecond = 'Terabytes/Second', - BitsPerSecond = 'Bits/Second', - KilobitsPerSecond = 'Kilobits/Second', - MegabitsPerSecond = 'Megabits/Second', - GigabitsPerSecond = 'Gigabits/Second', - TerabitsPerSecond = 'Terabits/Second', - CountPerSecond = 'Count/Second', -} - -type MetricUnitSeconds = MetricUnits.Seconds; -type MetricUnitMicroseconds = MetricUnits.Microseconds; -type MetricUnitMilliseconds = MetricUnits.Milliseconds; -type MetricUnitBytes = MetricUnits.Bytes; -type MetricUnitKilobytes = MetricUnits.Kilobytes; -type MetricUnitMegabytes = MetricUnits.Megabytes; -type MetricUnitGigabytes = MetricUnits.Gigabytes; -type MetricUnitTerabytes = MetricUnits.Terabytes; -type MetricUnitBits = MetricUnits.Bits; -type MetricUnitKilobits = MetricUnits.Kilobits; -type MetricUnitMegabits = MetricUnits.Megabits; -type MetricUnitGigabits = MetricUnits.Gigabits; -type MetricUnitTerabits = MetricUnits.Terabits; -type MetricUnitPercent = MetricUnits.Percent; -type MetricUnitCount = MetricUnits.Count; -type MetricUnitBytesPerSecond = MetricUnits.BytesPerSecond; -type MetricUnitKilobytesPerSecond = MetricUnits.KilobytesPerSecond; -type MetricUnitMegabytesPerSecond = MetricUnits.MegabytesPerSecond; -type MetricUnitGigabytesPerSecond = MetricUnits.GigabytesPerSecond; -type MetricUnitTerabytesPerSecond = MetricUnits.TerabytesPerSecond; -type MetricUnitBitsPerSecond = MetricUnits.BitsPerSecond; -type MetricUnitKilobitsPerSecond = MetricUnits.KilobitsPerSecond; -type MetricUnitMegabitsPerSecond = MetricUnits.MegabitsPerSecond; -type MetricUnitGigabitsPerSecond = MetricUnits.GigabitsPerSecond; -type MetricUnitTerabitsPerSecond = MetricUnits.TerabitsPerSecond; -type MetricUnitCountPerSecond = MetricUnits.CountPerSecond; - -type MetricUnit = - | MetricUnitSeconds - | MetricUnitMicroseconds - | MetricUnitMilliseconds - | MetricUnitBytes - | MetricUnitKilobytes - | MetricUnitMegabytes - | MetricUnitGigabytes - | MetricUnitTerabytes - | MetricUnitBits - | MetricUnitKilobits - | MetricUnitMegabits - | MetricUnitGigabits - | MetricUnitTerabits - | MetricUnitPercent - | MetricUnitCount - | MetricUnitBitsPerSecond - | MetricUnitBytesPerSecond - | MetricUnitKilobytesPerSecond - | MetricUnitMegabytesPerSecond - | MetricUnitGigabytesPerSecond - | MetricUnitTerabytesPerSecond - | MetricUnitKilobitsPerSecond - | MetricUnitMegabitsPerSecond - | MetricUnitGigabitsPerSecond - | MetricUnitTerabitsPerSecond - | MetricUnitCountPerSecond; - -export { MetricUnit, MetricUnits }; diff --git a/packages/metrics/src/types/Metrics.ts b/packages/metrics/src/types/Metrics.ts index ef5e7d5454..a8780bca10 100644 --- a/packages/metrics/src/types/Metrics.ts +++ b/packages/metrics/src/types/Metrics.ts @@ -1,12 +1,5 @@ -import type { Handler } from 'aws-lambda'; -import type { - LambdaInterface, - AsyncHandler, - SyncHandler, -} from '@aws-lambda-powertools/commons'; -import { ConfigServiceInterface } from '../config'; -import { MetricUnit } from './MetricUnit'; -import { MetricResolution } from './MetricResolution'; +import type { ConfigServiceInterface } from './ConfigServiceInterface.js'; +import { MetricResolution, MetricUnit } from '../constants.js'; type Dimensions = Record; @@ -30,14 +23,6 @@ type EmfOutput = Readonly<{ }; }>; -type HandlerMethodDecorator = ( - target: LambdaInterface, - propertyKey: string | symbol, - descriptor: - | TypedPropertyDescriptor> - | TypedPropertyDescriptor> -) => void; - /** * Options for the metrics decorator * @@ -63,6 +48,11 @@ type ExtraOptions = { captureColdStartMetric?: boolean; }; +type MetricResolution = + (typeof MetricResolution)[keyof typeof MetricResolution]; + +type MetricUnit = (typeof MetricUnit)[keyof typeof MetricUnit]; + type StoredMetric = { name: string; unit: MetricUnit; @@ -78,13 +68,14 @@ type MetricDefinition = { StorageResolution?: MetricResolution; }; -export { +export type { MetricsOptions, Dimensions, EmfOutput, - HandlerMethodDecorator, ExtraOptions, StoredMetrics, StoredMetric, MetricDefinition, + MetricResolution, + MetricUnit, }; diff --git a/packages/metrics/src/MetricsInterface.ts b/packages/metrics/src/types/MetricsInterface.ts similarity index 78% rename from packages/metrics/src/MetricsInterface.ts rename to packages/metrics/src/types/MetricsInterface.ts index ce11fd7aa1..c4e796cf98 100644 --- a/packages/metrics/src/MetricsInterface.ts +++ b/packages/metrics/src/types/MetricsInterface.ts @@ -1,12 +1,12 @@ -import { Metrics } from './Metrics'; -import { - MetricUnit, - MetricResolution, +import type { Metrics } from '../Metrics.js'; +import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types'; +import type { EmfOutput, - HandlerMethodDecorator, Dimensions, MetricsOptions, -} from './types'; + MetricResolution, + MetricUnit, +} from './Metrics.js'; interface MetricsInterface { addDimension(name: string, value: string): void; @@ -29,4 +29,4 @@ interface MetricsInterface { singleMetric(): Metrics; } -export { MetricsInterface }; +export type { MetricsInterface }; diff --git a/packages/metrics/src/types/index.ts b/packages/metrics/src/types/index.ts index 344dac9821..7d30ec8888 100644 --- a/packages/metrics/src/types/index.ts +++ b/packages/metrics/src/types/index.ts @@ -1,3 +1,13 @@ -export * from './Metrics'; -export * from './MetricUnit'; -export * from './MetricResolution'; +export type { + MetricsOptions, + Dimensions, + EmfOutput, + ExtraOptions, + StoredMetrics, + StoredMetric, + MetricDefinition, + MetricResolution, + MetricUnit, +} from './Metrics.js'; +export type { ConfigServiceInterface } from './ConfigServiceInterface.js'; +export type { MetricsInterface } from './MetricsInterface.js'; diff --git a/packages/metrics/tests/e2e/basicFeatures.decorator.test.functionCode.ts b/packages/metrics/tests/e2e/basicFeatures.decorator.test.functionCode.ts index 9016a01cbe..15e8da2a3d 100644 --- a/packages/metrics/tests/e2e/basicFeatures.decorator.test.functionCode.ts +++ b/packages/metrics/tests/e2e/basicFeatures.decorator.test.functionCode.ts @@ -1,13 +1,14 @@ -import { Metrics, MetricUnits } from '../../src'; +import { Metrics, MetricUnit } from '../../src/index.js'; +import type { MetricUnit as MetricUnitType } from '../../src/types/index.js'; import type { Context } from 'aws-lambda'; -import type { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const namespace = process.env.EXPECTED_NAMESPACE ?? 'CdkExample'; const serviceName = process.env.EXPECTED_SERVICE_NAME ?? 'MyFunctionWithStandardHandler'; const metricName = process.env.EXPECTED_METRIC_NAME ?? 'MyMetric'; const metricUnit = - (process.env.EXPECTED_METRIC_UNIT as MetricUnits) ?? MetricUnits.Count; + (process.env.EXPECTED_METRIC_UNIT as MetricUnitType) ?? MetricUnit.Count; const metricValue = process.env.EXPECTED_METRIC_VALUE ?? '1'; const defaultDimensions = process.env.EXPECTED_DEFAULT_DIMENSIONS ?? '{"MyDimension":"MyValue"}'; @@ -19,8 +20,8 @@ const singleMetricDimension = const singleMetricName = process.env.EXPECTED_SINGLE_METRIC_NAME ?? 'MySingleMetric'; const singleMetricUnit = - (process.env.EXPECTED_SINGLE_METRIC_UNIT as MetricUnits) ?? - MetricUnits.Percent; + (process.env.EXPECTED_SINGLE_METRIC_UNIT as MetricUnitType) ?? + MetricUnit.Percent; const singleMetricValue = process.env.EXPECTED_SINGLE_METRIC_VALUE ?? '2'; const metrics = new Metrics({ namespace: namespace, serviceName: serviceName }); diff --git a/packages/metrics/tests/e2e/basicFeatures.decorators.test.ts b/packages/metrics/tests/e2e/basicFeatures.decorators.test.ts index a0ab289392..5bf1a38809 100644 --- a/packages/metrics/tests/e2e/basicFeatures.decorators.test.ts +++ b/packages/metrics/tests/e2e/basicFeatures.decorators.test.ts @@ -12,8 +12,8 @@ import { GetMetricStatisticsCommand, } from '@aws-sdk/client-cloudwatch'; import { join } from 'node:path'; -import { getMetrics, sortDimensions } from '../helpers/metricsUtils'; -import { MetricsTestNodejsFunction } from '../helpers/resources'; +import { getMetrics, sortDimensions } from '../helpers/metricsUtils.js'; +import { MetricsTestNodejsFunction } from '../helpers/resources.js'; import { commonEnvironmentVars, ONE_MINUTE, @@ -21,7 +21,7 @@ import { SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; describe(`Metrics E2E tests, basic features decorator usage`, () => { const testStack = new TestStack({ diff --git a/packages/metrics/tests/e2e/basicFeatures.manual.test.functionCode.ts b/packages/metrics/tests/e2e/basicFeatures.manual.test.functionCode.ts index 67ea224083..8c06579e34 100644 --- a/packages/metrics/tests/e2e/basicFeatures.manual.test.functionCode.ts +++ b/packages/metrics/tests/e2e/basicFeatures.manual.test.functionCode.ts @@ -1,4 +1,5 @@ -import { Metrics, MetricUnits } from '../../src'; +import { Metrics, MetricUnit } from '../../src/index.js'; +import type { MetricUnit as MetricUnitType } from '../../src/types/index.js'; import type { Context } from 'aws-lambda'; const namespace = process.env.EXPECTED_NAMESPACE ?? 'CdkExample'; @@ -6,7 +7,7 @@ const serviceName = process.env.EXPECTED_SERVICE_NAME ?? 'MyFunctionWithStandardHandler'; const metricName = process.env.EXPECTED_METRIC_NAME ?? 'MyMetric'; const metricUnit = - (process.env.EXPECTED_METRIC_UNIT as MetricUnits) ?? MetricUnits.Count; + (process.env.EXPECTED_METRIC_UNIT as MetricUnitType) ?? MetricUnit.Count; const metricValue = process.env.EXPECTED_METRIC_VALUE ?? '1'; const defaultDimensions = process.env.EXPECTED_DEFAULT_DIMENSIONS ?? '{"MyDimension":"MyValue"}'; @@ -18,8 +19,8 @@ const singleMetricDimension = const singleMetricName = process.env.EXPECTED_SINGLE_METRIC_NAME ?? 'MySingleMetric'; const singleMetricUnit = - (process.env.EXPECTED_SINGLE_METRIC_UNIT as MetricUnits) ?? - MetricUnits.Percent; + (process.env.EXPECTED_SINGLE_METRIC_UNIT as MetricUnitType) ?? + MetricUnit.Percent; const singleMetricValue = process.env.EXPECTED_SINGLE_METRIC_VALUE ?? '2'; const metrics = new Metrics({ namespace: namespace, serviceName: serviceName }); diff --git a/packages/metrics/tests/e2e/basicFeatures.manual.test.ts b/packages/metrics/tests/e2e/basicFeatures.manual.test.ts index b984cffd54..8211d6c882 100644 --- a/packages/metrics/tests/e2e/basicFeatures.manual.test.ts +++ b/packages/metrics/tests/e2e/basicFeatures.manual.test.ts @@ -12,8 +12,8 @@ import { GetMetricStatisticsCommand, } from '@aws-sdk/client-cloudwatch'; import { join } from 'node:path'; -import { getMetrics, sortDimensions } from '../helpers/metricsUtils'; -import { MetricsTestNodejsFunction } from '../helpers/resources'; +import { getMetrics, sortDimensions } from '../helpers/metricsUtils.js'; +import { MetricsTestNodejsFunction } from '../helpers/resources.js'; import { commonEnvironmentVars, ONE_MINUTE, @@ -21,7 +21,7 @@ import { SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; describe(`Metrics E2E tests, manual usage`, () => { const testStack = new TestStack({ diff --git a/packages/metrics/tests/e2e/constants.ts b/packages/metrics/tests/e2e/constants.ts index b61307fe76..dbffabf33a 100644 --- a/packages/metrics/tests/e2e/constants.ts +++ b/packages/metrics/tests/e2e/constants.ts @@ -1,5 +1,5 @@ import { randomUUID } from 'node:crypto'; -import { MetricUnits } from '../../src'; +import { MetricUnit } from '../../src/index.js'; const RESOURCE_NAME_PREFIX = 'Metrics'; const ONE_MINUTE = 60 * 1000; @@ -9,14 +9,14 @@ const TEARDOWN_TIMEOUT = 5 * ONE_MINUTE; const commonEnvironmentVars = { EXPECTED_METRIC_NAME: 'MyMetric', - EXPECTED_METRIC_UNIT: MetricUnits.Count, + EXPECTED_METRIC_UNIT: MetricUnit.Count, EXPECTED_METRIC_VALUE: '1', EXPECTED_NAMESPACE: randomUUID(), EXPECTED_DEFAULT_DIMENSIONS: { MyDimension: 'MyValue' }, EXPECTED_EXTRA_DIMENSION: { MyExtraDimension: 'MyExtraValue' }, EXPECTED_SINGLE_METRIC_DIMENSION: { MySingleMetricDim: 'MySingleValue' }, EXPECTED_SINGLE_METRIC_NAME: 'MySingleMetric', - EXPECTED_SINGLE_METRIC_UNIT: MetricUnits.Percent, + EXPECTED_SINGLE_METRIC_UNIT: MetricUnit.Percent, EXPECTED_SINGLE_METRIC_VALUE: '2', POWERTOOLS_SERVICE_NAME: 'metrics-e2e-testing', }; diff --git a/packages/metrics/tests/helpers/metricsUtils.ts b/packages/metrics/tests/helpers/metricsUtils.ts index 674efa370f..859c551266 100644 --- a/packages/metrics/tests/helpers/metricsUtils.ts +++ b/packages/metrics/tests/helpers/metricsUtils.ts @@ -1,6 +1,6 @@ import promiseRetry from 'promise-retry'; -import { Metrics } from '../../src'; -import { ExtraOptions, MetricUnits } from '../../src/types'; +import { Metrics, MetricUnit } from '../../src/index.js'; +import { ExtraOptions } from '../../src/types/index.js'; import { CloudWatchClient, ListMetricsCommand, @@ -10,7 +10,7 @@ import type { ListMetricsCommandOutput, } from '@aws-sdk/client-cloudwatch'; import type { Context, Handler } from 'aws-lambda'; -import type { LambdaInterface } from '@aws-lambda-powertools/commons'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; const getMetrics = async ( cloudWatchClient: CloudWatchClient, @@ -51,13 +51,11 @@ const setupDecoratorLambdaHandler = ( ): Handler => { class LambdaFunction implements LambdaInterface { @metrics.logMetrics(options) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore public async handler( _event: TEvent, _context: Context ): Promise { - metrics.addMetric('decorator-lambda-test-metric', MetricUnits.Count, 1); + metrics.addMetric('decorator-lambda-test-metric', MetricUnit.Count, 1); return 'Lambda invoked!'; } diff --git a/packages/metrics/tests/helpers/resources.ts b/packages/metrics/tests/helpers/resources.ts index ebb111984b..128332eb88 100644 --- a/packages/metrics/tests/helpers/resources.ts +++ b/packages/metrics/tests/helpers/resources.ts @@ -1,10 +1,10 @@ +import type { TestStack } from '@aws-lambda-powertools/testing-utils'; import type { ExtraTestProps, TestNodejsFunctionProps, - TestStack, -} from '@aws-lambda-powertools/testing-utils'; -import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils'; -import { commonEnvironmentVars } from '../e2e/constants'; +} from '@aws-lambda-powertools/testing-utils/types'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; +import { commonEnvironmentVars } from '../e2e/constants.js'; class MetricsTestNodejsFunction extends TestNodejsFunction { public constructor( diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index c613b0535d..a1ee97ac4a 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -3,26 +3,25 @@ * * @group unit/metrics/class */ -import { - LambdaInterface, - ContextExamples as dummyContext, - Events as dummyEvent, -} from '@aws-lambda-powertools/commons'; -import { MetricResolution, MetricUnits, Metrics } from '../../src/'; -import { Context, Handler } from 'aws-lambda'; -import { Dimensions, EmfOutput, MetricsOptions } from '../../src/types'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { MetricResolution, MetricUnit, Metrics } from '../../src/index.js'; +import type { Context, Handler } from 'aws-lambda'; +import type { + Dimensions, + EmfOutput, + MetricsOptions, + ConfigServiceInterface, +} from '../../src/types/index.js'; import { COLD_START_METRIC, DEFAULT_NAMESPACE, MAX_DIMENSION_COUNT, MAX_METRICS_SIZE, MAX_METRIC_VALUES_SIZE, -} from '../../src/constants'; -import { setupDecoratorLambdaHandler } from '../helpers/metricsUtils'; -import { - ConfigServiceInterface, - EnvironmentVariablesService, -} from '../../src/config'; +} from '../../src/constants.js'; +import { setupDecoratorLambdaHandler } from '../helpers/metricsUtils.js'; +import { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; jest.mock('node:console', () => ({ ...jest.requireActual('node:console'), @@ -43,8 +42,10 @@ interface LooseObject { describe('Class: Metrics', () => { const ENVIRONMENT_VARIABLES = process.env; const TEST_NAMESPACE = 'test'; - const context = dummyContext.helloworldContext; - const event = dummyEvent.Custom.CustomEvent; + const event = { + foo: 'bar', + bar: 'baz', + }; beforeEach(() => { jest.clearAllMocks(); @@ -241,9 +242,15 @@ describe('Class: Metrics', () => { getServiceName(): string { return 'test-service'; }, + getXrayTraceId(): string | undefined { + return 'test-trace-id'; + }, isDevMode(): boolean { return false; }, + isValueTrue(value: string): boolean { + return value === 'true'; + }, }; const metricsOptions: MetricsOptions = { customConfigService: configService, @@ -530,12 +537,7 @@ describe('Class: Metrics', () => { const metricName = 'test-metric'; // Act - metrics.addMetric( - metricName, - MetricUnits.Count, - 1, - MetricResolution.High - ); + metrics.addMetric(metricName, MetricUnit.Count, 1, MetricResolution.High); // Assess expect(metrics).toEqual( @@ -544,7 +546,7 @@ describe('Class: Metrics', () => { [metricName]: { name: metricName, resolution: MetricResolution.High, - unit: MetricUnits.Count, + unit: MetricUnit.Count, value: 1, }, }, @@ -559,19 +561,19 @@ describe('Class: Metrics', () => { // Act metrics.addMetric( 'test-metric-1', - MetricUnits.Count, + MetricUnit.Count, 1, MetricResolution.High ); metrics.addMetric( 'test-metric-2', - MetricUnits.Count, + MetricUnit.Count, 3, MetricResolution.High ); metrics.addMetric( 'test-metric-3', - MetricUnits.Count, + MetricUnit.Count, 6, MetricResolution.High ); @@ -583,19 +585,19 @@ describe('Class: Metrics', () => { 'test-metric-1': { name: 'test-metric-1', resolution: MetricResolution.High, - unit: MetricUnits.Count, + unit: MetricUnit.Count, value: 1, }, 'test-metric-2': { name: 'test-metric-2', resolution: MetricResolution.High, - unit: MetricUnits.Count, + unit: MetricUnit.Count, value: 3, }, 'test-metric-3': { name: 'test-metric-3', resolution: MetricResolution.High, - unit: MetricUnits.Count, + unit: MetricUnit.Count, value: 6, }, }, @@ -608,8 +610,8 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric('test-metric-1', MetricUnits.Count, 1); - metrics.addMetric('test-metric-2', MetricUnits.Seconds, 3); + metrics.addMetric('test-metric-1', MetricUnit.Count, 1); + metrics.addMetric('test-metric-2', MetricUnit.Seconds, 3); // Assess expect(metrics).toEqual( @@ -618,13 +620,13 @@ describe('Class: Metrics', () => { 'test-metric-1': { name: 'test-metric-1', resolution: MetricResolution.Standard, - unit: MetricUnits.Count, + unit: MetricUnit.Count, value: 1, }, 'test-metric-2': { name: 'test-metric-2', resolution: MetricResolution.Standard, - unit: MetricUnits.Seconds, + unit: MetricUnit.Seconds, value: 3, }, }, @@ -638,10 +640,10 @@ describe('Class: Metrics', () => { const metricName = 'test-metric'; // Act - metrics.addMetric(metricName, MetricUnits.Count, 1); - metrics.addMetric(metricName, MetricUnits.Count, 5); - metrics.addMetric(metricName, MetricUnits.Count, 1); - metrics.addMetric(metricName, MetricUnits.Count, 4); + metrics.addMetric(metricName, MetricUnit.Count, 1); + metrics.addMetric(metricName, MetricUnit.Count, 5); + metrics.addMetric(metricName, MetricUnit.Count, 1); + metrics.addMetric(metricName, MetricUnit.Count, 4); // Assess expect(metrics).toEqual( @@ -650,7 +652,7 @@ describe('Class: Metrics', () => { [metricName]: { name: metricName, resolution: MetricResolution.Standard, - unit: MetricUnits.Count, + unit: MetricUnit.Count, value: [1, 5, 1, 4], }, }, @@ -665,10 +667,10 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => { - metrics.addMetric(metricName, MetricUnits.Count, 1); - metrics.addMetric(metricName, MetricUnits.Kilobits, 5); + metrics.addMetric(metricName, MetricUnit.Count, 1); + metrics.addMetric(metricName, MetricUnit.Kilobits, 5); }).toThrowError( - `Metric "${metricName}" has already been added with unit "${MetricUnits.Count}", but we received unit "${MetricUnits.Kilobits}". Did you mean to use metric unit "${MetricUnits.Count}"?` + `Metric "${metricName}" has already been added with unit "${MetricUnit.Count}", but we received unit "${MetricUnit.Kilobits}". Did you mean to use metric unit "${MetricUnit.Count}"?` ); }); @@ -684,7 +686,7 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => { for (let i = 0; i < MAX_METRICS_SIZE; i++) { - metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); + metrics.addMetric(`${metricName}-${i}`, MetricUnit.Count, i); } }).not.toThrowError(); expect(Object.keys(metrics['storedMetrics']).length).toEqual( @@ -692,7 +694,7 @@ describe('Class: Metrics', () => { ); metrics.addMetric( 'another-metric', - MetricUnits.Count, + MetricUnit.Count, MAX_METRICS_SIZE + 1 ); expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(1); @@ -702,7 +704,7 @@ describe('Class: Metrics', () => { 'another-metric': { name: 'another-metric', resolution: MetricResolution.Standard, - unit: MetricUnits.Count, + unit: MetricUnit.Count, value: MAX_METRICS_SIZE + 1, }, }, @@ -718,7 +720,7 @@ describe('Class: Metrics', () => { // Act for (let i = 0; i <= MAX_METRIC_VALUES_SIZE; i++) { - metrics.addMetric(`${metricName}`, MetricUnits.Count, i); + metrics.addMetric(`${metricName}`, MetricUnit.Count, i); } metrics.publishStoredMetrics(); @@ -750,13 +752,13 @@ describe('Class: Metrics', () => { // Act & Assess expect(() => { for (let i = 0; i < MAX_METRICS_SIZE - 1; i++) { - metrics.addMetric(`${metricName}-${i}`, MetricUnits.Count, i); + metrics.addMetric(`${metricName}-${i}`, MetricUnit.Count, i); } }).not.toThrowError(); expect(Object.keys(metrics['storedMetrics']).length).toEqual( MAX_METRICS_SIZE - 1 ); - metrics.addMetric('another-metric', MetricUnits.Count, MAX_METRICS_SIZE); + metrics.addMetric('another-metric', MetricUnit.Count, MAX_METRICS_SIZE); expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(0); expect(Object.keys(metrics['storedMetrics']).length).toEqual( MAX_METRICS_SIZE @@ -775,8 +777,8 @@ describe('Class: Metrics', () => { ); // Act - metrics.addMetric('test-metric-1', MetricUnits.Count, 1); - metrics.addMetric('test-metric-2', MetricUnits.Bits, 100); + metrics.addMetric('test-metric-1', MetricUnit.Count, 1); + metrics.addMetric('test-metric-2', MetricUnit.Bits, 100); // Assess expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(2); @@ -794,8 +796,8 @@ describe('Class: Metrics', () => { ); // Act - metrics.addMetric('test-metric-1', MetricUnits.Count, 1); - metrics.addMetric('test-metric-2', MetricUnits.Bits, 100); + metrics.addMetric('test-metric-1', MetricUnit.Count, 1); + metrics.addMetric('test-metric-2', MetricUnit.Bits, 100); // Assess expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(0); @@ -810,8 +812,8 @@ describe('Class: Metrics', () => { ); // Act - metrics.addMetric('test-metric-1', MetricUnits.Count, 1); - metrics.addMetric('test-metric-2', MetricUnits.Bits, 100); + metrics.addMetric('test-metric-1', MetricUnit.Count, 1); + metrics.addMetric('test-metric-2', MetricUnit.Bits, 100); // Assess expect(publishStoredMetricsSpy).toHaveBeenCalledTimes(0); @@ -839,7 +841,7 @@ describe('Class: Metrics', () => { expect(addMetricSpy).toBeCalledTimes(1); expect(addMetricSpy).toBeCalledWith( COLD_START_METRIC, - MetricUnits.Count, + MetricUnit.Count, 1 ); }); @@ -1080,7 +1082,7 @@ describe('Class: Metrics', () => { const metricName = 'test-metric'; // Act - metrics.addMetric(metricName, MetricUnits.Count, 1); + metrics.addMetric(metricName, MetricUnit.Count, 1); metrics.clearMetrics(); // Assess @@ -1125,7 +1127,7 @@ describe('Class: Metrics', () => { expect(addMetricSpy).toHaveBeenNthCalledWith( 1, decoratorLambdaMetric, - MetricUnits.Count, + MetricUnit.Count, 1 ); expect(publishStoredMetricsSpy).toBeCalledTimes(1); @@ -1150,7 +1152,7 @@ describe('Class: Metrics', () => { expect(addMetricSpy).toHaveBeenNthCalledWith( 1, decoratorLambdaMetric, - MetricUnits.Count, + MetricUnit.Count, 1 ); expect(captureColdStartMetricSpy).toBeCalledTimes(1); @@ -1175,7 +1177,7 @@ describe('Class: Metrics', () => { expect(addMetricSpy).toHaveBeenNthCalledWith( 1, decoratorLambdaMetric, - MetricUnits.Count, + MetricUnit.Count, 1 ); expect(throwOnEmptyMetricsSpy).toBeCalledTimes(1); @@ -1204,7 +1206,7 @@ describe('Class: Metrics', () => { expect(addMetricSpy).toHaveBeenNthCalledWith( 1, decoratorLambdaMetric, - MetricUnits.Count, + MetricUnit.Count, 1 ); expect(setDefaultDimensionsSpy).toHaveBeenNthCalledWith( @@ -1255,7 +1257,7 @@ describe('Class: Metrics', () => { test('it should call serializeMetrics && log the stringified return value of serializeMetrics', () => { // Prepare const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metric', MetricUnits.Count, 10); + metrics.addMetric('test-metric', MetricUnit.Count, 10); const consoleLogSpy = jest .spyOn(metrics['console'], 'log') .mockImplementation(); @@ -1269,7 +1271,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: 'test-metric', - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], }, @@ -1294,7 +1296,7 @@ describe('Class: Metrics', () => { test('it should call clearMetrics function', () => { // Prepare const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metric', MetricUnits.Count, 10); + metrics.addMetric('test-metric', MetricUnit.Count, 10); const clearMetricsSpy = jest.spyOn(metrics, 'clearMetrics'); // Act @@ -1307,7 +1309,7 @@ describe('Class: Metrics', () => { test('it should call clearDimensions function', () => { // Prepare const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metric', MetricUnits.Count, 10); + metrics.addMetric('test-metric', MetricUnit.Count, 10); const clearDimensionsSpy = jest.spyOn(metrics, 'clearDimensions'); // Act @@ -1320,7 +1322,7 @@ describe('Class: Metrics', () => { test('it should call clearMetadata function', () => { // Prepare const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); - metrics.addMetric('test-metric', MetricUnits.Count, 10); + metrics.addMetric('test-metric', MetricUnit.Count, 10); const clearMetadataSpy = jest.spyOn(metrics, 'clearMetadata'); // Act @@ -1360,11 +1362,11 @@ describe('Class: Metrics', () => { }); // Act - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); - metrics.addMetric('successfulBooking', MetricUnits.Count, 3); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 3); metrics.addMetric( 'failedBooking', - MetricUnits.Count, + MetricUnit.Count, 1, MetricResolution.High ); @@ -1381,11 +1383,11 @@ describe('Class: Metrics', () => { Metrics: [ { Name: 'successfulBooking', - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, { Name: 'failedBooking', - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, StorageResolution: 1, }, ], @@ -1409,7 +1411,7 @@ describe('Class: Metrics', () => { }); // Act - metrics.addMetric(testMetric, MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnit.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess @@ -1422,7 +1424,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: testMetric, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1443,7 +1445,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(testMetric, MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnit.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess @@ -1456,7 +1458,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: testMetric, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1482,7 +1484,7 @@ describe('Class: Metrics', () => { }); // Act - metrics.addMetric(testMetric, MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnit.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess @@ -1500,7 +1502,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: testMetric, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1528,7 +1530,7 @@ describe('Class: Metrics', () => { }); // Act - metrics.addMetric(testMetric, MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnit.Count, 10); metrics.addDimension('foo', 'baz'); const loggedData = metrics.serializeMetrics(); @@ -1547,7 +1549,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: testMetric, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1571,7 +1573,7 @@ describe('Class: Metrics', () => { // Act metrics.addMetric( 'test-metric', - MetricUnits.Count, + MetricUnit.Count, 10, MetricResolution.High ); @@ -1595,7 +1597,7 @@ describe('Class: Metrics', () => { { Name: testMetric, StorageResolution: 1, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1621,7 +1623,7 @@ describe('Class: Metrics', () => { // Act metrics.addMetric( testMetric, - MetricUnits.Count, + MetricUnit.Count, 10, MetricResolution.High ); @@ -1645,7 +1647,7 @@ describe('Class: Metrics', () => { { Name: testMetric, StorageResolution: 1, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1666,7 +1668,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(testMetric, MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnit.Count, 10); metrics.addMetadata('foo', 'bar'); const loggedData = metrics.serializeMetrics(); @@ -1680,7 +1682,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: testMetric, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1714,7 +1716,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics(); // Act - metrics.addMetric(testMetric, MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnit.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess @@ -1729,7 +1731,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: testMetric, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: DEFAULT_NAMESPACE, @@ -1748,7 +1750,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(testMetric, MetricUnits.Count, 10); + metrics.addMetric(testMetric, MetricUnit.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess @@ -1763,7 +1765,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: testMetric, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1782,7 +1784,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(metricName, MetricUnits.Count, 10); + metrics.addMetric(metricName, MetricUnit.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess @@ -1796,7 +1798,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: metricName, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1815,8 +1817,8 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(metricName, MetricUnits.Count, 10); - metrics.addMetric(metricName, MetricUnits.Count, 20); + metrics.addMetric(metricName, MetricUnit.Count, 10); + metrics.addMetric(metricName, MetricUnit.Count, 20); const loggedData = metrics.serializeMetrics(); // Assess @@ -1830,7 +1832,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: metricName, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1850,8 +1852,8 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(metricName1, MetricUnits.Count, 10); - metrics.addMetric(metricName2, MetricUnits.Seconds, 20); + metrics.addMetric(metricName1, MetricUnit.Count, 10); + metrics.addMetric(metricName2, MetricUnit.Seconds, 20); const loggedData = metrics.serializeMetrics(); // Assess @@ -1866,11 +1868,11 @@ describe('Class: Metrics', () => { Metrics: [ { Name: metricName1, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, { Name: metricName2, - Unit: MetricUnits.Seconds, + Unit: MetricUnit.Seconds, }, ], Namespace: TEST_NAMESPACE, @@ -1890,7 +1892,7 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(metricName, MetricUnits.Count, 10); + metrics.addMetric(metricName, MetricUnit.Count, 10); const loggedData = metrics.serializeMetrics(); // Assess @@ -1906,7 +1908,7 @@ describe('Class: Metrics', () => { Metrics: [ { Name: metricName, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, ], Namespace: TEST_NAMESPACE, @@ -1926,10 +1928,10 @@ describe('Class: Metrics', () => { const metrics: Metrics = new Metrics({ namespace: TEST_NAMESPACE }); // Act - metrics.addMetric(metricName1, MetricUnits.Count, 10); + metrics.addMetric(metricName1, MetricUnit.Count, 10); metrics.addMetric( metricName2, - MetricUnits.Seconds, + MetricUnit.Seconds, 10, MetricResolution.High ); @@ -1951,12 +1953,12 @@ describe('Class: Metrics', () => { Metrics: [ { Name: metricName1, - Unit: MetricUnits.Count, + Unit: MetricUnit.Count, }, { Name: metricName2, StorageResolution: 1, - Unit: MetricUnits.Seconds, + Unit: MetricUnit.Seconds, }, ], Namespace: TEST_NAMESPACE, diff --git a/packages/metrics/tests/unit/middleware/middy.test.ts b/packages/metrics/tests/unit/middleware/middy.test.ts index 5c1e22af48..f39d9f8db8 100644 --- a/packages/metrics/tests/unit/middleware/middy.test.ts +++ b/packages/metrics/tests/unit/middleware/middy.test.ts @@ -3,12 +3,12 @@ * * @group unit/metrics/middleware */ -import { Metrics, MetricUnits, logMetrics } from '../../../../metrics/src'; +import { Metrics, MetricUnit, MetricResolution } from '../../../src/index.js'; +import { logMetrics } from '../../../src/middleware/middy.js'; import middy from '@middy/core'; -import { ExtraOptions } from '../../../src/types'; -import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware'; -import { helloworldContext as dummyContext } from '@aws-lambda-powertools/commons/lib/samples/resources/contexts/hello-world'; -import { CustomEvent as dummyEvent } from '@aws-lambda-powertools/commons/lib/samples/resources/events/custom/index'; +import { ExtraOptions } from '../../../src/types/index.js'; +import { cleanupMiddlewares } from '@aws-lambda-powertools/commons'; +import context from '@aws-lambda-powertools/testing-utils/context'; jest.mock('node:console', () => ({ ...jest.requireActual('node:console'), @@ -28,6 +28,11 @@ describe('Middy middleware', () => { process.env = { ...ENVIRONMENT_VARIABLES }; }); + const event = { + foo: 'bar', + bar: 'baz', + }; + describe('throwOnEmptyMetrics', () => { test('should throw on empty metrics if set to true', async () => { // Prepare @@ -39,7 +44,7 @@ describe('Middy middleware', () => { logMetrics(metrics, { throwOnEmptyMetrics: true }) ); - await expect(handler(dummyEvent, dummyContext)).rejects.toThrowError( + await expect(handler(event, context)).rejects.toThrowError( 'The number of metrics recorded must be higher than zero' ); }); @@ -55,9 +60,7 @@ describe('Middy middleware', () => { ); // Act & Assess - await expect( - handler(dummyEvent, dummyContext) - ).resolves.not.toThrowError(); + await expect(handler(event, context)).resolves.not.toThrowError(); }); test('should not throw on empty metrics if not set, but should log a warning', async () => { @@ -72,9 +75,7 @@ describe('Middy middleware', () => { ); // Act & Assess - await expect( - handler(dummyEvent, dummyContext) - ).resolves.not.toThrowError(); + await expect(handler(event, context)).resolves.not.toThrowError(); expect(consoleWarnSpy).toBeCalledTimes(1); expect(consoleWarnSpy).toBeCalledWith( 'No application metrics to publish. The cold-start metric may be published if enabled. If application metrics should never be empty, consider using `throwOnEmptyMetrics`' @@ -101,8 +102,8 @@ describe('Middy middleware', () => { ); // Act - await handler(dummyEvent, dummyContext); - await handler(dummyEvent, dummyContext); + await handler(event, context); + await handler(event, context); // Assess const loggedData = [ @@ -137,7 +138,7 @@ describe('Middy middleware', () => { ); // Act - await handler(dummyEvent, dummyContext); + await handler(event, context); // Assess const loggedData = JSON.parse(consoleSpy.mock.calls[0][0]); @@ -156,7 +157,7 @@ describe('Middy middleware', () => { ); // Act & Assess - await expect(handler(dummyEvent, dummyContext)).resolves.not.toThrow(); + await expect(handler(event, context)).resolves.not.toThrow(); }); }); @@ -167,17 +168,17 @@ describe('Middy middleware', () => { namespace: 'serverlessAirline', serviceName: 'orders', }); - const cosoleSpy = jest.spyOn(metrics['console'], 'log'); + const consoleSpy = jest.spyOn(metrics['console'], 'log'); const handler = middy(async (): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 2); - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 2); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }).use(logMetrics(metrics)); // Act - await handler(dummyEvent, dummyContext); + await handler(event, context); // Assess - expect(cosoleSpy).toHaveBeenNthCalledWith( + expect(consoleSpy).toHaveBeenNthCalledWith( 1, JSON.stringify({ _aws: { @@ -209,11 +210,11 @@ describe('Middy middleware', () => { captureColdStartMetric: true, }; const handler = middy(async (): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }).use(logMetrics(metrics, metricsOptions)); // Act - await handler(dummyEvent, dummyContext); + await handler(event, context); // Assess expect(consoleSpy).toHaveBeenNthCalledWith( @@ -245,11 +246,11 @@ describe('Middy middleware', () => { }); const consoleSpy = jest.spyOn(metrics['console'], 'log'); const handler = middy(async (): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }).use(logMetrics(metrics)); // Act - await handler(dummyEvent, dummyContext); + await handler(event, context); // Assess expect(consoleSpy).toHaveBeenNthCalledWith( @@ -279,7 +280,7 @@ describe('Middy middleware', () => { }); const consoleSpy = jest.spyOn(metrics['console'], 'log'); const handler = middy(async (): Promise => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); }).use( logMetrics(metrics, { throwOnEmptyMetrics: true, @@ -287,7 +288,7 @@ describe('Middy middleware', () => { ); // Act - await handler(dummyEvent, dummyContext); + await handler(event, context); // Assess expect(consoleSpy).toHaveBeenNthCalledWith( @@ -338,19 +339,108 @@ describe('Middy middleware', () => { }; }; const handler = middy( - (_event: typeof dummyEvent & { idx: number }): void => { - metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + (_event: { foo: string; bar: string } & { idx: number }): void => { + metrics.addMetric('successfulBooking', MetricUnit.Count, 1); } ) .use(logMetrics(metrics)) .use(myCustomMiddleware()); // Act - await handler({ ...dummyEvent, idx: 0 }, dummyContext); - await handler({ ...dummyEvent, idx: 1 }, dummyContext); + await handler({ ...event, idx: 0 }, context); + await handler({ ...event, idx: 1 }, context); // Assess expect(publishStoredMetricsSpy).toBeCalledTimes(2); }); }); + describe('Metrics resolution', () => { + test('serialized metrics in EMF format should not contain `StorageResolution` as key if `60` is set', async () => { + // Prepare + const metrics = new Metrics({ + namespace: 'serverlessAirline', + serviceName: 'orders', + }); + + const consoleSpy = jest.spyOn(metrics['console'], 'log'); + const handler = middy((): void => { + metrics.addMetric( + 'successfulBooking', + MetricUnit.Count, + 1, + MetricResolution.Standard + ); + }).use(logMetrics(metrics)); + + // Act + await handler(event, context); + + // Assess + expect(consoleSpy).toHaveBeenCalledWith( + JSON.stringify({ + _aws: { + Timestamp: 1466424490000, + CloudWatchMetrics: [ + { + Namespace: 'serverlessAirline', + Dimensions: [['service']], + Metrics: [ + { + Name: 'successfulBooking', + Unit: 'Count', + }, + ], + }, + ], + }, + service: 'orders', + successfulBooking: 1, + }) + ); + }); + + test('Should be StorageResolution `1` if MetricResolution is set to `High`', async () => { + // Prepare + const metrics = new Metrics({ + namespace: 'serverlessAirline', + serviceName: 'orders', + }); + const consoleSpy = jest.spyOn(metrics['console'], 'log'); + const handler = middy((): void => { + metrics.addMetric( + 'successfulBooking', + MetricUnit.Count, + 1, + MetricResolution.High + ); + }).use(logMetrics(metrics)); + + // Act + await handler(event, context); + + // Assess + expect(consoleSpy).toHaveBeenCalledWith( + JSON.stringify({ + _aws: { + Timestamp: 1466424490000, + CloudWatchMetrics: [ + { + Namespace: 'serverlessAirline', + Dimensions: [['service']], + Metrics: [ + { + Name: 'successfulBooking', + Unit: 'Count', + StorageResolution: 1, + }, + ], + }, + ], + }, + service: 'orders', + successfulBooking: 1, + }) + ); + }); + }); }); diff --git a/packages/metrics/tsconfig.esm.json b/packages/metrics/tsconfig.esm.json new file mode 100644 index 0000000000..123291b0cf --- /dev/null +++ b/packages/metrics/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/esm.json" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/metrics/tsconfig.json b/packages/metrics/tsconfig.json index 1cb9d72773..f216927295 100644 --- a/packages/metrics/tsconfig.json +++ b/packages/metrics/tsconfig.json @@ -1,8 +1,9 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./lib", + "outDir": "./lib/cjs", "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" }, "include": [ "./src/**/*" diff --git a/packages/parameters/jest.config.js b/packages/parameters/jest.config.cjs similarity index 93% rename from packages/parameters/jest.config.js rename to packages/parameters/jest.config.cjs index 64f709a02d..d06944c4d3 100644 --- a/packages/parameters/jest.config.js +++ b/packages/parameters/jest.config.cjs @@ -5,6 +5,9 @@ module.exports = { }, runner: 'groups', preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, diff --git a/packages/parameters/package.json b/packages/parameters/package.json index 41547a3983..e2801ceeae 100644 --- a/packages/parameters/package.json +++ b/packages/parameters/package.json @@ -13,16 +13,16 @@ "test": "npm run test:unit", "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", "jest": "jest --detectOpenHandles --verbose", - "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e", "test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e", "test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e", "test:e2e": "jest --group=e2e", "watch": "jest --watch", - "build": "tsc --build --force", + "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", - "prebuild": "rimraf ./lib", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { @@ -30,95 +30,113 @@ }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/parameters#readme", "license": "MIT-0", + "type": "module", "exports": { ".": { - "import": "./lib/index.js", - "require": "./lib/index.js" + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } }, "./base/types": { - "import": "./lib/types/BaseProvider.d.ts", - "require": "./lib/types/BaseProvider.d.ts" + "import": "./lib/esm/types/BaseProvider.d.ts", + "require": "./lib/cjs/types/BaseProvider.d.ts" }, "./base": { - "import": "./lib/base/index.js", - "require": "./lib/base/index.js" + "import": "./lib/esm/base/index.js", + "require": "./lib/cjs/base/index.js" }, "./ssm/types": { - "import": "./lib/types/SSMProvider.d.ts", - "require": "./lib/types/SSMProvider.d.ts" + "import": "./lib/esm/types/SSMProvider.d.ts", + "require": "./lib/cjs/types/SSMProvider.d.ts" }, "./ssm": { - "import": "./lib/ssm/index.js", - "require": "./lib/ssm/index.js" + "import": "./lib/esm/ssm/index.js", + "require": "./lib/cjs/ssm/index.js" }, "./secrets/types": { - "import": "./lib/types/SecretsProvider.d.ts", - "require": "./lib/types/SecretsProvider.d.ts" + "import": "./lib/esm/types/SecretsProvider.d.ts", + "require": "./lib/cjs/types/SecretsProvider.d.ts" }, "./secrets": { - "import": "./lib/secrets/index.js", - "require": "./lib/secrets/index.js" + "import": "./lib/esm/secrets/index.js", + "require": "./lib/cjs/secrets/index.js" }, "./dynamodb/types": { - "import": "./lib/types/AppConfigProvider.d.ts", - "require": "./lib/types/AppConfigProvider.d.ts" + "import": "./lib/esm/types/AppConfigProvider.d.ts", + "require": "./lib/cjs/types/AppConfigProvider.d.ts" }, "./dynamodb": { - "import": "./lib/dynamodb/index.js", - "require": "./lib/dynamodb/index.js" + "import": "./lib/esm/dynamodb/index.js", + "require": "./lib/cjs/dynamodb/index.js" }, "./appconfig/types": { - "import": "./lib/appconfig/index.js", - "require": "./lib/appconfig/index.js" + "import": "./lib/esm/appconfig/index.js", + "require": "./lib/cjs/appconfig/index.js" }, "./appconfig": { - "import": "./lib/appconfig/index.js", - "require": "./lib/appconfig/index.js" + "import": "./lib/esm/appconfig/index.js", + "require": "./lib/cjs/appconfig/index.js" }, "./errors": { - "import": "./lib/errors.js", - "require": "./lib/errors.js" + "import": "./lib/esm/errors.js", + "require": "./lib/cjs/errors.js" } }, "typesVersions": { "*": { "base/types": [ - "lib/types/BaseProvider.d.ts" + "lib/cjs/types/BaseProvider.d.ts", + "lib/esm/types/BaseProvider.d.ts" ], "base": [ - "lib/base/index.d.ts" + "lib/cjs/base/index.d.ts", + "lib/esm/base/index.d.ts" ], "ssm/types": [ - "lib/types/SSMProvider.d.ts" + "lib/cjs/types/SSMProvider.d.ts", + "lib/esm/types/SSMProvider.d.ts" ], "ssm": [ - "lib/ssm/index.d.ts" + "lib/cjs/ssm/index.d.ts", + "lib/esm/ssm/index.d.ts" ], "secrets/types": [ - "lib/types/SecretsProvider.d.ts" + "lib/cjs/types/SecretsProvider.d.ts", + "lib/esm/types/SecretsProvider.d.ts" ], "secrets": [ - "lib/secrets/index.d.ts" + "lib/cjs/secrets/index.d.ts", + "lib/esm/secrets/index.d.ts" ], "dynamodb/types": [ - "./lib/types/DynamoDBProvider.d.ts" + "./lib/cjs/types/DynamoDBProvider.d.ts", + "./lib/esm/types/DynamoDBProvider.d.ts" ], "dynamodb": [ - "lib/dynamodb/index.d.ts" + "lib/cjs/dynamodb/index.d.ts", + "lib/esm/dynamodb/index.d.ts" ], "appconfig/types": [ - "./lib/types/AppConfigProvider.d.ts" + "./lib/cjs/types/AppConfigProvider.d.ts", + "./lib/esm/types/AppConfigProvider.d.ts" ], "appconfig": [ - "lib/appconfig/index.d.ts" + "lib/cjs/appconfig/index.d.ts", + "lib/esm/appconfig/index.d.ts" ], "errors": [ - "lib/errors.d.ts" + "lib/cjs/errors.d.ts", + "lib/esm/errors.d.ts" ] } }, - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "files": [ "lib" ], @@ -139,6 +157,7 @@ "nodejs" ], "devDependencies": { + "@aws-lambda-powertools/testing-utils": "file:../testing", "@aws-sdk/client-appconfigdata": "^3.515.0", "@aws-sdk/client-dynamodb": "^3.515.0", "@aws-sdk/client-secrets-manager": "^3.515.0", @@ -179,4 +198,4 @@ "optional": true } } -} +} \ No newline at end of file diff --git a/packages/parameters/src/appconfig/AppConfigProvider.ts b/packages/parameters/src/appconfig/AppConfigProvider.ts index bf61526b66..4a77a19e99 100644 --- a/packages/parameters/src/appconfig/AppConfigProvider.ts +++ b/packages/parameters/src/appconfig/AppConfigProvider.ts @@ -1,4 +1,4 @@ -import { BaseProvider, DEFAULT_PROVIDERS } from '../base'; +import { BaseProvider } from '../base/BaseProvider.js'; import { AppConfigDataClient, StartConfigurationSessionCommand, @@ -9,8 +9,8 @@ import type { AppConfigProviderOptions, AppConfigGetOptions, AppConfigGetOutput, -} from '../types/AppConfigProvider'; -import { APPCONFIG_TOKEN_EXPIRATION } from '../constants'; +} from '../types/AppConfigProvider.js'; +import { APPCONFIG_TOKEN_EXPIRATION } from '../constants.js'; /** * ## Intro @@ -182,7 +182,7 @@ import { APPCONFIG_TOKEN_EXPIRATION } from '../constants'; * For more usage examples, see [our documentation](https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/parameters/). */ class AppConfigProvider extends BaseProvider { - public client!: AppConfigDataClient; + public declare client: AppConfigDataClient; protected configurationTokenStore = new Map< string, { value: string; expiration: number } @@ -361,4 +361,4 @@ class AppConfigProvider extends BaseProvider { } } -export { AppConfigProvider, DEFAULT_PROVIDERS }; +export { AppConfigProvider }; diff --git a/packages/parameters/src/appconfig/getAppConfig.ts b/packages/parameters/src/appconfig/getAppConfig.ts index 94d0a95e94..f7ec65f7f0 100644 --- a/packages/parameters/src/appconfig/getAppConfig.ts +++ b/packages/parameters/src/appconfig/getAppConfig.ts @@ -1,8 +1,9 @@ -import { AppConfigProvider, DEFAULT_PROVIDERS } from './AppConfigProvider'; +import { AppConfigProvider } from './AppConfigProvider.js'; +import { DEFAULT_PROVIDERS } from '../base/index.js'; import type { AppConfigGetOutput, GetAppConfigOptions, -} from '../types/AppConfigProvider'; +} from '../types/AppConfigProvider.js'; /** * ## Intro diff --git a/packages/parameters/src/appconfig/index.ts b/packages/parameters/src/appconfig/index.ts index 9b8a24a5a4..39d7f59dd9 100644 --- a/packages/parameters/src/appconfig/index.ts +++ b/packages/parameters/src/appconfig/index.ts @@ -1,2 +1,2 @@ -export * from './AppConfigProvider'; -export * from './getAppConfig'; +export { AppConfigProvider } from './AppConfigProvider.js'; +export { getAppConfig } from './getAppConfig.js'; diff --git a/packages/parameters/src/base/BaseProvider.ts b/packages/parameters/src/base/BaseProvider.ts index 494c44ab03..739913e3aa 100644 --- a/packages/parameters/src/base/BaseProvider.ts +++ b/packages/parameters/src/base/BaseProvider.ts @@ -5,17 +5,17 @@ import { isSdkClient, isString, } from '@aws-lambda-powertools/commons'; -import { GetOptions } from './GetOptions'; -import { GetMultipleOptions } from './GetMultipleOptions'; -import { ExpirableValue } from './ExpirableValue'; -import { GetParameterError, TransformParameterError } from '../errors'; -import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService'; -import { transformValue } from './transformValue'; +import { GetOptions } from './GetOptions.js'; +import { GetMultipleOptions } from './GetMultipleOptions.js'; +import { ExpirableValue } from './ExpirableValue.js'; +import { GetParameterError, TransformParameterError } from '../errors.js'; +import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js'; +import { transformValue } from './transformValue.js'; import type { BaseProviderInterface, GetMultipleOptionsInterface, GetOptionsInterface, -} from '../types/BaseProvider'; +} from '../types/BaseProvider.js'; /** * Base class for all providers. diff --git a/packages/parameters/src/base/DefaultProviders.ts b/packages/parameters/src/base/DefaultProviders.ts index 692dacdcc3..03832b4e85 100644 --- a/packages/parameters/src/base/DefaultProviders.ts +++ b/packages/parameters/src/base/DefaultProviders.ts @@ -1,4 +1,4 @@ -import type { BaseProviderInterface } from '../types/BaseProvider'; +import type { BaseProviderInterface } from '../types/BaseProvider.js'; // These providers are dinamycally intialized on first use of the helper functions const DEFAULT_PROVIDERS: Record = {}; diff --git a/packages/parameters/src/base/ExpirableValue.ts b/packages/parameters/src/base/ExpirableValue.ts index 7793319e59..d9d213c715 100644 --- a/packages/parameters/src/base/ExpirableValue.ts +++ b/packages/parameters/src/base/ExpirableValue.ts @@ -1,4 +1,4 @@ -import type { ExpirableValueInterface } from '../types/BaseProvider'; +import type { ExpirableValueInterface } from '../types/BaseProvider.js'; /** * Class to represent a value that can expire. diff --git a/packages/parameters/src/base/GetMultipleOptions.ts b/packages/parameters/src/base/GetMultipleOptions.ts index d452062e43..1bf2ca5514 100644 --- a/packages/parameters/src/base/GetMultipleOptions.ts +++ b/packages/parameters/src/base/GetMultipleOptions.ts @@ -1,6 +1,6 @@ -import { GetOptions } from './GetOptions'; -import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService'; -import type { GetMultipleOptionsInterface } from '../types/BaseProvider'; +import { GetOptions } from './GetOptions.js'; +import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js'; +import type { GetMultipleOptionsInterface } from '../types/BaseProvider.js'; /** * Options for the `getMultiple` method. diff --git a/packages/parameters/src/base/GetOptions.ts b/packages/parameters/src/base/GetOptions.ts index e83d0a622f..1128f47faa 100644 --- a/packages/parameters/src/base/GetOptions.ts +++ b/packages/parameters/src/base/GetOptions.ts @@ -1,9 +1,9 @@ -import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService'; -import { DEFAULT_MAX_AGE_SECS } from '../constants'; +import { EnvironmentVariablesService } from '../config/EnvironmentVariablesService.js'; +import { DEFAULT_MAX_AGE_SECS } from '../constants.js'; import type { GetOptionsInterface, TransformOptions, -} from '../types/BaseProvider'; +} from '../types/BaseProvider.js'; /** * Options for the `get` method. diff --git a/packages/parameters/src/base/index.ts b/packages/parameters/src/base/index.ts index 2833869ad7..760ba42344 100644 --- a/packages/parameters/src/base/index.ts +++ b/packages/parameters/src/base/index.ts @@ -1,4 +1,4 @@ -export * from './DefaultProviders'; -export * from './BaseProvider'; -export * from './GetOptions'; -export * from './GetMultipleOptions'; +export { BaseProvider } from './BaseProvider.js'; +export { DEFAULT_PROVIDERS } from './DefaultProviders.js'; +export { GetOptions } from './GetOptions.js'; +export { GetMultipleOptions } from './GetMultipleOptions.js'; diff --git a/packages/parameters/src/base/transformValue.ts b/packages/parameters/src/base/transformValue.ts index a3b767ddbd..3dc1c3fcea 100644 --- a/packages/parameters/src/base/transformValue.ts +++ b/packages/parameters/src/base/transformValue.ts @@ -1,9 +1,12 @@ -import type { JSONValue } from '@aws-lambda-powertools/commons'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import { isString } from '@aws-lambda-powertools/commons'; import { fromBase64 } from '@aws-sdk/util-base64-node'; -import { TRANSFORM_METHOD_BINARY, TRANSFORM_METHOD_JSON } from '../constants'; -import { TransformParameterError } from '../errors'; -import type { TransformOptions } from '../types/BaseProvider'; +import { + TRANSFORM_METHOD_BINARY, + TRANSFORM_METHOD_JSON, +} from '../constants.js'; +import { TransformParameterError } from '../errors.js'; +import type { TransformOptions } from '../types/BaseProvider.js'; /** * Utility function to transform a value. diff --git a/packages/parameters/src/config/ConfigServiceInterface.ts b/packages/parameters/src/config/ConfigServiceInterface.ts deleted file mode 100644 index c422265c48..0000000000 --- a/packages/parameters/src/config/ConfigServiceInterface.ts +++ /dev/null @@ -1,11 +0,0 @@ -interface ConfigServiceInterface { - get?(name: string): string; - - getServiceName(): string; - - getParametersMaxAge(): number | undefined; - - getSSMDecrypt(): string; -} - -export { ConfigServiceInterface }; diff --git a/packages/parameters/src/config/EnvironmentVariablesService.ts b/packages/parameters/src/config/EnvironmentVariablesService.ts index 3ae96c5d56..b0c39e9bf3 100644 --- a/packages/parameters/src/config/EnvironmentVariablesService.ts +++ b/packages/parameters/src/config/EnvironmentVariablesService.ts @@ -1,5 +1,5 @@ -import { ConfigServiceInterface } from './ConfigServiceInterface'; -import { DEFAULT_MAX_AGE_SECS } from '../constants'; +import { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; +import { DEFAULT_MAX_AGE_SECS } from '../constants.js'; import { EnvironmentVariablesService as CommonEnvironmentVariablesService } from '@aws-lambda-powertools/commons'; class EnvironmentVariablesService @@ -10,6 +10,11 @@ class EnvironmentVariablesService private parametersMaxAgeVariable = 'POWERTOOLS_PARAMETERS_MAX_AGE'; private ssmDecryptVariable = 'POWERTOOLS_PARAMETERS_SSM_DECRYPT'; + /** + * It returns the value of the POWERTOOLS_PARAMETERS_MAX_AGE environment variable. + * + * @returns {number|undefined} + */ public getParametersMaxAge(): number | undefined { const maxAge = this.get(this.parametersMaxAgeVariable); @@ -25,6 +30,11 @@ class EnvironmentVariablesService } } + /** + * It returns the value of the POWERTOOLS_PARAMETERS_SSM_DECRYPT environment variable. + * + * @returns {string} + */ public getSSMDecrypt(): string { return this.get(this.ssmDecryptVariable); } diff --git a/packages/parameters/src/dynamodb/DynamoDBProvider.ts b/packages/parameters/src/dynamodb/DynamoDBProvider.ts index 34d7026424..566635a626 100644 --- a/packages/parameters/src/dynamodb/DynamoDBProvider.ts +++ b/packages/parameters/src/dynamodb/DynamoDBProvider.ts @@ -1,4 +1,4 @@ -import { BaseProvider } from '../base'; +import { BaseProvider } from '../base/BaseProvider.js'; import { DynamoDBClient, GetItemCommand, @@ -12,12 +12,12 @@ import type { DynamoDBGetMultipleOptions, DynamoDBGetOutput, DynamoDBGetMultipleOutput, -} from '../types/DynamoDBProvider'; +} from '../types/DynamoDBProvider.js'; import type { GetItemCommandInput, QueryCommandInput, } from '@aws-sdk/client-dynamodb'; -import type { JSONValue } from '@aws-lambda-powertools/commons'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; /** * ## Intro @@ -235,7 +235,7 @@ import type { JSONValue } from '@aws-lambda-powertools/commons'; * For more usage examples, see [our documentation](https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/parameters/). */ class DynamoDBProvider extends BaseProvider { - public client!: DynamoDBClient; + public declare client: DynamoDBClient; protected keyAttr = 'id'; protected sortAttr = 'sk'; protected tableName: string; diff --git a/packages/parameters/src/dynamodb/index.ts b/packages/parameters/src/dynamodb/index.ts index 65bb792929..2165ec9606 100644 --- a/packages/parameters/src/dynamodb/index.ts +++ b/packages/parameters/src/dynamodb/index.ts @@ -1 +1 @@ -export * from './DynamoDBProvider'; +export { DynamoDBProvider } from './DynamoDBProvider.js'; diff --git a/packages/parameters/src/index.ts b/packages/parameters/src/index.ts index 13cad811c8..58580e1e1a 100644 --- a/packages/parameters/src/index.ts +++ b/packages/parameters/src/index.ts @@ -1,2 +1,3 @@ -export * from './errors'; -export * from './constants'; +export { clearCaches } from './base/DefaultProviders.js'; +export { GetParameterError, TransformParameterError } from './errors.js'; +export { Transform } from './constants.js'; diff --git a/packages/parameters/src/secrets/SecretsProvider.ts b/packages/parameters/src/secrets/SecretsProvider.ts index fbc6cf0951..c60d2aee85 100644 --- a/packages/parameters/src/secrets/SecretsProvider.ts +++ b/packages/parameters/src/secrets/SecretsProvider.ts @@ -1,4 +1,4 @@ -import { BaseProvider } from '../base'; +import { BaseProvider } from '../base/BaseProvider.js'; import { SecretsManagerClient, GetSecretValueCommand, @@ -8,7 +8,7 @@ import type { SecretsProviderOptions, SecretsGetOptions, SecretsGetOutput, -} from '../types/SecretsProvider'; +} from '../types/SecretsProvider.js'; /** * ## Intro @@ -145,7 +145,7 @@ import type { * @see https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/parameters/ */ class SecretsProvider extends BaseProvider { - public client!: SecretsManagerClient; + public declare client: SecretsManagerClient; /** * It initializes the SecretsProvider class. diff --git a/packages/parameters/src/secrets/getSecret.ts b/packages/parameters/src/secrets/getSecret.ts index 1a0432ecfe..ef84d5d58b 100644 --- a/packages/parameters/src/secrets/getSecret.ts +++ b/packages/parameters/src/secrets/getSecret.ts @@ -1,9 +1,9 @@ -import { DEFAULT_PROVIDERS } from '../base'; -import { SecretsProvider } from './SecretsProvider'; +import { DEFAULT_PROVIDERS } from '../base/DefaultProviders.js'; +import { SecretsProvider } from './SecretsProvider.js'; import type { SecretsGetOptions, SecretsGetOutput, -} from '../types/SecretsProvider'; +} from '../types/SecretsProvider.js'; /** * ## Intro diff --git a/packages/parameters/src/secrets/index.ts b/packages/parameters/src/secrets/index.ts index 5e54bcec5b..6bb75d813f 100644 --- a/packages/parameters/src/secrets/index.ts +++ b/packages/parameters/src/secrets/index.ts @@ -1,2 +1,2 @@ -export * from './SecretsProvider'; -export * from './getSecret'; +export { SecretsProvider } from './SecretsProvider.js'; +export { getSecret } from './getSecret.js'; diff --git a/packages/parameters/src/ssm/SSMProvider.ts b/packages/parameters/src/ssm/SSMProvider.ts index c663a024ee..787bd8915c 100644 --- a/packages/parameters/src/ssm/SSMProvider.ts +++ b/packages/parameters/src/ssm/SSMProvider.ts @@ -1,7 +1,7 @@ -import { BaseProvider, DEFAULT_PROVIDERS } from '../base'; -import { transformValue } from '../base/transformValue'; -import { GetParameterError } from '../errors'; -import { DEFAULT_MAX_AGE_SECS } from '../constants'; +import { BaseProvider } from '../base/BaseProvider.js'; +import { transformValue } from '../base/transformValue.js'; +import { GetParameterError } from '../errors.js'; +import { DEFAULT_MAX_AGE_SECS } from '../constants.js'; import { SSMClient, GetParameterCommand, @@ -26,7 +26,7 @@ import type { SSMGetParametersByNameOptions, SSMSplitBatchAndDecryptParametersOutputType, SSMGetParametersByNameFromCacheOutputType, -} from '../types/SSMProvider'; +} from '../types/SSMProvider.js'; /** * ## Intro @@ -262,7 +262,7 @@ import type { * For more usage examples, see [our documentation](https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/parameters/). */ class SSMProvider extends BaseProvider { - public client!: SSMClient; + public declare client: SSMClient; protected errorsKey = '_errors'; protected maxGetParametersItems = 10; @@ -902,4 +902,4 @@ class SSMProvider extends BaseProvider { } } -export { SSMProvider, DEFAULT_PROVIDERS }; +export { SSMProvider }; diff --git a/packages/parameters/src/ssm/getParameter.ts b/packages/parameters/src/ssm/getParameter.ts index 55144a232b..0f9e452a69 100644 --- a/packages/parameters/src/ssm/getParameter.ts +++ b/packages/parameters/src/ssm/getParameter.ts @@ -1,5 +1,6 @@ -import { SSMProvider, DEFAULT_PROVIDERS } from './SSMProvider'; -import type { SSMGetOptions, SSMGetOutput } from '../types/SSMProvider'; +import { DEFAULT_PROVIDERS } from '../base/DefaultProviders.js'; +import { SSMProvider } from './SSMProvider.js'; +import type { SSMGetOptions, SSMGetOutput } from '../types/SSMProvider.js'; /** * ## Intro diff --git a/packages/parameters/src/ssm/getParameters.ts b/packages/parameters/src/ssm/getParameters.ts index 9c93aee938..7f2a3b1fc1 100644 --- a/packages/parameters/src/ssm/getParameters.ts +++ b/packages/parameters/src/ssm/getParameters.ts @@ -1,8 +1,9 @@ -import { SSMProvider, DEFAULT_PROVIDERS } from './SSMProvider'; +import { DEFAULT_PROVIDERS } from '../base/DefaultProviders.js'; +import { SSMProvider } from './SSMProvider.js'; import type { SSMGetMultipleOptions, SSMGetMultipleOutput, -} from '../types/SSMProvider'; +} from '../types/SSMProvider.js'; /** * ## Intro diff --git a/packages/parameters/src/ssm/getParametersByName.ts b/packages/parameters/src/ssm/getParametersByName.ts index 973a690191..72128db5ae 100644 --- a/packages/parameters/src/ssm/getParametersByName.ts +++ b/packages/parameters/src/ssm/getParametersByName.ts @@ -1,8 +1,9 @@ -import { SSMProvider, DEFAULT_PROVIDERS } from './SSMProvider'; +import { DEFAULT_PROVIDERS } from '../base/DefaultProviders.js'; +import { SSMProvider } from './SSMProvider.js'; import type { SSMGetParametersByNameOptions, SSMGetParametersByNameOutput, -} from '../types/SSMProvider'; +} from '../types/SSMProvider.js'; /** * ## Intro diff --git a/packages/parameters/src/ssm/index.ts b/packages/parameters/src/ssm/index.ts index 2efdcd6952..ffa9aba3a9 100644 --- a/packages/parameters/src/ssm/index.ts +++ b/packages/parameters/src/ssm/index.ts @@ -1,4 +1,4 @@ -export * from './SSMProvider'; -export * from './getParameter'; -export * from './getParameters'; -export * from './getParametersByName'; +export { SSMProvider } from './SSMProvider.js'; +export { getParameter } from './getParameter.js'; +export { getParameters } from './getParameters.js'; +export { getParametersByName } from './getParametersByName.js'; diff --git a/packages/parameters/src/types/AppConfigProvider.ts b/packages/parameters/src/types/AppConfigProvider.ts index f20d7d656a..64dc4ee170 100644 --- a/packages/parameters/src/types/AppConfigProvider.ts +++ b/packages/parameters/src/types/AppConfigProvider.ts @@ -1,10 +1,10 @@ -import type { JSONValue } from '@aws-lambda-powertools/commons'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import type { AppConfigDataClient, AppConfigDataClientConfig, StartConfigurationSessionCommandInput, } from '@aws-sdk/client-appconfigdata'; -import type { GetOptionsInterface } from './BaseProvider'; +import type { GetOptionsInterface } from './BaseProvider.js'; /** * Base interface for AppConfigProviderOptions. diff --git a/packages/parameters/src/types/BaseProvider.ts b/packages/parameters/src/types/BaseProvider.ts index 5a82970460..f773a7267a 100644 --- a/packages/parameters/src/types/BaseProvider.ts +++ b/packages/parameters/src/types/BaseProvider.ts @@ -1,4 +1,4 @@ -import { Transform } from '../constants'; +import { Transform } from '../constants.js'; /** * Type for the transform option. diff --git a/packages/parameters/src/types/ConfigServiceInterface.ts b/packages/parameters/src/types/ConfigServiceInterface.ts new file mode 100644 index 0000000000..c05f1856a0 --- /dev/null +++ b/packages/parameters/src/types/ConfigServiceInterface.ts @@ -0,0 +1,25 @@ +import type { ConfigServiceInterface as ConfigServiceBaseInterface } from '@aws-lambda-powertools/commons/types'; + +/** + * Interface ConfigServiceInterface + * + * @interface + * @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime + * @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables + */ +interface ConfigServiceInterface extends ConfigServiceBaseInterface { + /** + * It returns the value of the POWERTOOLS_PARAMETERS_MAX_AGE environment variable. + * + * @returns {number|undefined} + */ + getParametersMaxAge(): number | undefined; + /** + * It returns the value of the POWERTOOLS_PARAMETERS_SSM_DECRYPT environment variable. + * + * @returns {string} + */ + getSSMDecrypt(): string; +} + +export type { ConfigServiceInterface }; diff --git a/packages/parameters/src/types/DynamoDBProvider.ts b/packages/parameters/src/types/DynamoDBProvider.ts index 53aecfed64..97b6241636 100644 --- a/packages/parameters/src/types/DynamoDBProvider.ts +++ b/packages/parameters/src/types/DynamoDBProvider.ts @@ -1,4 +1,4 @@ -import type { JSONValue } from '@aws-lambda-powertools/commons'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import type { DynamoDBClient, DynamoDBClientConfig, @@ -8,7 +8,7 @@ import type { import type { GetMultipleOptionsInterface, GetOptionsInterface, -} from './BaseProvider'; +} from './BaseProvider.js'; /** * Base interface for DynamoDBProviderOptions. diff --git a/packages/parameters/src/types/SSMProvider.ts b/packages/parameters/src/types/SSMProvider.ts index cd9267fac7..b647fe29c9 100644 --- a/packages/parameters/src/types/SSMProvider.ts +++ b/packages/parameters/src/types/SSMProvider.ts @@ -1,4 +1,4 @@ -import type { JSONValue } from '@aws-lambda-powertools/commons'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import type { GetParameterCommandInput, GetParametersByPathCommandInput, @@ -9,7 +9,7 @@ import type { GetMultipleOptionsInterface, GetOptionsInterface, TransformOptions, -} from './BaseProvider'; +} from './BaseProvider.js'; /** * Interface for SSMProvider with clientConfig property. diff --git a/packages/parameters/src/types/SecretsProvider.ts b/packages/parameters/src/types/SecretsProvider.ts index 6d797d37b3..9e2c492f3b 100644 --- a/packages/parameters/src/types/SecretsProvider.ts +++ b/packages/parameters/src/types/SecretsProvider.ts @@ -1,10 +1,10 @@ -import type { JSONValue } from '@aws-lambda-powertools/commons'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; import type { GetSecretValueCommandInput, SecretsManagerClient, SecretsManagerClientConfig, } from '@aws-sdk/client-secrets-manager'; -import type { GetOptionsInterface, TransformOptions } from './BaseProvider'; +import type { GetOptionsInterface, TransformOptions } from './BaseProvider.js'; /** * Base interface for SecretsProviderOptions. diff --git a/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts index 48735b703b..c55193326f 100644 --- a/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts +++ b/packages/parameters/tests/e2e/appConfigProvider.class.test.functionCode.ts @@ -1,9 +1,9 @@ import type { Context } from 'aws-lambda'; -import { Transform } from '../../src'; -import { AppConfigProvider } from '../../src/appconfig'; -import { AppConfigGetOptions } from '../../src/types/AppConfigProvider'; -import { TinyLogger } from '../helpers/tinyLogger'; -import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; +import { Transform } from '../../src/constants.js'; +import { AppConfigProvider } from '../../src/appconfig/AppConfigProvider.js'; +import { AppConfigGetOptions } from '../../src/types/AppConfigProvider.js'; +import { TinyLogger } from '../helpers/tinyLogger.js'; +import { middleware } from '../helpers/sdkMiddlewareRequestCounter.js'; import { AppConfigDataClient } from '@aws-sdk/client-appconfigdata'; // We use a custom logger to log pure JSON objects to stdout diff --git a/packages/parameters/tests/e2e/appConfigProvider.class.test.ts b/packages/parameters/tests/e2e/appConfigProvider.class.test.ts index 6d28b3ef04..69bb52ae17 100644 --- a/packages/parameters/tests/e2e/appConfigProvider.class.test.ts +++ b/packages/parameters/tests/e2e/appConfigProvider.class.test.ts @@ -6,18 +6,18 @@ import { invokeFunctionOnce, TestInvocationLogs, - TestNodejsFunction, TestStack, } from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { toBase64 } from '@aws-sdk/util-base64-node'; import { join } from 'node:path'; -import { TestAppConfigWithProfiles } from '../helpers/resources'; +import { TestAppConfigWithProfiles } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; /** * This test suite deploys a CDK stack with a Lambda function and a number of AppConfig parameters. diff --git a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts index 88f1eccd20..808bb188e9 100644 --- a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts +++ b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.functionCode.ts @@ -1,12 +1,12 @@ import type { Context } from 'aws-lambda'; -import { Transform } from '../../src'; -import { DynamoDBProvider } from '../../src/dynamodb'; +import { Transform } from '../../src/constants.js'; +import { DynamoDBProvider } from '../../src/dynamodb/DynamoDBProvider.js'; import { DynamoDBGetOptions, DynamoDBGetMultipleOptions, -} from '../../src/types/DynamoDBProvider'; -import { TinyLogger } from '../helpers/tinyLogger'; -import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; +} from '../../src/types/DynamoDBProvider.js'; +import { TinyLogger } from '../helpers/tinyLogger.js'; +import { middleware } from '../helpers/sdkMiddlewareRequestCounter.js'; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; // We use a custom logger to log pure JSON objects to stdout diff --git a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts index dfc730d469..8f1748246a 100644 --- a/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts +++ b/packages/parameters/tests/e2e/dynamoDBProvider.class.test.ts @@ -6,18 +6,18 @@ import { invokeFunctionOnce, TestInvocationLogs, - TestNodejsFunction, TestStack, } from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { AttributeType } from 'aws-cdk-lib/aws-dynamodb'; import { join } from 'node:path'; -import { TestDynamodbTableWithItems } from '../helpers/resources'; +import { TestDynamodbTableWithItems } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; /** * This test suite deploys a CDK stack with a Lambda function and a number of DynamoDB tables. diff --git a/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts index e7526fdefc..4c04484897 100644 --- a/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts +++ b/packages/parameters/tests/e2e/secretsProvider.class.test.functionCode.ts @@ -1,10 +1,10 @@ import { Context } from 'aws-lambda'; -import { TinyLogger } from '../helpers/tinyLogger'; +import { TinyLogger } from '../helpers/tinyLogger.js'; import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; -import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; -import { Transform } from '../../src'; -import { SecretsProvider } from '../../src/secrets'; -import { SecretsGetOptions } from '../../src/types/SecretsProvider'; +import { middleware } from '../helpers/sdkMiddlewareRequestCounter.js'; +import { Transform } from '../../src/constants.js'; +import { SecretsProvider } from '../../src/secrets/SecretsProvider.js'; +import { SecretsGetOptions } from '../../src/types/SecretsProvider.js'; const logger = new TinyLogger(); const defaultProvider = new SecretsProvider(); diff --git a/packages/parameters/tests/e2e/secretsProvider.class.test.ts b/packages/parameters/tests/e2e/secretsProvider.class.test.ts index 3b700cdd3f..9a8bffa9b7 100644 --- a/packages/parameters/tests/e2e/secretsProvider.class.test.ts +++ b/packages/parameters/tests/e2e/secretsProvider.class.test.ts @@ -6,18 +6,18 @@ import { invokeFunctionOnce, TestInvocationLogs, - TestNodejsFunction, TestStack, } from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { SecretValue } from 'aws-cdk-lib'; import { join } from 'node:path'; -import { TestSecret } from '../helpers/resources'; +import { TestSecret } from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; /** * Collection of e2e tests for SecretsProvider utility. diff --git a/packages/parameters/tests/e2e/ssmProvider.class.test.functionCode.ts b/packages/parameters/tests/e2e/ssmProvider.class.test.functionCode.ts index a8915c2779..f1b6171e0a 100644 --- a/packages/parameters/tests/e2e/ssmProvider.class.test.functionCode.ts +++ b/packages/parameters/tests/e2e/ssmProvider.class.test.functionCode.ts @@ -1,12 +1,12 @@ -import { Context } from 'aws-lambda'; -import { SSMProvider } from '../../src/ssm'; +import type { Context } from 'aws-lambda'; +import { SSMProvider } from '../../src/ssm/SSMProvider.js'; import { SSMGetOptions, SSMGetMultipleOptions, SSMGetParametersByNameOptions, -} from '../../src/types/SSMProvider'; -import { TinyLogger } from '../helpers/tinyLogger'; -import { middleware } from '../helpers/sdkMiddlewareRequestCounter'; +} from '../../src/types/SSMProvider.js'; +import { TinyLogger } from '../helpers/tinyLogger.js'; +import { middleware } from '../helpers/sdkMiddlewareRequestCounter.js'; import { SSMClient } from '@aws-sdk/client-ssm'; // We use a custom logger to log pure JSON objects to stdout diff --git a/packages/parameters/tests/e2e/ssmProvider.class.test.ts b/packages/parameters/tests/e2e/ssmProvider.class.test.ts index 771aafd7c8..f6fb8ed858 100644 --- a/packages/parameters/tests/e2e/ssmProvider.class.test.ts +++ b/packages/parameters/tests/e2e/ssmProvider.class.test.ts @@ -6,20 +6,20 @@ import { invokeFunctionOnce, TestInvocationLogs, - TestNodejsFunction, TestStack, } from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; import { join } from 'node:path'; import { TestSecureStringParameter, TestStringParameter, -} from '../helpers/resources'; +} from '../helpers/resources.js'; import { RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; /** * This test suite deploys a CDK stack with a Lambda function and a number of SSM parameters. diff --git a/packages/parameters/tests/helpers/resources.ts b/packages/parameters/tests/helpers/resources.ts index 0be54d8db4..424065961c 100644 --- a/packages/parameters/tests/helpers/resources.ts +++ b/packages/parameters/tests/helpers/resources.ts @@ -1,15 +1,15 @@ import type { ExtraTestProps, TestDynamodbTableProps, - TestStack, -} from '@aws-lambda-powertools/testing-utils'; +} from '@aws-lambda-powertools/testing-utils/types'; import { concatenateResourceName, getRuntimeKey, getArchitectureKey, - TestDynamodbTable, - TestNodejsFunction, + type TestStack, } from '@aws-lambda-powertools/testing-utils'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; +import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; import { marshall } from '@aws-sdk/util-dynamodb'; import { CfnOutput, Stack } from 'aws-cdk-lib'; import { diff --git a/packages/parameters/tests/helpers/tinyLogger.ts b/packages/parameters/tests/helpers/tinyLogger.ts index 4fd4603d53..c49ec87ed6 100644 --- a/packages/parameters/tests/helpers/tinyLogger.ts +++ b/packages/parameters/tests/helpers/tinyLogger.ts @@ -1,4 +1,4 @@ -import { Console } from 'console'; +import { Console } from 'node:console'; /** * A tiny logger that logs to stdout and stderr. diff --git a/packages/parameters/tests/unit/AppConfigProvider.test.ts b/packages/parameters/tests/unit/AppConfigProvider.test.ts index 84e72889d5..71881d3c48 100644 --- a/packages/parameters/tests/unit/AppConfigProvider.test.ts +++ b/packages/parameters/tests/unit/AppConfigProvider.test.ts @@ -3,9 +3,9 @@ * * @group unit/parameters/AppConfigProvider/class */ -import { AppConfigProvider } from '../../src/appconfig/index'; -import { ExpirableValue } from '../../src/base/ExpirableValue'; -import { AppConfigProviderOptions } from '../../src/types/AppConfigProvider'; +import { AppConfigProvider } from '../../src/appconfig/index.js'; +import { ExpirableValue } from '../../src/base/ExpirableValue.js'; +import { AppConfigProviderOptions } from '../../src/types/AppConfigProvider.js'; import { AppConfigDataClient, GetLatestConfigurationCommand, diff --git a/packages/parameters/tests/unit/BaseProvider.test.ts b/packages/parameters/tests/unit/BaseProvider.test.ts index 355ae5ca27..18e87bb0dc 100644 --- a/packages/parameters/tests/unit/BaseProvider.test.ts +++ b/packages/parameters/tests/unit/BaseProvider.test.ts @@ -3,9 +3,20 @@ * * @group unit/parameters/baseProvider/class */ -import { BaseProvider, clearCaches, DEFAULT_PROVIDERS } from '../../src/base'; -import { ExpirableValue } from '../../src/base/ExpirableValue'; -import { GetParameterError, TransformParameterError } from '../../src/errors'; +import { + BaseProvider, + DEFAULT_PROVIDERS, + GetOptions, + GetMultipleOptions, +} from '../../src/base/index.js'; +import { DEFAULT_MAX_AGE_SECS } from '../../src/constants.js'; +import type { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; +import { ExpirableValue } from '../../src/base/ExpirableValue.js'; +import { + GetParameterError, + TransformParameterError, + clearCaches, +} from '../../src/index.js'; import { toBase64 } from '@aws-sdk/util-base64-node'; const encoder = new TextEncoder(); @@ -614,3 +625,35 @@ describe('Function: clearCaches', () => { expect(provider2Spy).toBeCalledTimes(1); }); }); + +describe('Class: GetOptions', () => { + it('should set the default maxAge when not provided', () => { + // Prepare + const envVarsService = { + getParametersMaxAge: jest.fn(), + }; + const options = new GetOptions( + undefined, + envVarsService as unknown as EnvironmentVariablesService + ); + + // Assess + expect(options.maxAge).toBe(DEFAULT_MAX_AGE_SECS); + }); +}); + +describe('Class: GetMultipleOptions', () => { + it('should set throwOnTransformError to false when not provided', () => { + // Prepare + const envVarsService = { + getParametersMaxAge: jest.fn(), + }; + const options = new GetMultipleOptions( + undefined, + envVarsService as unknown as EnvironmentVariablesService + ); + + // Assess + expect(options.throwOnTransformError).toBe(false); + }); +}); diff --git a/packages/parameters/tests/unit/DynamoDBProvider.test.ts b/packages/parameters/tests/unit/DynamoDBProvider.test.ts index b8112b8908..12ccd80d09 100644 --- a/packages/parameters/tests/unit/DynamoDBProvider.test.ts +++ b/packages/parameters/tests/unit/DynamoDBProvider.test.ts @@ -3,7 +3,7 @@ * * @group unit/parameters/DynamoDBProvider/class */ -import { DynamoDBProvider } from '../../src/dynamodb'; +import { DynamoDBProvider } from '../../src/dynamodb/index.js'; import { DynamoDBClient, GetItemCommand, @@ -13,7 +13,7 @@ import type { GetItemCommandInput, QueryCommandInput, } from '@aws-sdk/client-dynamodb'; -import type { DynamoDBProviderOptions } from '../../src/types/DynamoDBProvider'; +import type { DynamoDBProviderOptions } from '../../src/types/DynamoDBProvider.js'; import { marshall } from '@aws-sdk/util-dynamodb'; import { addUserAgentMiddleware } from '@aws-lambda-powertools/commons'; import { mockClient } from 'aws-sdk-client-mock'; diff --git a/packages/parameters/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/parameters/tests/unit/EnvironmentVariablesService.test.ts similarity index 95% rename from packages/parameters/tests/unit/config/EnvironmentVariablesService.test.ts rename to packages/parameters/tests/unit/EnvironmentVariablesService.test.ts index 80e60086c5..c8bca2a8e0 100644 --- a/packages/parameters/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/parameters/tests/unit/EnvironmentVariablesService.test.ts @@ -3,7 +3,7 @@ * * @group unit/parameters/config */ -import { EnvironmentVariablesService } from '../../../src/config/EnvironmentVariablesService'; +import { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; describe('Class: EnvironmentVariablesService', () => { const ENVIRONMENT_VARIABLES = process.env; diff --git a/packages/parameters/tests/unit/SSMProvider.test.ts b/packages/parameters/tests/unit/SSMProvider.test.ts index c11d2410b5..04da68117c 100644 --- a/packages/parameters/tests/unit/SSMProvider.test.ts +++ b/packages/parameters/tests/unit/SSMProvider.test.ts @@ -3,7 +3,7 @@ * * @group unit/parameters/ssm/class */ -import { SSMProvider } from '../../src/ssm'; +import { SSMProvider } from '../../src/ssm/index.js'; import { SSMClient, GetParameterCommand, @@ -19,8 +19,8 @@ import type { SSMGetParametersByNameOptions, SSMSplitBatchAndDecryptParametersOutputType, SSMGetParametersByNameOutputInterface, -} from '../../src/types/SSMProvider'; -import { ExpirableValue } from '../../src/base/ExpirableValue'; +} from '../../src/types/SSMProvider.js'; +import { ExpirableValue } from '../../src/base/ExpirableValue.js'; import { toBase64 } from '@aws-sdk/util-base64-node'; import { addUserAgentMiddleware } from '@aws-lambda-powertools/commons'; diff --git a/packages/parameters/tests/unit/SecretsProvider.test.ts b/packages/parameters/tests/unit/SecretsProvider.test.ts index 9bfd254537..04f54adc26 100644 --- a/packages/parameters/tests/unit/SecretsProvider.test.ts +++ b/packages/parameters/tests/unit/SecretsProvider.test.ts @@ -3,13 +3,13 @@ * * @group unit/parameters/SecretsProvider/class */ -import { SecretsProvider } from '../../src/secrets'; +import { SecretsProvider } from '../../src/secrets/index.js'; import { SecretsManagerClient, GetSecretValueCommand, } from '@aws-sdk/client-secrets-manager'; import type { GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; -import type { SecretsProviderOptions } from '../../src/types/SecretsProvider'; +import type { SecretsProviderOptions } from '../../src/types/SecretsProvider.js'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; import { addUserAgentMiddleware } from '@aws-lambda-powertools/commons'; diff --git a/packages/parameters/tests/unit/getAppConfig.test.ts b/packages/parameters/tests/unit/getAppConfig.test.ts index 5884a9e30e..adbc2f6a6c 100644 --- a/packages/parameters/tests/unit/getAppConfig.test.ts +++ b/packages/parameters/tests/unit/getAppConfig.test.ts @@ -3,12 +3,9 @@ * * @group unit/parameters/AppConfigProvider/getAppConfig/function */ -import { - AppConfigProvider, - getAppConfig, - DEFAULT_PROVIDERS, -} from '../../src/appconfig'; -import { Transform } from '../../src'; +import { AppConfigProvider, getAppConfig } from '../../src/appconfig/index.js'; +import { DEFAULT_PROVIDERS } from '../../src/base/DefaultProviders.js'; +import { Transform } from '../../src/index.js'; import { AppConfigDataClient, StartConfigurationSessionCommand, @@ -18,7 +15,7 @@ import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; import { toBase64 } from '@aws-sdk/util-base64-node'; import { Uint8ArrayBlobAdapter } from '@smithy/util-stream'; -import { JSONValue } from '@aws-lambda-powertools/commons'; +import type { JSONValue } from '@aws-lambda-powertools/commons/types'; describe('Function: getAppConfig', () => { const client = mockClient(AppConfigDataClient); diff --git a/packages/parameters/tests/unit/getParameter.test.ts b/packages/parameters/tests/unit/getParameter.test.ts index 175f5e5f4a..e02b2b887e 100644 --- a/packages/parameters/tests/unit/getParameter.test.ts +++ b/packages/parameters/tests/unit/getParameter.test.ts @@ -3,8 +3,8 @@ * * @group unit/parameters/ssm/getParameter/function */ -import { DEFAULT_PROVIDERS } from '../../src/base'; -import { SSMProvider, getParameter } from '../../src/ssm'; +import { DEFAULT_PROVIDERS } from '../../src/base/index.js'; +import { SSMProvider, getParameter } from '../../src/ssm/index.js'; import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; diff --git a/packages/parameters/tests/unit/getParameters.test.ts b/packages/parameters/tests/unit/getParameters.test.ts index f1270b5a08..e8b0d7b430 100644 --- a/packages/parameters/tests/unit/getParameters.test.ts +++ b/packages/parameters/tests/unit/getParameters.test.ts @@ -3,8 +3,8 @@ * * @group unit/parameters/ssm/getParameters/function */ -import { DEFAULT_PROVIDERS } from '../../src/base'; -import { SSMProvider, getParameters } from '../../src/ssm'; +import { DEFAULT_PROVIDERS } from '../../src/base/index.js'; +import { SSMProvider, getParameters } from '../../src/ssm/index.js'; import { SSMClient, GetParametersByPathCommand } from '@aws-sdk/client-ssm'; import { mockClient } from 'aws-sdk-client-mock'; import 'aws-sdk-client-mock-jest'; diff --git a/packages/parameters/tests/unit/getParametersByName.test.ts b/packages/parameters/tests/unit/getParametersByName.test.ts index e0c6201d22..e423dc3502 100644 --- a/packages/parameters/tests/unit/getParametersByName.test.ts +++ b/packages/parameters/tests/unit/getParametersByName.test.ts @@ -3,9 +3,9 @@ * * @group unit/parameters/ssm/getParametersByName/function */ -import { DEFAULT_PROVIDERS } from '../../src/base'; -import { SSMProvider, getParametersByName } from '../../src/ssm'; -import type { SSMGetParametersByNameOptions } from '../../src/types/SSMProvider'; +import { DEFAULT_PROVIDERS } from '../../src/base/index.js'; +import { SSMProvider, getParametersByName } from '../../src/ssm/index.js'; +import type { SSMGetParametersByNameOptions } from '../../src/types/SSMProvider.js'; describe('Function: getParametersByName', () => { beforeEach(() => { diff --git a/packages/parameters/tests/unit/getSecret.test.ts b/packages/parameters/tests/unit/getSecret.test.ts index e1cf3b82ee..da72fab483 100644 --- a/packages/parameters/tests/unit/getSecret.test.ts +++ b/packages/parameters/tests/unit/getSecret.test.ts @@ -3,8 +3,8 @@ * * @group unit/parameters/SecretsProvider/getSecret/function */ -import { DEFAULT_PROVIDERS } from '../../src/base'; -import { SecretsProvider, getSecret } from '../../src/secrets'; +import { DEFAULT_PROVIDERS } from '../../src/base/index.js'; +import { SecretsProvider, getSecret } from '../../src/secrets/index.js'; import { SecretsManagerClient, GetSecretValueCommand, diff --git a/packages/parameters/tsconfig.esm.json b/packages/parameters/tsconfig.esm.json new file mode 100644 index 0000000000..123291b0cf --- /dev/null +++ b/packages/parameters/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/esm.json" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/parameters/tsconfig.json b/packages/parameters/tsconfig.json index 1cb9d72773..4836a14962 100644 --- a/packages/parameters/tsconfig.json +++ b/packages/parameters/tsconfig.json @@ -1,8 +1,9 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./lib", + "outDir": "./lib/cjs/", "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" }, "include": [ "./src/**/*" diff --git a/packages/testing/jest.config.js b/packages/testing/jest.config.cjs similarity index 93% rename from packages/testing/jest.config.js rename to packages/testing/jest.config.cjs index 6d6c2fecf2..f13ab92dfd 100644 --- a/packages/testing/jest.config.js +++ b/packages/testing/jest.config.cjs @@ -5,6 +5,9 @@ module.exports = { }, runner: 'groups', preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, diff --git a/packages/testing/package.json b/packages/testing/package.json index 1d1d7f92c1..c9d8b2103c 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -13,10 +13,11 @@ "jest": "jest --detectOpenHandles --verbose", "test:e2e": "echo 'Not implemented'", "watch": "jest --watch", - "build": "tsc --build --force", + "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", - "prebuild": "rimraf ./lib", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { @@ -29,8 +30,57 @@ "files": [ "lib" ], - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "type": "module", + "exports": { + ".": { + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } + }, + "./resources/lambda": { + "import": "./lib/esm/resources/TestNodejsFunction.js", + "require": "./lib/cjs/resources/TestNodejsFunction.js" + }, + "./resources/dynamodb": { + "import": "./lib/esm/resources/TestDynamodbTable.js", + "require": "./lib/cjs/resources/TestDynamodbTable.js" + }, + "./context": { + "import": "./lib/esm/context.js", + "require": "./lib/cjs/context.js" + }, + "./types": { + "import": "./lib/esm/types.js", + "require": "./lib/cjs/types.js" + } + }, + "typesVersions": { + "*": { + "resources/lambda": [ + "lib/cjs/resources/TestNodejsFunction.d.ts", + "lib/esm/resources/TestNodejsFunction.d.ts" + ], + "resources/dynamodb": [ + "lib/cjs/resources/TestDynamodbTable.d.ts", + "lib/esm/resources/TestDynamodbTable.d.ts" + ], + "types": [ + "lib/cjs/types.d.ts", + "lib/esm/types.d.ts" + ], + "context": [ + "lib/cjs/context.d.ts", + "lib/esm/context.d.ts" + ] + } + }, + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "keywords": [ "aws", "lambda", @@ -50,4 +100,4 @@ "aws-cdk-lib": "^2.127.0", "esbuild": "^0.20.0" } -} +} \ No newline at end of file diff --git a/packages/testing/src/TestInvocationLogs.ts b/packages/testing/src/TestInvocationLogs.ts index fa396c98eb..106033a180 100644 --- a/packages/testing/src/TestInvocationLogs.ts +++ b/packages/testing/src/TestInvocationLogs.ts @@ -1,26 +1,16 @@ -/** - * Log level. used for filtering the log - */ -const Level = { - DEBUG: 'DEBUG', - INFO: 'INFO', - WARN: 'WARN', - ERROR: 'ERROR', +import { LogLevel } from './constants.js'; +import type { FunctionLog } from './types.js'; + +const CloudWatchLogKeywords = { + END: 'END RequestId', + INIT_START: 'INIT_START', + REPORT: 'REPORT RequestId', + START: 'START RequestId', + XRAY: 'XRAY TraceId', } as const; -type ErrorField = { - name: string; - message: string; - stack: string; -}; - -type FunctionLog = { - level: keyof typeof Level; - error: ErrorField; -} & { [key: string]: unknown }; - class TestInvocationLogs { - public static LEVEL = Level; + public static readonly LEVEL = LogLevel; /** * Array of logs from invocation. @@ -51,7 +41,7 @@ class TestInvocationLogs { */ public doesAnyFunctionLogsContains( text: string, - levelToFilter?: keyof typeof Level + levelToFilter?: keyof typeof LogLevel ): boolean { const filteredLogs = this.getFunctionLogs(levelToFilter).filter((log) => log.includes(text) @@ -69,25 +59,26 @@ class TestInvocationLogs { } /** - * Return the index of the log that contains END RequestId + * Return the index of the log that contains `END RequestId` * @param logs * @returns {number} index of the log that contains END RequestId */ public static getEndLogIndex(logs: string[]): number { - return logs.findIndex((log) => log.startsWith('END RequestId')); + return logs.findIndex((log) => log.startsWith(CloudWatchLogKeywords.END)); } /** - * Return only logs from function, exclude START, END, REPORT, + * Return only logs from function, exclude INIT_START, START, END, REPORT, * and X-Ray log generated by the Lambda service. * - * @param {typeof Level} [levelToFilter] - Level to filter the logs + * @param {typeof LogLevel} [levelToFilter] - Level to filter the logs * @returns Array of function logs, filtered by level if provided */ - public getFunctionLogs(levelToFilter?: keyof typeof Level): string[] { - const startLogIndex = TestInvocationLogs.getStartLogIndex(this.logs); - const endLogIndex = TestInvocationLogs.getEndLogIndex(this.logs); - let filteredLogs = this.logs.slice(startLogIndex + 1, endLogIndex); + public getFunctionLogs(levelToFilter?: keyof typeof LogLevel): string[] { + const exclusionKeywords = Object.values(CloudWatchLogKeywords); + let filteredLogs = this.logs.filter( + (log) => !exclusionKeywords.some((keyword) => log.startsWith(keyword)) + ); if (levelToFilter) { filteredLogs = filteredLogs.filter((log) => { @@ -97,7 +88,9 @@ class TestInvocationLogs { return parsedLog.level == levelToFilter; } catch (error) { // If log is not from structured logging : such as metrics one. - return (log.split('\t')[2] as keyof typeof Level) === levelToFilter; + return ( + (log.split('\t')[2] as keyof typeof LogLevel) === levelToFilter + ); } }); } @@ -105,8 +98,44 @@ class TestInvocationLogs { return filteredLogs; } + /** + * Return the index of the log that contains `INIT_START` + * @param logs + * @returns {number} index of the log that contains `INIT_START` + */ + public static getInitLogIndex(logs: string[]): number { + return logs.findIndex((log) => + log.startsWith(CloudWatchLogKeywords.INIT_START) + ); + } + + /** + * Return the index of the log that contains `REPORT RequestId` + * @param logs + * @returns {number} index of the log that contains `REPORT RequestId` + */ + public static getReportLogIndex(logs: string[]): number { + return logs.findIndex((log) => + log.startsWith(CloudWatchLogKeywords.REPORT) + ); + } + + /** + * Return the index of the log that contains `START RequestId` + * @param logs + * @returns {number} index of the log that contains `START RequestId` + */ public static getStartLogIndex(logs: string[]): number { - return logs.findIndex((log) => log.startsWith('START RequestId')); + return logs.findIndex((log) => log.startsWith(CloudWatchLogKeywords.START)); + } + + /** + * Return the index of the log that contains `XRAY TraceId` + * @param logs + * @returns {number} index of the log that contains `XRAY TraceId` + */ + public static getXRayLogIndex(logs: string[]): number { + return logs.findIndex((log) => log.startsWith(CloudWatchLogKeywords.XRAY)); } /** diff --git a/packages/testing/src/TestStack.ts b/packages/testing/src/TestStack.ts index 952f00a5a4..3e5d32da20 100644 --- a/packages/testing/src/TestStack.ts +++ b/packages/testing/src/TestStack.ts @@ -4,35 +4,8 @@ import { readFile } from 'node:fs/promises'; import { App, Stack } from 'aws-cdk-lib'; import { AwsCdkCli, RequireApproval } from '@aws-cdk/cli-lib-alpha'; import type { ICloudAssemblyDirectoryProducer } from '@aws-cdk/cli-lib-alpha'; -import { generateTestUniqueName } from './helpers'; - -type StackNameProps = { - /** - * Prefix for the stack name. - */ - stackNamePrefix: string; - /** - * Name of the test. - */ - testName: string; -}; - -interface TestStackProps { - /** - * Name of the test stack. - */ - stackNameProps: StackNameProps; - /** - * Reference to the AWS CDK App object. - * @default new App() - */ - app?: App; - /** - * Reference to the AWS CDK Stack object. - * @default new Stack(this.app, stackName) - */ - stack?: Stack; -} +import { generateTestUniqueName } from './helpers.js'; +import type { TestStackProps } from './types.js'; /** * Test stack that can be deployed to the selected environment. diff --git a/packages/testing/src/constants.ts b/packages/testing/src/constants.ts index e024780918..0699c4efb4 100644 --- a/packages/testing/src/constants.ts +++ b/packages/testing/src/constants.ts @@ -27,9 +27,20 @@ const TEST_ARCHITECTURES = { arm64: Architecture.ARM_64, } as const; +/** + * Log level. used for filtering the log + */ +const LogLevel = { + DEBUG: 'DEBUG', + INFO: 'INFO', + WARN: 'WARN', + ERROR: 'ERROR', +} as const; + export { TEST_RUNTIMES, defaultRuntime, TEST_ARCHITECTURES, defaultArchitecture, + LogLevel, }; diff --git a/packages/commons/src/samples/resources/contexts/hello-world.ts b/packages/testing/src/context.ts similarity index 89% rename from packages/commons/src/samples/resources/contexts/hello-world.ts rename to packages/testing/src/context.ts index 2dccd0cc8b..59ebe833d8 100644 --- a/packages/commons/src/samples/resources/contexts/hello-world.ts +++ b/packages/testing/src/context.ts @@ -1,6 +1,6 @@ import type { Context } from 'aws-lambda'; -const helloworldContext: Context = { +export default { callbackWaitsForEmptyEventLoop: true, functionVersion: '$LATEST', functionName: 'foo-bar-function', @@ -14,6 +14,4 @@ const helloworldContext: Context = { done: () => console.log('Done!'), fail: () => console.log('Failed!'), succeed: () => console.log('Succeeded!'), -}; - -export { helloworldContext }; +} as const as Context; diff --git a/packages/testing/src/helpers.ts b/packages/testing/src/helpers.ts index ab1a0222e5..10d584d242 100644 --- a/packages/testing/src/helpers.ts +++ b/packages/testing/src/helpers.ts @@ -4,7 +4,7 @@ import { defaultRuntime, TEST_ARCHITECTURES, defaultArchitecture, -} from './constants'; +} from './constants.js'; const isValidRuntimeKey = ( runtime: string diff --git a/packages/testing/src/index.ts b/packages/testing/src/index.ts index 7c8276c774..89341279d2 100644 --- a/packages/testing/src/index.ts +++ b/packages/testing/src/index.ts @@ -1,6 +1,18 @@ -export * from './TestStack'; -export * from './constants'; -export * from './helpers'; -export * from './resources'; -export * from './invokeTestFunction'; -export * from './TestInvocationLogs'; +export { TestStack } from './TestStack.js'; +export { + TEST_RUNTIMES, + defaultRuntime, + TEST_ARCHITECTURES, + defaultArchitecture, + LogLevel, +} from './constants.js'; +export { + isValidRuntimeKey, + getRuntimeKey, + generateTestUniqueName, + concatenateResourceName, + findAndGetStackOutputValue, + getArchitectureKey, +} from './helpers.js'; +export { invokeFunction, invokeFunctionOnce } from './invokeTestFunction.js'; +export { TestInvocationLogs } from './TestInvocationLogs.js'; diff --git a/packages/testing/src/invokeTestFunction.ts b/packages/testing/src/invokeTestFunction.ts index 4b333d756d..8ff4099109 100644 --- a/packages/testing/src/invokeTestFunction.ts +++ b/packages/testing/src/invokeTestFunction.ts @@ -1,13 +1,7 @@ import { InvokeCommand, LambdaClient } from '@aws-sdk/client-lambda'; import { fromUtf8 } from '@smithy/util-utf8'; -import { TestInvocationLogs } from './TestInvocationLogs'; - -type InvokeTestFunctionOptions = { - functionName: string; - times?: number; - invocationMode?: 'PARALLEL' | 'SEQUENTIAL'; - payload?: Record | Array>; -}; +import { TestInvocationLogs } from './TestInvocationLogs.js'; +import type { InvokeTestFunctionOptions } from './types.js'; const lambdaClient = new LambdaClient({}); diff --git a/packages/testing/src/resources/TestDynamodbTable.ts b/packages/testing/src/resources/TestDynamodbTable.ts index 2aa663639a..3717f54ad1 100644 --- a/packages/testing/src/resources/TestDynamodbTable.ts +++ b/packages/testing/src/resources/TestDynamodbTable.ts @@ -1,9 +1,9 @@ import { CfnOutput, RemovalPolicy } from 'aws-cdk-lib'; import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb'; import { randomUUID } from 'node:crypto'; -import { concatenateResourceName } from '../helpers'; -import type { TestStack } from '../TestStack'; -import type { TestDynamodbTableProps, ExtraTestProps } from './types'; +import { concatenateResourceName } from '../helpers.js'; +import type { TestStack } from '../TestStack.js'; +import type { TestDynamodbTableProps, ExtraTestProps } from '../types.js'; /** * A DynamoDB Table that can be used in tests. @@ -36,4 +36,4 @@ class TestDynamodbTable extends Table { } } -export { TestDynamodbTable, TestDynamodbTableProps }; +export { TestDynamodbTable }; diff --git a/packages/testing/src/resources/TestNodejsFunction.ts b/packages/testing/src/resources/TestNodejsFunction.ts index 8ee0ed2f6b..5be3597caa 100644 --- a/packages/testing/src/resources/TestNodejsFunction.ts +++ b/packages/testing/src/resources/TestNodejsFunction.ts @@ -3,14 +3,14 @@ import { Tracing } from 'aws-cdk-lib/aws-lambda'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import { RetentionDays } from 'aws-cdk-lib/aws-logs'; import { randomUUID } from 'node:crypto'; -import { TEST_RUNTIMES, TEST_ARCHITECTURES } from '../constants'; +import { TEST_RUNTIMES, TEST_ARCHITECTURES } from '../constants.js'; import { concatenateResourceName, getRuntimeKey, getArchitectureKey, -} from '../helpers'; -import type { TestStack } from '../TestStack'; -import type { ExtraTestProps, TestNodejsFunctionProps } from './types'; +} from '../helpers.js'; +import type { TestStack } from '../TestStack.js'; +import type { ExtraTestProps, TestNodejsFunctionProps } from '../types.js'; /** * A NodejsFunction that can be used in tests. @@ -43,4 +43,4 @@ class TestNodejsFunction extends NodejsFunction { } } -export { ExtraTestProps, TestNodejsFunction, TestNodejsFunctionProps }; +export { TestNodejsFunction }; diff --git a/packages/testing/src/resources/index.ts b/packages/testing/src/resources/index.ts deleted file mode 100644 index ce4b5bbc26..0000000000 --- a/packages/testing/src/resources/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './TestNodejsFunction'; -export * from './TestDynamodbTable'; diff --git a/packages/testing/src/resources/types.ts b/packages/testing/src/resources/types.ts deleted file mode 100644 index 5596a08f31..0000000000 --- a/packages/testing/src/resources/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { TableProps, AttributeType } from 'aws-cdk-lib/aws-dynamodb'; -import type { NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs'; - -interface ExtraTestProps { - /** - * The suffix to be added to the resource name. - * - * For example, if the resource name is `fn-12345` and the suffix is `BasicFeatures`, - * the output will be `fn-12345-BasicFeatures`. - * - * Note that the maximum length of the name is 64 characters, so the suffix might be truncated. - */ - nameSuffix: string; -} - -type TestDynamodbTableProps = Omit< - TableProps, - 'removalPolicy' | 'tableName' | 'billingMode' | 'partitionKey' -> & { - partitionKey?: { - name: string; - type: AttributeType; - }; -}; - -type TestNodejsFunctionProps = Omit< - NodejsFunctionProps, - 'logRetention' | 'runtime' | 'functionName' ->; - -export { ExtraTestProps, TestDynamodbTableProps, TestNodejsFunctionProps }; diff --git a/packages/testing/src/types.ts b/packages/testing/src/types.ts new file mode 100644 index 0000000000..ce11c0fc41 --- /dev/null +++ b/packages/testing/src/types.ts @@ -0,0 +1,88 @@ +import type { TableProps, AttributeType } from 'aws-cdk-lib/aws-dynamodb'; +import type { NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs'; +import type { App, Stack } from 'aws-cdk-lib'; +import { LogLevel } from './constants.js'; + +interface ExtraTestProps { + /** + * The suffix to be added to the resource name. + * + * For example, if the resource name is `fn-12345` and the suffix is `BasicFeatures`, + * the output will be `fn-12345-BasicFeatures`. + * + * Note that the maximum length of the name is 64 characters, so the suffix might be truncated. + */ + nameSuffix: string; +} + +type TestDynamodbTableProps = Omit< + TableProps, + 'removalPolicy' | 'tableName' | 'billingMode' | 'partitionKey' +> & { + partitionKey?: { + name: string; + type: AttributeType; + }; +}; + +type TestNodejsFunctionProps = Omit< + NodejsFunctionProps, + 'logRetention' | 'runtime' | 'functionName' +>; + +type InvokeTestFunctionOptions = { + functionName: string; + times?: number; + invocationMode?: 'PARALLEL' | 'SEQUENTIAL'; + payload?: Record | Array>; +}; + +type ErrorField = { + name: string; + message: string; + stack: string; +}; + +type FunctionLog = { + level: keyof typeof LogLevel; + error: ErrorField; +} & { [key: string]: unknown }; + +type StackNameProps = { + /** + * Prefix for the stack name. + */ + stackNamePrefix: string; + /** + * Name of the test. + */ + testName: string; +}; + +interface TestStackProps { + /** + * Name of the test stack. + */ + stackNameProps: StackNameProps; + /** + * Reference to the AWS CDK App object. + * @default new App() + */ + app?: App; + /** + * Reference to the AWS CDK Stack object. + * @default new Stack(this.app, stackName) + */ + stack?: Stack; +} + +export type { + ExtraTestProps, + TestDynamodbTableProps, + TestNodejsFunctionProps, + InvokeTestFunctionOptions, + ErrorField, + FunctionLog, + StackNameProps, + TestStackProps, +}; diff --git a/packages/testing/tests/unit/TestInvocationLogs.test.ts b/packages/testing/tests/unit/TestInvocationLogs.test.ts index c81a05b0b0..33c4e46968 100644 --- a/packages/testing/tests/unit/TestInvocationLogs.test.ts +++ b/packages/testing/tests/unit/TestInvocationLogs.test.ts @@ -5,7 +5,7 @@ * */ -import { TestInvocationLogs } from '../../src/TestInvocationLogs'; +import { TestInvocationLogs } from '../../src/TestInvocationLogs.js'; const exampleLogs = `START RequestId: c6af9ac6-7b61-11e6-9a41-93e812345678 Version: $LATEST {"cold_start":true,"function_arn":"arn:aws:lambda:eu-west-1:561912387782:function:loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c","function_memory_size":128,"function_name":"loggerMiddyStandardFeatures-c555a2ec-1121-4586-9c04-185ab36ea34c","function_request_id":"7f586697-238a-4c3b-9250-a5f057c1119c","level":"DEBUG","message":"This is a DEBUG log but contains the word INFO some context and persistent key","service":"logger-e2e-testing","timestamp":"2022-01-27T16:04:39.323Z","persistentKey":"works"} diff --git a/packages/testing/tsconfig.esm.json b/packages/testing/tsconfig.esm.json new file mode 100644 index 0000000000..123291b0cf --- /dev/null +++ b/packages/testing/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/esm.json" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/testing/tsconfig.json b/packages/testing/tsconfig.json index 1cb9d72773..f216927295 100644 --- a/packages/testing/tsconfig.json +++ b/packages/testing/tsconfig.json @@ -1,8 +1,9 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./lib", + "outDir": "./lib/cjs", "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" }, "include": [ "./src/**/*" diff --git a/packages/tracer/jest.config.js b/packages/tracer/jest.config.cjs similarity index 93% rename from packages/tracer/jest.config.js rename to packages/tracer/jest.config.cjs index f6b014a746..4932f36338 100644 --- a/packages/tracer/jest.config.js +++ b/packages/tracer/jest.config.cjs @@ -5,6 +5,9 @@ module.exports = { }, runner: 'groups', preset: 'ts-jest', + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, transform: { '^.+\\.ts?$': 'ts-jest', }, diff --git a/packages/tracer/package.json b/packages/tracer/package.json index 259f618ac3..a374fc7a0f 100644 --- a/packages/tracer/package.json +++ b/packages/tracer/package.json @@ -13,16 +13,16 @@ "test": "npm run test:unit", "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", "jest": "jest --detectOpenHandles --verbose", - "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", "test:e2e:nodejs16x": "RUNTIME=nodejs16x jest --group=e2e", "test:e2e:nodejs18x": "RUNTIME=nodejs18x jest --group=e2e", "test:e2e:nodejs20x": "RUNTIME=nodejs20x jest --group=e2e", "test:e2e": "jest --group=e2e", "watch": "jest --watch", - "build": "tsc --build --force", + "build:cjs": "tsc --build tsconfig.json && echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", + "build:esm": "tsc --build tsconfig.esm.json && echo '{ \"type\": \"module\" }' > lib/esm/package.json", + "build": "npm run build:esm & npm run build:cjs", "lint": "eslint --ext .ts,.js --no-error-on-unmatched-pattern .", "lint-fix": "eslint --fix --ext .ts,.js --no-error-on-unmatched-pattern .", - "prebuild": "rimraf ./lib", "prepack": "node ../../.github/scripts/release_patch_package_json.js ." }, "lint-staged": { @@ -30,8 +30,6 @@ }, "homepage": "https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/packages/tracer#readme", "license": "MIT-0", - "main": "./lib/index.js", - "types": "./lib/index.d.ts", "devDependencies": { "@aws-lambda-powertools/testing-utils": "file:../testing", "@aws-sdk/client-dynamodb": "^3.515.0", @@ -49,6 +47,41 @@ "optional": true } }, + "type": "module", + "exports": { + ".": { + "require": { + "types": "./lib/cjs/index.d.ts", + "default": "./lib/cjs/index.js" + }, + "import": { + "types": "./lib/esm/index.d.ts", + "default": "./lib/esm/index.js" + } + }, + "./middleware": { + "import": "./lib/esm/middleware/middy.js", + "require": "./lib/cjs/middleware/middy.js" + }, + "./types": { + "import": "./lib/esm/types/index.js", + "require": "./lib/cjs/types/index.js" + } + }, + "typesVersions": { + "*": { + "middleware": [ + "lib/cjs/middleware/middy.d.ts", + "lib/esm/middleware/middy.d.ts" + ], + "types": [ + "lib/cjs/types/index.d.ts", + "lib/esm/types/index.d.ts" + ] + } + }, + "types": "./lib/cjs/index.d.ts", + "main": "./lib/cjs/index.js", "files": [ "lib" ], @@ -72,4 +105,4 @@ "serverless", "nodejs" ] -} +} \ No newline at end of file diff --git a/packages/tracer/src/Tracer.ts b/packages/tracer/src/Tracer.ts index 4d1fb11267..622adeae98 100644 --- a/packages/tracer/src/Tracer.ts +++ b/packages/tracer/src/Tracer.ts @@ -1,22 +1,22 @@ import type { Handler } from 'aws-lambda'; -import { - type AsyncHandler, - type SyncHandler, - Utility, -} from '@aws-lambda-powertools/commons'; -import type { TracerInterface } from '.'; -import { - type ConfigServiceInterface, - EnvironmentVariablesService, -} from './config'; +import { Utility } from '@aws-lambda-powertools/commons'; import type { + AsyncHandler, + SyncHandler, HandlerMethodDecorator, +} from '@aws-lambda-powertools/commons/types'; +import { EnvironmentVariablesService } from './config/EnvironmentVariablesService.js'; +import type { ConfigServiceInterface } from './types/ConfigServiceInterface.js'; +import type { + TracerInterface, TracerOptions, + AnyClass, MethodDecorator, CaptureLambdaHandlerOptions, CaptureMethodOptions, -} from './types'; -import { ProviderService, type ProviderServiceInterface } from './provider'; +} from './types/Tracer.js'; +import { ProviderService } from './provider/ProviderService.js'; +import type { ProviderServiceInterface } from './types/ProviderServiceInterface.js'; import { type Segment, Subsegment } from 'aws-xray-sdk-core'; /** @@ -46,7 +46,8 @@ import { type Segment, Subsegment } from 'aws-xray-sdk-core'; * * @example * ```typescript - * import { captureLambdaHandler, Tracer } from '@aws-lambda-powertools/tracer'; + * import { Tracer } from '@aws-lambda-powertools/tracer'; + * import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; * import middy from '@middy/core'; * * const tracer = new Tracer({ serviceName: 'serverlessAirline' }); @@ -69,7 +70,7 @@ import { type Segment, Subsegment } from 'aws-xray-sdk-core'; * @example * ```typescript * import { Tracer } from '@aws-lambda-powertools/tracer'; - * import { LambdaInterface } from '@aws-lambda-powertools/commons'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; * * const tracer = new Tracer({ serviceName: 'serverlessAirline' }); * @@ -345,7 +346,7 @@ class Tracer extends Utility implements TracerInterface { * @example * ```typescript * import { Tracer } from '@aws-lambda-powertools/tracer'; - * import { LambdaInterface } from '@aws-lambda-powertools/commons'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; * * const tracer = new Tracer({ serviceName: 'serverlessAirline' }); * @@ -460,7 +461,9 @@ class Tracer extends Utility implements TracerInterface { * @decorator Class * @param options - (_optional_) Options for the decorator */ - public captureMethod(options?: CaptureMethodOptions): MethodDecorator { + public captureMethod( + options?: CaptureMethodOptions + ): MethodDecorator { return (_target, propertyKey, descriptor) => { // The descriptor.value is the method this decorator decorates, it cannot be undefined. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/packages/tracer/src/TracerInterface.ts b/packages/tracer/src/TracerInterface.ts deleted file mode 100644 index b346af0f73..0000000000 --- a/packages/tracer/src/TracerInterface.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { - CaptureLambdaHandlerOptions, - CaptureMethodOptions, - HandlerMethodDecorator, - MethodDecorator, -} from './types'; -import type { Segment, Subsegment } from 'aws-xray-sdk-core'; - -interface TracerInterface { - addErrorAsMetadata(error: Error, remote?: boolean): void; - addResponseAsMetadata(data?: unknown, methodName?: string): void; - addServiceNameAnnotation(): void; - annotateColdStart(): void; - captureAWS(aws: T): void | T; - captureAWSv3Client(service: T): void | T; - captureAWSClient(service: T): void | T; - captureLambdaHandler( - options?: CaptureLambdaHandlerOptions - ): HandlerMethodDecorator; - captureMethod(options?: CaptureMethodOptions): MethodDecorator; - getSegment(): Segment | Subsegment | undefined; - getRootXrayTraceId(): string | undefined; - isTraceSampled(): boolean; - isTracingEnabled(): boolean; - putAnnotation: (key: string, value: string | number | boolean) => void; - putMetadata: ( - key: string, - value: unknown, - namespace?: string | undefined - ) => void; - setSegment(segment: Segment | Subsegment): void; -} - -export { TracerInterface }; diff --git a/packages/tracer/src/config/ConfigServiceInterface.ts b/packages/tracer/src/config/ConfigServiceInterface.ts deleted file mode 100644 index 78169c4de3..0000000000 --- a/packages/tracer/src/config/ConfigServiceInterface.ts +++ /dev/null @@ -1,15 +0,0 @@ -interface ConfigServiceInterface { - get(name: string): string; - - getCaptureHTTPsRequests(): string; - - getTracingEnabled(): string; - - getServiceName(): string; - - getTracingCaptureResponse(): string; - - getTracingCaptureError(): string; -} - -export { ConfigServiceInterface }; diff --git a/packages/tracer/src/config/EnvironmentVariablesService.ts b/packages/tracer/src/config/EnvironmentVariablesService.ts index ecb46b3e23..d6d9bb7343 100644 --- a/packages/tracer/src/config/EnvironmentVariablesService.ts +++ b/packages/tracer/src/config/EnvironmentVariablesService.ts @@ -1,4 +1,4 @@ -import { ConfigServiceInterface } from './ConfigServiceInterface'; +import { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; import { EnvironmentVariablesService as CommonEnvironmentVariablesService } from '@aws-lambda-powertools/commons'; class EnvironmentVariablesService @@ -14,26 +14,56 @@ class EnvironmentVariablesService private tracerCaptureResponseVariable = 'POWERTOOLS_TRACER_CAPTURE_RESPONSE'; private tracingEnabledVariable = 'POWERTOOLS_TRACE_ENABLED'; + /** + * It returns the value of the AWS_EXECUTION_ENV environment variable. + * + * @returns {string} + */ public getAwsExecutionEnv(): string { return this.get(this.awsExecutionEnv); } + /** + * It returns the value of the POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable. + * + * @returns {string} + */ public getCaptureHTTPsRequests(): string { return this.get(this.tracerCaptureHTTPsRequestsVariable); } + /** + * It returns the value of the AWS_SAM_LOCAL environment variable. + * + * @returns {string} + */ public getSamLocal(): string { return this.get(this.samLocalVariable); } + /** + * It returns the value of the POWERTOOLS_TRACER_CAPTURE_ERROR environment variable. + * + * @returns {string} + */ public getTracingCaptureError(): string { return this.get(this.tracerCaptureErrorVariable); } + /** + * It returns the value of the POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable. + * + * @returns {string} + */ public getTracingCaptureResponse(): string { return this.get(this.tracerCaptureResponseVariable); } + /** + * It returns the value of the POWERTOOLS_TRACE_ENABLED environment variable. + * + * @returns {string} + */ public getTracingEnabled(): string { return this.get(this.tracingEnabledVariable); } diff --git a/packages/tracer/src/config/index.ts b/packages/tracer/src/config/index.ts deleted file mode 100644 index 11fd37677e..0000000000 --- a/packages/tracer/src/config/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ConfigServiceInterface'; -export * from './EnvironmentVariablesService'; diff --git a/packages/tracer/src/helpers.ts b/packages/tracer/src/helpers.ts deleted file mode 100644 index 48fc9eda68..0000000000 --- a/packages/tracer/src/helpers.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Tracer } from '.'; -import type { TracerOptions } from './types'; - -/** - * Create a new tracer instance with the given options. - * - * @deprecated - This function will be removed in the next major release. Use the Tracer class directly instead. - */ -const createTracer = (options: TracerOptions = {}): Tracer => - new Tracer(options); - -export { createTracer }; diff --git a/packages/tracer/src/index.ts b/packages/tracer/src/index.ts index 1303ff1fb2..56c5e72163 100644 --- a/packages/tracer/src/index.ts +++ b/packages/tracer/src/index.ts @@ -1,4 +1 @@ -export * from './helpers'; -export * from './Tracer'; -export * from './TracerInterface'; -export * from './middleware/middy'; +export { Tracer } from './Tracer.js'; diff --git a/packages/tracer/src/middleware/middy.ts b/packages/tracer/src/middleware/middy.ts index 547561500f..16fd45676c 100644 --- a/packages/tracer/src/middleware/middy.ts +++ b/packages/tracer/src/middleware/middy.ts @@ -1,11 +1,11 @@ -import { TRACER_KEY } from '@aws-lambda-powertools/commons/lib/middleware'; -import type { Tracer } from '../Tracer'; +import { TRACER_KEY } from '@aws-lambda-powertools/commons'; +import type { Tracer } from '../Tracer.js'; import type { Segment, Subsegment } from 'aws-xray-sdk-core'; -import type { CaptureLambdaHandlerOptions } from '../types'; +import type { CaptureLambdaHandlerOptions } from '../types/Tracer.js'; import type { MiddlewareLikeObj, MiddyLikeRequest, -} from '@aws-lambda-powertools/commons'; +} from '@aws-lambda-powertools/commons/types'; /** * A middy middleware automating capture of metadata and annotations on segments or subsegments for a Lambda Handler. @@ -18,7 +18,8 @@ import type { * * @example * ```typescript - * import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer'; + * import { Tracer } from '@aws-lambda-powertools/tracer'; + * import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware'; * import middy from '@middy/core'; * * const tracer = new Tracer({ serviceName: 'serverlessAirline' }); diff --git a/packages/tracer/src/provider/ProviderService.ts b/packages/tracer/src/provider/ProviderService.ts index 271a820310..40130067b3 100644 --- a/packages/tracer/src/provider/ProviderService.ts +++ b/packages/tracer/src/provider/ProviderService.ts @@ -1,6 +1,8 @@ -import { ContextMissingStrategy } from 'aws-xray-sdk-core/dist/lib/context_utils'; import { Namespace } from 'cls-hooked'; -import { ProviderServiceInterface } from '.'; +import type { + ProviderServiceInterface, + ContextMissingStrategy, +} from '../types/ProviderServiceInterface.js'; import { captureAWS, captureAWSClient, @@ -107,8 +109,8 @@ class ProviderService implements ProviderServiceInterface { segment.addMetadata(key, value, namespace); } - public setContextMissingStrategy(strategy: unknown): void { - setContextMissingStrategy(strategy as ContextMissingStrategy); + public setContextMissingStrategy(strategy: ContextMissingStrategy): void { + setContextMissingStrategy(strategy); } public setDaemonAddress(address: string): void { diff --git a/packages/tracer/src/provider/index.ts b/packages/tracer/src/provider/index.ts deleted file mode 100644 index ef648fd32d..0000000000 --- a/packages/tracer/src/provider/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ProviderService'; -export * from './ProviderServiceInterface'; diff --git a/packages/tracer/src/types/ConfigServiceInterface.ts b/packages/tracer/src/types/ConfigServiceInterface.ts new file mode 100644 index 0000000000..8ac7b0bcd8 --- /dev/null +++ b/packages/tracer/src/types/ConfigServiceInterface.ts @@ -0,0 +1,54 @@ +import type { ConfigServiceInterface as ConfigServiceBaseInterface } from '@aws-lambda-powertools/commons/types'; + +/** + * Interface ConfigServiceInterface + * + * @interface + * @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime + * @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables + */ +interface ConfigServiceInterface extends ConfigServiceBaseInterface { + /** + * It returns the value of the AWS_EXECUTION_ENV environment variable. + * + * @returns {string} + */ + getAwsExecutionEnv(): string; + + /** + * It returns the value of the POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable. + * + * @returns {string} + */ + getCaptureHTTPsRequests(): string; + + /** + * It returns the value of the AWS_SAM_LOCAL environment variable. + * + * @returns {string} + */ + getSamLocal(): string; + + /** + * It returns the value of the POWERTOOLS_TRACE_ENABLED environment variable. + * + * @returns {string} + */ + getTracingEnabled(): string; + + /** + * It returns the value of the POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable. + * + * @returns {string} + */ + getTracingCaptureResponse(): string; + + /** + * It returns the value of the POWERTOOLS_TRACER_CAPTURE_ERROR environment variable. + * + * @returns {string} + */ + getTracingCaptureError(): string; +} + +export type { ConfigServiceInterface }; diff --git a/packages/tracer/src/provider/ProviderServiceInterface.ts b/packages/tracer/src/types/ProviderServiceInterface.ts similarity index 69% rename from packages/tracer/src/provider/ProviderServiceInterface.ts rename to packages/tracer/src/types/ProviderServiceInterface.ts index a8cd4a4f91..5a704fc705 100644 --- a/packages/tracer/src/provider/ProviderServiceInterface.ts +++ b/packages/tracer/src/types/ProviderServiceInterface.ts @@ -1,5 +1,11 @@ -import { Namespace } from 'cls-hooked'; -import { Segment, Subsegment } from 'aws-xray-sdk-core'; +import type { Namespace } from 'cls-hooked'; +import type { Segment, Subsegment } from 'aws-xray-sdk-core'; + +type ContextMissingStrategy = + | 'LOG_ERROR' + | 'RUNTIME_ERROR' + | 'IGNORE_ERROR' + | ((msg: string) => void); interface ProviderServiceInterface { getNamespace(): Namespace; @@ -12,7 +18,7 @@ interface ProviderServiceInterface { setDaemonAddress(address: string): void; - setContextMissingStrategy(strategy: unknown): void; + setContextMissingStrategy(strategy: ContextMissingStrategy): void; captureAWS(awsservice: T): T; @@ -39,4 +45,4 @@ interface ProviderServiceInterface { putMetadata(key: string, value: unknown, namespace?: string): void; } -export { ProviderServiceInterface }; +export type { ProviderServiceInterface, ContextMissingStrategy }; diff --git a/packages/tracer/src/types/Tracer.ts b/packages/tracer/src/types/Tracer.ts index e31c06c4e7..ac2e399707 100644 --- a/packages/tracer/src/types/Tracer.ts +++ b/packages/tracer/src/types/Tracer.ts @@ -1,10 +1,6 @@ -import type { ConfigServiceInterface } from '../config'; -import type { Handler } from 'aws-lambda'; -import type { - AsyncHandler, - LambdaInterface, - SyncHandler, -} from '@aws-lambda-powertools/commons'; +import type { ConfigServiceInterface } from './ConfigServiceInterface.js'; +import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types'; +import type { Segment, Subsegment } from 'aws-xray-sdk-core'; /** * Options for the tracer class to be used during initialization. @@ -100,28 +96,51 @@ type CaptureMethodOptions = { captureResponse?: boolean; }; -type HandlerMethodDecorator = ( - target: LambdaInterface, +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyClassMethod = (...args: any[]) => any; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyClass = new (...args: any[]) => any; + +type MethodDecorator = ( + target: InstanceType, propertyKey: string | symbol, - descriptor: - | TypedPropertyDescriptor> - | TypedPropertyDescriptor> + descriptor: TypedPropertyDescriptor ) => void; -// TODO: Revisit type below & make it more specific -type MethodDecorator = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - target: any, - propertyKey: string | symbol, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - descriptor: TypedPropertyDescriptor - // eslint-disable-next-line @typescript-eslint/no-explicit-any -) => any; +interface TracerInterface { + addErrorAsMetadata(error: Error, remote?: boolean): void; + addResponseAsMetadata(data?: unknown, methodName?: string): void; + addServiceNameAnnotation(): void; + annotateColdStart(): void; + captureAWS(aws: T): void | T; + captureAWSv3Client(service: T): void | T; + captureAWSClient(service: T): void | T; + captureLambdaHandler( + options?: CaptureLambdaHandlerOptions + ): HandlerMethodDecorator; + captureMethod( + options?: CaptureMethodOptions + ): MethodDecorator; + getSegment(): Segment | Subsegment | undefined; + getRootXrayTraceId(): string | undefined; + isTraceSampled(): boolean; + isTracingEnabled(): boolean; + putAnnotation: (key: string, value: string | number | boolean) => void; + putMetadata: ( + key: string, + value: unknown, + namespace?: string | undefined + ) => void; + setSegment(segment: Segment | Subsegment): void; +} -export { +export type { TracerOptions, CaptureLambdaHandlerOptions, CaptureMethodOptions, HandlerMethodDecorator, + AnyClass, + AnyClassMethod, MethodDecorator, + TracerInterface, }; diff --git a/packages/tracer/src/types/index.ts b/packages/tracer/src/types/index.ts index 8f8f55c173..62fd8b99b2 100644 --- a/packages/tracer/src/types/index.ts +++ b/packages/tracer/src/types/index.ts @@ -1 +1,10 @@ -export * from './Tracer'; +export type { + TracerOptions, + CaptureLambdaHandlerOptions, + CaptureMethodOptions, + HandlerMethodDecorator, + AnyClass, + AnyClassMethod, + MethodDecorator, + TracerInterface, +} from './Tracer.js'; diff --git a/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts b/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts index 90480bc3cd..ca2f6ace93 100644 --- a/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts +++ b/packages/tracer/tests/e2e/allFeatures.decorator.test.functionCode.ts @@ -1,4 +1,4 @@ -import { Tracer } from '../../src'; +import { Tracer } from '../../src/Tracer.js'; import type { Callback, Context } from 'aws-lambda'; import AWS from 'aws-sdk'; import axios from 'axios'; @@ -79,8 +79,6 @@ export class MyFunctionBase { class MyFunctionWithDecorator extends MyFunctionBase { @tracer.captureLambdaHandler() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore public handler( event: CustomEvent, _context: Context, @@ -90,8 +88,6 @@ class MyFunctionWithDecorator extends MyFunctionBase { } @tracer.captureMethod() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore public myMethod(): string { return super.myMethod(); } @@ -102,8 +98,6 @@ export const handler = handlerClass.handler.bind(handlerClass); class MyFunctionWithDecoratorCaptureResponseFalse extends MyFunctionBase { @tracer.captureLambdaHandler({ captureResponse: false }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore public handler( event: CustomEvent, _context: Context, @@ -113,8 +107,6 @@ class MyFunctionWithDecoratorCaptureResponseFalse extends MyFunctionBase { } @tracer.captureMethod({ captureResponse: false }) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore public myMethod(): string { return super.myMethod(); } diff --git a/packages/tracer/tests/e2e/allFeatures.decorator.test.ts b/packages/tracer/tests/e2e/allFeatures.decorator.test.ts index 2ec65d83b3..0dfeb15b79 100644 --- a/packages/tracer/tests/e2e/allFeatures.decorator.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.decorator.test.ts @@ -3,30 +3,28 @@ * * @group e2e/tracer/decorator */ -import { - TestStack, - TestDynamodbTable, -} from '@aws-lambda-powertools/testing-utils'; +import { TestStack } from '@aws-lambda-powertools/testing-utils'; +import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; import { join } from 'node:path'; -import { TracerTestNodejsFunction } from '../helpers/resources'; +import { TracerTestNodejsFunction } from '../helpers/resources.js'; import { assertAnnotation, assertErrorAndFault, -} from '../helpers/traceAssertions'; +} from '../helpers/traceAssertions.js'; import { getFirstSubsegment, getInvocationSubsegment, getTraces, invokeAllTestCases, splitSegmentsByName, -} from '../helpers/tracesUtils'; +} from '../helpers/tracesUtils.js'; import { commonEnvironmentVars, RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; /** * The test includes one stack with 4 Lambda functions that correspond to the following test cases: diff --git a/packages/tracer/tests/e2e/allFeatures.manual.test.functionCode.ts b/packages/tracer/tests/e2e/allFeatures.manual.test.functionCode.ts index f7bd2cda64..95cdc3fd33 100644 --- a/packages/tracer/tests/e2e/allFeatures.manual.test.functionCode.ts +++ b/packages/tracer/tests/e2e/allFeatures.manual.test.functionCode.ts @@ -1,4 +1,4 @@ -import { Tracer } from '../../src'; +import { Tracer } from '../../src/index.js'; import type { Context } from 'aws-lambda'; import axios from 'axios'; import AWS from 'aws-sdk'; diff --git a/packages/tracer/tests/e2e/allFeatures.manual.test.ts b/packages/tracer/tests/e2e/allFeatures.manual.test.ts index 3374205e67..280a1c3efd 100644 --- a/packages/tracer/tests/e2e/allFeatures.manual.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.manual.test.ts @@ -3,31 +3,29 @@ * * @group e2e/tracer/manual */ -import { - TestDynamodbTable, - TestStack, -} from '@aws-lambda-powertools/testing-utils'; -import { join } from 'path'; -import { TracerTestNodejsFunction } from '../helpers/resources'; +import { TestStack } from '@aws-lambda-powertools/testing-utils'; +import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; +import { join } from 'node:path'; +import { TracerTestNodejsFunction } from '../helpers/resources.js'; import { assertAnnotation, assertErrorAndFault, -} from '../helpers/traceAssertions'; +} from '../helpers/traceAssertions.js'; import { getFirstSubsegment, getInvocationSubsegment, getTraces, invokeAllTestCases, splitSegmentsByName, -} from '../helpers/tracesUtils'; -import type { ParsedTrace } from '../helpers/traceUtils.types'; + type ParsedTrace, +} from '../helpers/tracesUtils.js'; import { commonEnvironmentVars, RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; describe(`Tracer E2E tests, all features with manual instantiation`, () => { const testStack = new TestStack({ diff --git a/packages/tracer/tests/e2e/allFeatures.middy.test.functionCode.ts b/packages/tracer/tests/e2e/allFeatures.middy.test.functionCode.ts index 2dd55050da..f55af0bdd4 100644 --- a/packages/tracer/tests/e2e/allFeatures.middy.test.functionCode.ts +++ b/packages/tracer/tests/e2e/allFeatures.middy.test.functionCode.ts @@ -1,5 +1,6 @@ import middy from '@middy/core'; -import { captureLambdaHandler, Tracer } from '../../src'; +import { Tracer } from '../../src/index.js'; +import { captureLambdaHandler } from '../../src/middleware/middy.js'; import type { Context } from 'aws-lambda'; import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'; import axios from 'axios'; diff --git a/packages/tracer/tests/e2e/allFeatures.middy.test.ts b/packages/tracer/tests/e2e/allFeatures.middy.test.ts index 5f8f965d8c..23d2fdfc23 100644 --- a/packages/tracer/tests/e2e/allFeatures.middy.test.ts +++ b/packages/tracer/tests/e2e/allFeatures.middy.test.ts @@ -3,30 +3,28 @@ * * @group e2e/tracer/middy */ -import { - TestStack, - TestDynamodbTable, -} from '@aws-lambda-powertools/testing-utils'; +import { TestStack } from '@aws-lambda-powertools/testing-utils'; +import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; import { join } from 'node:path'; -import { TracerTestNodejsFunction } from '../helpers/resources'; +import { TracerTestNodejsFunction } from '../helpers/resources.js'; import { assertAnnotation, assertErrorAndFault, -} from '../helpers/traceAssertions'; +} from '../helpers/traceAssertions.js'; import { getFirstSubsegment, getInvocationSubsegment, getTraces, invokeAllTestCases, splitSegmentsByName, -} from '../helpers/tracesUtils'; +} from '../helpers/tracesUtils.js'; import { commonEnvironmentVars, RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; /** * The test includes one stack with 4 Lambda functions that correspond to the following test cases: diff --git a/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts b/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts index 98ee23cbf1..6d60a7b53e 100644 --- a/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts +++ b/packages/tracer/tests/e2e/asyncHandler.decorator.test.functionCode.ts @@ -1,4 +1,4 @@ -import { Tracer } from '../../src'; +import { Tracer } from '../../src/index.js'; import type { Context } from 'aws-lambda'; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb'; diff --git a/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts b/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts index f97f9d2f77..557031143a 100644 --- a/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts +++ b/packages/tracer/tests/e2e/asyncHandler.decorator.test.ts @@ -3,30 +3,28 @@ * * @group e2e/tracer/decorator-async-handler */ -import { - TestStack, - TestDynamodbTable, -} from '@aws-lambda-powertools/testing-utils'; +import { TestStack } from '@aws-lambda-powertools/testing-utils'; +import { TestDynamodbTable } from '@aws-lambda-powertools/testing-utils/resources/dynamodb'; import { join } from 'node:path'; -import { TracerTestNodejsFunction } from '../helpers/resources'; +import { TracerTestNodejsFunction } from '../helpers/resources.js'; import { assertAnnotation, assertErrorAndFault, -} from '../helpers/traceAssertions'; +} from '../helpers/traceAssertions.js'; import { getFirstSubsegment, getInvocationSubsegment, getTraces, invokeAllTestCases, splitSegmentsByName, -} from '../helpers/tracesUtils'; +} from '../helpers/tracesUtils.js'; import { commonEnvironmentVars, RESOURCE_NAME_PREFIX, SETUP_TIMEOUT, TEARDOWN_TIMEOUT, TEST_CASE_TIMEOUT, -} from './constants'; +} from './constants.js'; describe(`Tracer E2E tests, async handler with decorator instantiation`, () => { const testStack = new TestStack({ diff --git a/packages/tracer/tests/helpers/resources.ts b/packages/tracer/tests/helpers/resources.ts index 6f46cb98d6..edd4faa769 100644 --- a/packages/tracer/tests/helpers/resources.ts +++ b/packages/tracer/tests/helpers/resources.ts @@ -1,10 +1,10 @@ +import type { TestStack } from '@aws-lambda-powertools/testing-utils'; import type { ExtraTestProps, TestNodejsFunctionProps, - TestStack, -} from '@aws-lambda-powertools/testing-utils'; -import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils'; -import { commonEnvironmentVars } from '../e2e/constants'; +} from '@aws-lambda-powertools/testing-utils/types'; +import { TestNodejsFunction } from '@aws-lambda-powertools/testing-utils/resources/lambda'; +import { commonEnvironmentVars } from '../e2e/constants.js'; class TracerTestNodejsFunction extends TestNodejsFunction { public constructor( diff --git a/packages/tracer/tests/helpers/traceAssertions.ts b/packages/tracer/tests/helpers/traceAssertions.ts index 8ad7b31ee8..559be2dfa1 100644 --- a/packages/tracer/tests/helpers/traceAssertions.ts +++ b/packages/tracer/tests/helpers/traceAssertions.ts @@ -1,8 +1,8 @@ -import { getFirstSubsegment } from './tracesUtils'; -import type { - AssertAnnotationParams, - ParsedDocument, -} from './traceUtils.types'; +import { + getFirstSubsegment, + type AssertAnnotationParams, + type ParsedDocument, +} from './tracesUtils.js'; export const assertAnnotation = (params: AssertAnnotationParams): void => { const { diff --git a/packages/tracer/tests/helpers/traceUtils.types.ts b/packages/tracer/tests/helpers/traceUtils.types.ts deleted file mode 100644 index fc2c8cac05..0000000000 --- a/packages/tracer/tests/helpers/traceUtils.types.ts +++ /dev/null @@ -1,70 +0,0 @@ -interface ParsedDocument { - name: string; - id: string; - start_time: number; - end_time?: number; - // This flag may be set if the segment hasn't been fully processed - // The trace may have already appeared in the `getTraceSummaries` response - // but a segment may still be in_progress - in_progress?: boolean; - aws?: { - request_id: string; - }; - http?: { - response: { - status: number; - }; - }; - origin?: string; - resource_arn?: string; - trace_id?: string; - subsegments?: ParsedDocument[]; - annotations?: { - [key: string]: string | boolean | number; - }; - metadata?: { - [key: string]: { - [key: string]: unknown; - }; - }; - fault?: boolean; - cause?: { - working_directory: string; - exceptions: { - message: string; - type: string; - remote: boolean; - stack: { - path: string; - line: number; - label: string; - }[]; - }[]; - }; - exception: { - message: string; - }; - error?: boolean; -} - -interface ParsedSegment { - Document: ParsedDocument; - Id: string; -} - -interface ParsedTrace { - Duration: number; - Id: string; - LimitExceeded: boolean; - Segments: ParsedSegment[]; -} - -interface AssertAnnotationParams { - annotations: ParsedDocument['annotations']; - isColdStart: boolean; - expectedServiceName: string; - expectedCustomAnnotationKey: string; - expectedCustomAnnotationValue: string | number | boolean; -} - -export { ParsedDocument, ParsedSegment, ParsedTrace, AssertAnnotationParams }; diff --git a/packages/tracer/tests/helpers/tracesUtils.ts b/packages/tracer/tests/helpers/tracesUtils.ts index 7995474a3b..8557e60b9b 100644 --- a/packages/tracer/tests/helpers/tracesUtils.ts +++ b/packages/tracer/tests/helpers/tracesUtils.ts @@ -5,12 +5,76 @@ import { GetTraceSummariesCommand, } from '@aws-sdk/client-xray'; import { invokeFunction } from '@aws-lambda-powertools/testing-utils'; -import { FunctionSegmentNotDefinedError } from './FunctionSegmentNotDefinedError'; -import type { - ParsedDocument, - ParsedSegment, - ParsedTrace, -} from './traceUtils.types'; +import { FunctionSegmentNotDefinedError } from './FunctionSegmentNotDefinedError.js'; + +interface ParsedDocument { + name: string; + id: string; + start_time: number; + end_time?: number; + // This flag may be set if the segment hasn't been fully processed + // The trace may have already appeared in the `getTraceSummaries` response + // but a segment may still be in_progress + in_progress?: boolean; + aws?: { + request_id: string; + }; + http?: { + response: { + status: number; + }; + }; + origin?: string; + resource_arn?: string; + trace_id?: string; + subsegments?: ParsedDocument[]; + annotations?: { + [key: string]: string | boolean | number; + }; + metadata?: { + [key: string]: { + [key: string]: unknown; + }; + }; + fault?: boolean; + cause?: { + working_directory: string; + exceptions: { + message: string; + type: string; + remote: boolean; + stack: { + path: string; + line: number; + label: string; + }[]; + }[]; + }; + exception: { + message: string; + }; + error?: boolean; +} + +interface ParsedSegment { + Document: ParsedDocument; + Id: string; +} + +interface ParsedTrace { + Duration: number; + Id: string; + LimitExceeded: boolean; + Segments: ParsedSegment[]; +} + +interface AssertAnnotationParams { + annotations: ParsedDocument['annotations']; + isColdStart: boolean; + expectedServiceName: string; + expectedCustomAnnotationKey: string; + expectedCustomAnnotationValue: string | number | boolean; +} type GetTracesOptions = { startTime: Date; @@ -250,4 +314,8 @@ export { getInvocationSubsegment, splitSegmentsByName, invokeAllTestCases, + type ParsedDocument, + type ParsedSegment, + type ParsedTrace, + type AssertAnnotationParams, }; diff --git a/packages/tracer/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts similarity index 96% rename from packages/tracer/tests/unit/config/EnvironmentVariablesService.test.ts rename to packages/tracer/tests/unit/EnvironmentVariablesService.test.ts index 9e2adf9eb2..d20692b6dd 100644 --- a/packages/tracer/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/tracer/tests/unit/EnvironmentVariablesService.test.ts @@ -4,7 +4,7 @@ * @group unit/tracer/all */ -import { EnvironmentVariablesService } from '../../../src/config'; +import { EnvironmentVariablesService } from '../../src/config/EnvironmentVariablesService.js'; describe('Class: EnvironmentVariablesService', () => { const ENVIRONMENT_VARIABLES = process.env; diff --git a/packages/tracer/tests/unit/ProviderService.test.ts b/packages/tracer/tests/unit/ProviderService.test.ts index 1b74701b26..f7fe81ab62 100644 --- a/packages/tracer/tests/unit/ProviderService.test.ts +++ b/packages/tracer/tests/unit/ProviderService.test.ts @@ -3,8 +3,7 @@ * * @group unit/tracer/providerservice */ - -import { ProviderService } from '../../src/provider'; +import { ProviderService } from '../../src/provider/ProviderService.js'; import { captureAsyncFunc, captureAWS, @@ -21,8 +20,8 @@ import { setSegment, Subsegment, } from 'aws-xray-sdk-core'; -import http from 'http'; -import https from 'https'; +import http from 'node:http'; +import https from 'node:https'; import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { addUserAgentMiddleware } from '@aws-lambda-powertools/commons'; diff --git a/packages/tracer/tests/unit/Tracer.test.ts b/packages/tracer/tests/unit/Tracer.test.ts index 870bc6bd09..04747e1f9f 100644 --- a/packages/tracer/tests/unit/Tracer.test.ts +++ b/packages/tracer/tests/unit/Tracer.test.ts @@ -3,19 +3,17 @@ * * @group unit/tracer/all */ -import { - ContextExamples as dummyContext, - Events as dummyEvent, - LambdaInterface, -} from '@aws-lambda-powertools/commons'; -import { Tracer } from './../../src'; -import type { Callback, Context } from 'aws-lambda/handler'; +import context from '@aws-lambda-powertools/testing-utils/context'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; +import { Tracer } from './../../src/index.js'; +import type { Callback, Context } from 'aws-lambda'; import { Segment, setContextMissingStrategy, Subsegment, } from 'aws-xray-sdk-core'; -import { ProviderServiceInterface } from '../../src/provider'; +import type { ProviderServiceInterface } from '../../src/types/ProviderServiceInterface.js'; +import type { ConfigServiceInterface } from '../../src/types/ConfigServiceInterface.js'; type CaptureAsyncFuncMock = jest.SpyInstance< unknown, @@ -47,8 +45,10 @@ jest.spyOn(console, 'error').mockImplementation(() => null); describe('Class: Tracer', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = dummyContext.helloworldContext; - const event = dummyEvent.Custom.CustomEvent; + const event = { + foo: 'bar', + bar: 'baz', + }; beforeEach(() => { jest.clearAllMocks(); @@ -60,6 +60,321 @@ describe('Class: Tracer', () => { process.env = ENVIRONMENT_VARIABLES; }); + describe('Method: constructor', () => { + it('instantiates with default settings when no option is passed', () => { + // Prepare & Act + const tracer = new Tracer(undefined); + + // Assess + expect(tracer).toBeInstanceOf(Tracer); + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: true, + serviceName: 'hello-world', + captureHTTPsRequests: true, + }) + ); + }); + + it('uses the provided options when passed ', () => { + // Prepare + const tracerOptions = { + enabled: false, + serviceName: 'my-lambda-service', + captureHTTPsRequests: false, + }; + + // Act + const tracer = new Tracer(tracerOptions); + + // Assess + expect(tracer).toBeInstanceOf(Tracer); + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: false, + serviceName: 'my-lambda-service', + captureHTTPsRequests: false, + }) + ); + }); + + it('uses the default service name when an invalid one is passed', () => { + // Prepare + const tracerOptions = { + serviceName: '', + }; + + // Act + const tracer = new Tracer(tracerOptions); + + // Assess + expect(tracer).toBeInstanceOf(Tracer); + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: true, + serviceName: 'hello-world', + captureHTTPsRequests: true, + }) + ); + }); + + it('uses the custom config service when one is passed', () => { + // Prepare + const configService: ConfigServiceInterface = { + get(name: string): string { + return `a-string-from-${name}`; + }, + getCaptureHTTPsRequests(): string { + return 'false'; + }, + getTracingEnabled(): string { + return 'false'; + }, + getTracingCaptureResponse(): string { + return 'false'; + }, + getTracingCaptureError(): string { + return 'false'; + }, + getServiceName(): string { + return 'my-backend-service'; + }, + getSamLocal() { + return 'false'; + }, + getAwsExecutionEnv() { + return 'AWS_Lambda_nodejs12.x'; + }, + isDevMode(): boolean { + return false; + }, + isValueTrue(value: string): boolean { + return value === 'true'; + }, + getXrayTraceId() { + return '1-abcdef12-3456abcdef123456abcdef12'; + }, + }; + + // Act + const tracer = new Tracer({ + customConfigService: configService, + }); + + // Assess + expect(tracer).toBeInstanceOf(Tracer); + expect(tracer).toEqual( + expect.objectContaining({ + customConfigService: configService, + tracingEnabled: false, + serviceName: 'my-backend-service', + captureHTTPsRequests: false, + }) + ); + }); + + it('sets captureHTTPsGlobal to true by default when tracing is enabled', () => { + // Prepare + const tracerOptions = { + enabled: true, + }; + + // Act + const tracer = new Tracer(tracerOptions); + + // Assess + expect(tracer).toBeInstanceOf(Tracer); + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: true, + captureHTTPsRequests: true, + }) + ); + }); + }); + + describe('Environment Variables configs', () => { + test('when AWS_EXECUTION_ENV environment variable is equal to AWS_Lambda_amplify-mock, tracing is disabled', () => { + // Prepare + process.env.AWS_EXECUTION_ENV = 'AWS_Lambda_amplify-mock'; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: false, + }) + ); + }); + + test('when AWS_SAM_LOCAL environment variable is set, tracing is disabled', () => { + // Prepare + process.env.AWS_SAM_LOCAL = 'true'; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: false, + }) + ); + }); + + test('when AWS_EXECUTION_ENV environment variable is set, tracing is enabled', () => { + // Prepare + process.env.AWS_EXECUTION_ENV = 'nodejs16.x'; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: true, + }) + ); + }); + + test('when AWS_EXECUTION_ENV environment variable is NOT set, tracing is disabled', () => { + // Prepare + delete process.env.AWS_EXECUTION_ENV; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: false, + }) + ); + }); + + test('when POWERTOOLS_TRACE_ENABLED environment variable is set, a tracer with tracing disabled is returned', () => { + // Prepare + process.env.POWERTOOLS_TRACE_ENABLED = 'false'; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + tracingEnabled: false, + }) + ); + }); + + test('when POWERTOOLS_SERVICE_NAME environment variable is set, a tracer with the correct serviceName is returned', () => { + // Prepare + process.env.POWERTOOLS_SERVICE_NAME = 'my-backend-service'; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + serviceName: 'my-backend-service', + }) + ); + }); + + test('when POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable is set, a tracer with captureResponse disabled is returned', () => { + // Prepare + process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false'; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + captureResponse: false, + }) + ); + }); + + test('when POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable is set to invalid value, a tracer with captureResponse enabled is returned', () => { + // Prepare + process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = ''; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + captureResponse: true, + }) + ); + }); + + test('when POWERTOOLS_TRACER_CAPTURE_ERROR environment variable is set, a tracer with captureError disabled is returned', () => { + // Prepare + process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = 'false'; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + captureError: false, + }) + ); + }); + + test('when POWERTOOLS_TRACER_CAPTURE_ERROR environment variable is set to invalid value, a tracer with captureError enabled is returned', () => { + // Prepare + process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = ''; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + captureError: true, + }) + ); + }); + + test('when POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable is set, captureHTTPsGlobal is called', () => { + // Prepare + process.env.POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS = 'false'; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + captureHTTPsRequests: false, + }) + ); + }); + + test('when POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable is set to invalid value, captureHTTPsGlobal is called', () => { + // Prepare + process.env.POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS = ''; + + // Act + const tracer = new Tracer(); + + // Assess + expect(tracer).toEqual( + expect.objectContaining({ + captureHTTPsRequests: true, + }) + ); + }); + }); + describe('Method: annotateColdStart', () => { test('when called while tracing is disabled, it does nothing', () => { // Prepare diff --git a/packages/tracer/tests/unit/helpers.test.ts b/packages/tracer/tests/unit/helpers.test.ts deleted file mode 100644 index cb2394a875..0000000000 --- a/packages/tracer/tests/unit/helpers.test.ts +++ /dev/null @@ -1,386 +0,0 @@ -/** - * Test Tracer helpers - * - * @group unit/tracer/all - */ - -import { ConfigServiceInterface } from '../../src/config'; -import { TracerOptions } from '../../src/types'; -import { createTracer, Tracer } from './../../src'; - -describe('Helper: createTracer function', () => { - const ENVIRONMENT_VARIABLES = process.env; - - beforeEach(() => { - jest.resetModules(); - process.env = { ...ENVIRONMENT_VARIABLES }; - }); - - afterAll(() => { - process.env = ENVIRONMENT_VARIABLES; - }); - - describe('TracerOptions parameters', () => { - test('when no tracer options are passed, returns a Tracer instance with the correct proprieties', () => { - // Prepare - const tracerOptions = undefined; - - // Act - const tracer = createTracer(tracerOptions); - - // Assess - expect(tracer).toBeInstanceOf(Tracer); - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: true, - serviceName: 'hello-world', - captureHTTPsRequests: true, - }) - ); - }); - - test('when all tracer options are passed, returns a Tracer instance with the correct properties', () => { - // Prepare - const tracerOptions = { - enabled: false, - serviceName: 'my-lambda-service', - captureHTTPsRequests: false, - }; - - // Act - const tracer = createTracer(tracerOptions); - - // Assess - expect(tracer).toBeInstanceOf(Tracer); - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: false, - serviceName: 'my-lambda-service', - captureHTTPsRequests: false, - }) - ); - }); - - test('when a custom serviceName is passed, returns a Tracer instance with the correct properties', () => { - // Prepare - const tracerOptions = { - serviceName: 'my-lambda-service', - }; - - // Act - const tracer = createTracer(tracerOptions); - - // Assess - expect(tracer).toBeInstanceOf(Tracer); - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: true, - serviceName: 'my-lambda-service', - captureHTTPsRequests: true, - }) - ); - }); - - test('when a custom, but invalid, serviceName is passed, returns a Tracer instance with the correct properties', () => { - // Prepare - const tracerOptions = { - serviceName: '', - }; - - // Act - const tracer = createTracer(tracerOptions); - - // Assess - expect(tracer).toBeInstanceOf(Tracer); - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: true, - serviceName: 'hello-world', - captureHTTPsRequests: true, - }) - ); - }); - - test('when (tracing) disabled is passed, returns a Tracer instance with the correct properties', () => { - // Prepare - const tracerOptions = { - enabled: false, - }; - - // Act - const tracer = createTracer(tracerOptions); - - // Assess - expect(tracer).toBeInstanceOf(Tracer); - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: false, - serviceName: 'hello-world', - captureHTTPsRequests: true, - }) - ); - }); - - test('when a custom customConfigService is passed, returns a Logger instance with the correct proprieties', () => { - const configService: ConfigServiceInterface = { - get(name: string): string { - return `a-string-from-${name}`; - }, - getCaptureHTTPsRequests(): string { - return 'false'; - }, - getTracingEnabled(): string { - return 'false'; - }, - getTracingCaptureResponse(): string { - return 'false'; - }, - getTracingCaptureError(): string { - return 'false'; - }, - getServiceName(): string { - return 'my-backend-service'; - }, - }; - // Prepare - const tracerOptions: TracerOptions = { - customConfigService: configService, - }; - - // Act - const tracer = createTracer(tracerOptions); - - // Assess - expect(tracer).toBeInstanceOf(Tracer); - expect(tracer).toEqual( - expect.objectContaining({ - customConfigService: configService, - tracingEnabled: false, - serviceName: 'my-backend-service', - captureHTTPsRequests: false, - }) - ); - }); - - test('when tracing is enabled and patching of http requests is opted-out, captureHTTPsRequests is false', () => { - // Prepare - const tracerOptions = { - enabled: true, - captureHTTPsRequests: false, - }; - - // Act - const tracer = createTracer(tracerOptions); - - // Assess - expect(tracer).toBeInstanceOf(Tracer); - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: true, - captureHTTPsRequests: false, - }) - ); - }); - - test('when tracing is enabled captureHTTPsGlobal is true', () => { - // Prepare - const tracerOptions = { - enabled: true, - }; - - // Act - const tracer = createTracer(tracerOptions); - - // Assess - expect(tracer).toBeInstanceOf(Tracer); - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: true, - captureHTTPsRequests: true, - }) - ); - }); - }); - - describe('Environment Variables configs', () => { - test('when AWS_EXECUTION_ENV environment variable is equal to AWS_Lambda_amplify-mock, tracing is disabled', () => { - // Prepare - process.env.AWS_EXECUTION_ENV = 'AWS_Lambda_amplify-mock'; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: false, - }) - ); - }); - - test('when AWS_SAM_LOCAL environment variable is set, tracing is disabled', () => { - // Prepare - process.env.AWS_SAM_LOCAL = 'true'; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: false, - }) - ); - }); - - test('when AWS_EXECUTION_ENV environment variable is set, tracing is enabled', () => { - // Prepare - process.env.AWS_EXECUTION_ENV = 'nodejs16.x'; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: true, - }) - ); - }); - - test('when AWS_EXECUTION_ENV environment variable is NOT set, tracing is disabled', () => { - // Prepare - delete process.env.AWS_EXECUTION_ENV; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: false, - }) - ); - }); - - test('when POWERTOOLS_TRACE_ENABLED environment variable is set, a tracer with tracing disabled is returned', () => { - // Prepare - process.env.POWERTOOLS_TRACE_ENABLED = 'false'; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - tracingEnabled: false, - }) - ); - }); - - test('when POWERTOOLS_SERVICE_NAME environment variable is set, a tracer with the correct serviceName is returned', () => { - // Prepare - process.env.POWERTOOLS_SERVICE_NAME = 'my-backend-service'; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - serviceName: 'my-backend-service', - }) - ); - }); - - test('when POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable is set, a tracer with captureResponse disabled is returned', () => { - // Prepare - process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = 'false'; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - captureResponse: false, - }) - ); - }); - - test('when POWERTOOLS_TRACER_CAPTURE_RESPONSE environment variable is set to invalid value, a tracer with captureResponse enabled is returned', () => { - // Prepare - process.env.POWERTOOLS_TRACER_CAPTURE_RESPONSE = ''; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - captureResponse: true, - }) - ); - }); - - test('when POWERTOOLS_TRACER_CAPTURE_ERROR environment variable is set, a tracer with captureError disabled is returned', () => { - // Prepare - process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = 'false'; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - captureError: false, - }) - ); - }); - - test('when POWERTOOLS_TRACER_CAPTURE_ERROR environment variable is set to invalid value, a tracer with captureError enabled is returned', () => { - // Prepare - process.env.POWERTOOLS_TRACER_CAPTURE_ERROR = ''; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - captureError: true, - }) - ); - }); - - test('when POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable is set, captureHTTPsGlobal is called', () => { - // Prepare - process.env.POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS = 'false'; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - captureHTTPsRequests: false, - }) - ); - }); - - test('when POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS environment variable is set to invalid value, captureHTTPsGlobal is called', () => { - // Prepare - process.env.POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS = ''; - - // Act - const tracer = createTracer(); - - // Assess - expect(tracer).toEqual( - expect.objectContaining({ - captureHTTPsRequests: true, - }) - ); - }); - }); -}); diff --git a/packages/tracer/tests/unit/middy.test.ts b/packages/tracer/tests/unit/middy.test.ts index c470b1d25a..5819e54faa 100644 --- a/packages/tracer/tests/unit/middy.test.ts +++ b/packages/tracer/tests/unit/middy.test.ts @@ -3,16 +3,17 @@ * * @group unit/tracer/all */ -import { captureLambdaHandler } from '../../src/middleware/middy'; +import { captureLambdaHandler } from '../../src/middleware/middy.js'; import middy from '@middy/core'; -import { Tracer } from './../../src'; -import type { Context, Handler } from 'aws-lambda/handler'; +import { Tracer } from './../../src/index.js'; +import type { Context, Handler } from 'aws-lambda'; import { Segment, setContextMissingStrategy, Subsegment, } from 'aws-xray-sdk-core'; -import { cleanupMiddlewares } from '@aws-lambda-powertools/commons/lib/middleware'; +import { cleanupMiddlewares } from '@aws-lambda-powertools/commons'; +import context from '@aws-lambda-powertools/testing-utils/context'; jest.spyOn(console, 'debug').mockImplementation(() => null); jest.spyOn(console, 'warn').mockImplementation(() => null); @@ -20,21 +21,6 @@ jest.spyOn(console, 'error').mockImplementation(() => null); describe('Middy middleware', () => { const ENVIRONMENT_VARIABLES = process.env; - const context = { - callbackWaitsForEmptyEventLoop: true, - functionVersion: '$LATEST', - functionName: 'foo-bar-function', - memoryLimitInMB: '128', - logGroupName: '/aws/lambda/foo-bar-function-123456abcdef', - logStreamName: '2021/03/09/[$LATEST]abcdef123456abcdef123456abcdef123456', - invokedFunctionArn: - 'arn:aws:lambda:eu-west-1:123456789012:function:Example', - awsRequestId: 'c6af9ac6-7b61-11e6-9a41-93e8deadbeef', - getRemainingTimeInMillis: () => 1234, - done: () => console.log('Done!'), - fail: () => console.log('Failed!'), - succeed: () => console.log('Succeeded!'), - }; beforeEach(() => { jest.clearAllMocks(); diff --git a/packages/tracer/tsconfig.esm.json b/packages/tracer/tsconfig.esm.json new file mode 100644 index 0000000000..123291b0cf --- /dev/null +++ b/packages/tracer/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./lib/esm", + "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/esm.json" + }, + "include": [ + "./src/**/*" + ] +} \ No newline at end of file diff --git a/packages/tracer/tsconfig.json b/packages/tracer/tsconfig.json index 1cb9d72773..f216927295 100644 --- a/packages/tracer/tsconfig.json +++ b/packages/tracer/tsconfig.json @@ -1,8 +1,9 @@ { "extends": "../../tsconfig.json", "compilerOptions": { - "outDir": "./lib", + "outDir": "./lib/cjs", "rootDir": "./src", + "tsBuildInfoFile": ".tsbuildinfo/cjs.json" }, "include": [ "./src/**/*" diff --git a/tsconfig.esm.json b/tsconfig.esm.json new file mode 100644 index 0000000000..4c11bfc0ab --- /dev/null +++ b/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", // TODO: experiment with bundler & esnext + "noEmitHelpers": true /* Disable generating custom helper functions like '__extends' in compiled output. */ + } +} diff --git a/tsconfig.json b/tsconfig.json index a49b03e30e..06b00be287 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,17 +5,12 @@ "target": "ES2021", // Node.js 16 "experimentalDecorators": true, "module": "commonjs", - "moduleResolution": "node", // TODO: experiment with bundler & esnext + "moduleResolution": "node", "baseUrl": ".", // "traceResolution": true, // Enable this to debug module resolution issues "declaration": true, "removeComments": false, - // TODO: experiment with this & move to tslib in v2 - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // TODO: see if this can introduced in v2 (requires breaking changes in exports) - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + "isolatedModules": true, "declarationMap": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true,