From 8568cde90f96f3f63fb0ab34b8b0548863da4acb Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 29 Apr 2024 17:40:26 +0200 Subject: [PATCH 1/2] docs(commons): refresh API docs & README --- packages/commons/README.md | 145 ++++++++++-------- packages/commons/src/Utility.ts | 52 +++++-- packages/commons/src/awsSdkUtils.ts | 33 +++- .../src/config/EnvironmentVariablesService.ts | 62 ++++---- .../src/middleware/cleanupMiddlewares.ts | 10 +- packages/commons/src/middleware/constants.ts | 20 +++ packages/commons/src/typeUtils.ts | 137 ++++++++++++++++- .../src/types/ConfigServiceInterface.ts | 30 ++-- packages/commons/src/types/LambdaInterface.ts | 29 ++++ packages/commons/src/types/awsSdk.ts | 6 +- packages/commons/src/types/index.ts | 2 + packages/commons/src/types/json.ts | 12 ++ packages/commons/src/types/middy.ts | 37 ++++- packages/commons/typedoc.json | 9 +- 14 files changed, 439 insertions(+), 145 deletions(-) diff --git a/packages/commons/README.md b/packages/commons/README.md index c60e624068..d563bf5a18 100644 --- a/packages/commons/README.md +++ b/packages/commons/README.md @@ -4,73 +4,96 @@ Powertools for AWS Lambda (TypeScript) is a developer toolkit to implement Serve You can use the library in both TypeScript and JavaScript code bases. -> Also available in [Python](https://github.com/aws-powertools/powertools-lambda-python), [Java](https://github.com/aws-powertools/powertools-lambda-java), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet). +## Intro -**[Documentation](https://docs.powertools.aws.dev/lambda/typescript/)** | **[npm](https://www.npmjs.com/org/aws-lambda-powertools)** | **[Roadmap](https://docs.powertools.aws.dev/lambda/typescript/latest/roadmap)** | **[Examples](https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/examples)** +The Commons package contains a set of utilities that are shared across one or more Powertools for AWS Lambda (TypeScript) utilities. Some of these utilities can also be used independently in your AWS Lambda functions. -## Table of contents +## Usage -- [Table of contents ](#table-of-contents-) -- [Features](#features) -- [Getting started](#getting-started) - - [Installation](#installation) - - [Examples](#examples) - - [Demo applications](#demo-applications) -- [Contribute](#contribute) -- [Roadmap](#roadmap) -- [Connect](#connect) -- [How to support Powertools for AWS Lambda (TypeScript)?](#how-to-support-powertools-for-aws-lambda-typescript) - - [Becoming a reference customer](#becoming-a-reference-customer) - - [Sharing your work](#sharing-your-work) - - [Using Lambda Layer](#using-lambda-layer) -- [Credits](#credits) -- [License](#license) +To get started, install the utility by running: -## Features - -* **[Tracer](https://docs.powertools.aws.dev/lambda/typescript/latest/core/tracer/)** - Utilities to trace Lambda function handlers, and both synchronous and asynchronous functions -* **[Logger](https://docs.powertools.aws.dev/lambda/typescript/latest/core/logger/)** - Structured logging made easier, and a middleware to enrich log items with key details of the Lambda context -* **[Metrics](https://docs.powertools.aws.dev/lambda/typescript/latest/core/metrics/)** - Custom Metrics created asynchronously via CloudWatch Embedded Metric Format (EMF) -* **[Parameters](https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/parameters/)** - High-level functions to retrieve one or more parameters from AWS SSM, Secrets Manager, AppConfig, and DynamoDB -* **[Idempotency](https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/idempotency/)** - Class method decorator, Middy middleware, and function wrapper to make your Lambda functions idempotent and prevent duplicate execution based on payload content -* **[Batch processing](https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/batch/)** - Utility to handle partial failures when processing batches from Amazon SQS, Amazon Kinesis Data Streams, and Amazon DynamoDB Streams. +```sh +npm i @aws-lambda-powertools/commons +``` -## Getting started +### Type utils -Find the complete project's [documentation here](https://docs.powertools.aws.dev/lambda/typescript). +When working with different objects and values, you may want to do runtime type checks. The utility comes with a set of type utilities that you can use to check the type of an object or value. -### Installation +```typescript +import { isRecord } from '@aws-lambda-powertools/commons/typeUtils'; +import { isString } from '@aws-lambda-powertools/commons/typeUtils'; +import { isTruthy } from '@aws-lambda-powertools/commons/typeUtils'; -The Powertools for AWS Lambda (TypeScript) utilities follow a modular approach, similar to the official [AWS SDK v3 for JavaScript](https://github.com/aws/aws-sdk-js-v3). -Each TypeScript utility is installed as standalone npm package. +const value = { key: 'value' }; +if (isRecord(value)) { + // value is a record +} -Install all three core utilities at once with this single command: +const stringValue = 'string'; +if (isString(stringValue)) { + // stringValue is a string +} -```shell -npm install @aws-lambda-powertools/logger @aws-lambda-powertools/tracer @aws-lambda-powertools/metrics +const truthyValue = 'true'; +if (isTruthy(truthyValue)) { + // truthyValue is truthy +} ``` -Or refer to the installation guide of each utility: +Here's a full list of type utilities available in the package: + +- [`isInteger`](https://docs.powertools.aws.dev/lambda/typescript/latest/api/functions/_aws_lambda_powertools_commons.isIntegerNumber.html) +- [`isNull`](https://docs.powertools.aws.dev/lambda/typescript/latest/api/functions/_aws_lambda_powertools_commons.isNull.html) +- [`isNullOrUndefined`](https://docs.powertools.aws.dev/lambda/typescript/latest/api/functions/_aws_lambda_powertools_commons.isNullOrUndefined.html) +- [`isNumber`](https://docs.powertools.aws.dev/lambda/typescript/latest/api/functions/_aws_lambda_powertools_commons.isNumber.html) +- [`isRecord`](https://docs.powertools.aws.dev/lambda/typescript/latest/api/functions/_aws_lambda_powertools_commons.isRecord.html) +- [`isStrictEqual`](https://docs.powertools.aws.dev/lambda/typescript/latest/api/functions/_aws_lambda_powertools_commons.isStrictEqual.html) +- [`isString`](https://docs.powertools.aws.dev/lambda/typescript/latest/api/functions/_aws_lambda_powertools_commons.isString.html) +- [`isTruthy`](https://docs.powertools.aws.dev/lambda/typescript/latest/api/functions/_aws_lambda_powertools_commons.isTruthy.html) + +Many of these utilities also double as type guards, which you can use to narrow down the type of an object or value. + +### Base64 utils + +When working with Base64-encoded data, you can use the `fromBase64` utilities to quickly decode data and convert it to a `Uint8Array`. + +```typescript -๐Ÿ‘‰ [Installation guide for the **Tracer** utility](https://docs.powertools.aws.dev/lambda/typescript/latest/core/tracer#getting-started) +import { fromBase64 } from '@aws-lambda-powertools/commons/utils/base64'; -๐Ÿ‘‰ [Installation guide for the **Logger** utility](https://docs.powertools.aws.dev/lambda/typescript/latest/core/logger#getting-started) +const encodedValue = 'aGVsbG8gd29ybGQ='; -๐Ÿ‘‰ [Installation guide for the **Metrics** utility](https://docs.powertools.aws.dev/lambda/typescript/latest/core/metrics#getting-started) +const decoded = fromBase64(encodedValue); +// new Uint8Array([ 97, 71, 86, 115, 98, 71, 56, 103, 100, 50, 57, 121, 98, 71, 81, 61 ]); +``` + +### JSON type utils -๐Ÿ‘‰ [Installation guide for the **Parameters** utility](https://docs.powertools.aws.dev/lambda/typescript/latest/utilities/parameters/#getting-started) +In some cases, you may want to define a type for a JSON object or value. The utility comes with a set of types that you can use to define your JSON objects. -### Examples +```typescript +import type { JSONValue, JSONObject, JSONArray } from '@aws-lambda-powertools/commons'; +``` -You can find examples of how to use Powertools for AWS Lambda (TypeScript) in the [examples](https://github.com/aws-powertools/powertools-lambda-typescript/tree/main/examples/app) directory. The application is a simple REST API that can be deployed via either AWS CDK or AWS SAM. +### Lambda interface -### Demo applications +When using object-oriented patterns to define your Lambda handlers, you can use the `LambdaHandler` interface to define the shape of your handler methods. -The [Serverless TypeScript Demo](https://github.com/aws-samples/serverless-typescript-demo) shows how to use Powertools for AWS Lambda (TypeScript). -You can find instructions on how to deploy and load test this application in the [repository](https://github.com/aws-samples/serverless-typescript-demo). +```typescript +import type { Context } from 'aws-lambda'; +import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; -The [AWS Lambda performance tuning](https://github.com/aws-samples/optimizations-for-lambda-functions) repository also uses Powertools for AWS Lambda (TypeScript) as well as demonstrating other performance tuning techniques for Lambda functions written in TypeScript. +class Lambda implements LambdaInterface { + public handler = async (event: unknown, context: Context) => { + // Your handler code here + } +} + +const handlerClass = new Lambda(); +export const handler = lambda.handler.bind(lambda); +``` ## Contribute @@ -83,8 +106,8 @@ Help us prioritize upcoming functionalities or utilities by [upvoting existing R ## Connect -* **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** -* **Email**: aws-lambda-powertools-feedback@amazon.com +- **Powertools for AWS Lambda on Discord**: `#typescript` - **[Invite link](https://discord.gg/B8zZKbbyET)** +- **Email**: ## How to support Powertools for AWS Lambda (TypeScript)? @@ -94,17 +117,17 @@ Knowing which companies are using this library is important to help prioritize t The following companies, among others, use Powertools: -* [Hashnode](https://hashnode.com/) -* [Trek10](https://www.trek10.com/) -* [Elva](https://elva-group.com) -* [globaldatanet](https://globaldatanet.com/) -* [Bailey Nelson](https://www.baileynelson.com.au) -* [Perfect Post](https://www.perfectpost.fr) -* [Sennder](https://sennder.com/) -* [Certible](https://www.certible.com/) -* [tecRacer GmbH & Co. KG](https://www.tecracer.com/) -* [AppYourself](https://appyourself.net) -* [Alma Media](https://www.almamedia.fi) +- [Hashnode](https://hashnode.com/) +- [Trek10](https://www.trek10.com/) +- [Elva](https://elva-group.com) +- [globaldatanet](https://globaldatanet.com/) +- [Bailey Nelson](https://www.baileynelson.com.au) +- [Perfect Post](https://www.perfectpost.fr) +- [Sennder](https://sennder.com/) +- [Certible](https://www.certible.com/) +- [tecRacer GmbH & Co. KG](https://www.tecracer.com/) +- [AppYourself](https://appyourself.net) +- [Alma Media](https://www.almamedia.fi) ### Sharing your work @@ -112,11 +135,7 @@ Share what you did with Powertools for AWS Lambda (TypeScript) ๐Ÿ’ž๐Ÿ’ž. Blog po ### Using Lambda Layer -This helps us understand who uses Powertools for AWS Lambda (TypeScript) in a non-intrusive way, and helps us gain future investments for other Powertools for AWS Lambda languages. When [using Layers](#lambda-layers), you can add Powertools for AWS Lambda (TypeScript) as a dev dependency (or as part of your virtual env) to not impact the development process. - -## Credits - -Credits for the Lambda Powertools for AWS Lambda (TypeScript) idea go to [DAZN](https://github.com/getndazn) and their [DAZN Lambda Powertools](https://github.com/getndazn/dazn-lambda-powertools/). +This helps us understand who uses Powertools for AWS Lambda (TypeScript) in a non-intrusive way, and helps us gain future investments for other Powertools for AWS Lambda languages. When [using Layers](https://docs.powertools.aws.dev/lambda/typescript/latest/#lambda-layer), you can add Powertools as a dev dependency to not impact the development process. ## License diff --git a/packages/commons/src/Utility.ts b/packages/commons/src/Utility.ts index 23aacedad1..4a79cc82dc 100644 --- a/packages/commons/src/Utility.ts +++ b/packages/commons/src/Utility.ts @@ -1,16 +1,11 @@ /** - * ## Intro - * Utility is a base class that other Powertools for AWS Lambda (TypeScript) utilites can extend to inherit shared logic. + * `Utility` is a base class that other Powertools for AWS Lambda (TypeScript) utilites can extend to inherit shared logic. * + * Its main purpose is to encapsulate the cold start heuristic logic. Cold start is a term commonly used to describe the `Init` phase of a Lambda function. + * In this phase, Lambda creates or unfreezes an execution environment with the configured resources, downloads the code for the function and all layers, + * initializes any extensions, initializes the runtime, and then runs the functionโ€™s initialization code (the code outside the main handler). * - * ## Key features - * * Cold Start heuristic to determine if the current - * - * ## Usage - * - * ### Cold Start - * - * Cold start is a term commonly used to describe the `Init` phase of a Lambda function. In this phase, Lambda creates or unfreezes an execution environment with the configured resources, downloads the code for the function and all layers, initializes any extensions, initializes the runtime, and then runs the functionโ€™s initialization code (the code outside the main handler). The Init phase happens either during the first invocation, or in advance of function invocations if you have enabled provisioned concurrency. + * The Init phase happens either during the first invocation, or in advance of function invocations if you have enabled provisioned concurrency. * * To learn more about the Lambda execution environment lifecycle, see the [Execution environment section](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html) of the AWS Lambda documentation. * @@ -21,7 +16,7 @@ * * If you want to use this logic in your own utilities, `Utility` provides two methods: * - * #### `getColdStart()` + * `Utility.getColdStart()` * * Since the `Utility` class is instantiated outside of the Lambda handler it will persist across invocations of the same execution environment. This means that if you call `getColdStart()` multiple times, it will return `true` during the first invocation, and `false` afterwards. * @@ -36,7 +31,7 @@ * }; * ``` * - * #### `isColdStart()` + * `Utility.isColdStart()` * * This method is an alias of `getColdStart()` and is exposed for convenience and better readability in certain usages. * @@ -59,6 +54,20 @@ export class Utility { private coldStart = true; private readonly defaultServiceName: string = 'service_undefined'; + /** + * Get the cold start status of the current execution environment. + * + * @example + * ```typescript + * import { Utility } from '@aws-lambda-powertools/commons'; + * + * const utility = new Utility(); + * utility.isColdStart(); // true + * utility.isColdStart(); // false + * ``` + * + * The method also flips the cold start status to `false` after the first invocation. + */ public getColdStart(): boolean { if (this.coldStart) { this.coldStart = false; @@ -69,10 +78,27 @@ export class Utility { return false; } + /** + * Get the cold start status of the current execution environment. + * + * @example + * ```typescript + * import { Utility } from '@aws-lambda-powertools/commons'; + * + * const utility = new Utility(); + * utility.isColdStart(); // true + * utility.isColdStart(); // false + * ``` + * + * @see {@link getColdStart} + */ public isColdStart(): boolean { return this.getColdStart(); } + /** + * Get the default service name. + */ protected getDefaultServiceName(): string { return this.defaultServiceName; } @@ -81,7 +107,7 @@ export class Utility { * Validate that the service name provided is valid. * Used internally during initialization. * - * @param serviceName - Service name to validate + * @param serviceName Service name to validate */ protected isValidServiceName(serviceName?: string): boolean { return typeof serviceName === 'string' && serviceName.trim().length > 0; diff --git a/packages/commons/src/awsSdkUtils.ts b/packages/commons/src/awsSdkUtils.ts index 29095c3d37..451661b007 100644 --- a/packages/commons/src/awsSdkUtils.ts +++ b/packages/commons/src/awsSdkUtils.ts @@ -1,9 +1,6 @@ import { PT_VERSION } from './version.js'; import type { MiddlewareArgsLike, SdkClient } from './types/awsSdk.js'; -/** - * @internal - */ const EXEC_ENV = process.env.AWS_EXECUTION_ENV || 'NA'; const middlewareOptions = { relation: 'after', @@ -13,8 +10,9 @@ const middlewareOptions = { }; /** + * Type guard to check if the client provided is a valid AWS SDK v3 client. + * * @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' && @@ -35,9 +33,16 @@ const isSdkClient = (client: unknown): client is SdkClient => typeof client.middlewareStack.addRelativeTo === 'function'; /** + * Helper function to create a custom user agent middleware for the AWS SDK v3 clients. + * + * The middleware will append the provided feature name and the current version of + * the Powertools for AWS Lambda library to the user agent string. + * + * @example "PT/Tracer/2.1.0 PTEnv/nodejs20x" + * + * @param feature The feature name to be added to the user agent + * * @internal - * returns a middleware function for the MiddlewareStack, that can be used for the SDK clients - * @param feature */ const customUserAgentMiddleware = (feature: string) => { return (next: (arg0: T) => Promise) => @@ -51,8 +56,12 @@ const customUserAgentMiddleware = (feature: string) => { }; /** + * Check if the provided middleware stack already has the Powertools for AWS Lambda + * user agent middleware. + * + * @param middlewareStack The middleware stack to check + * * @internal - * Checks if the middleware stack already has the Powertools UA middleware */ const hasPowertools = (middlewareStack: string[]): boolean => { let found = false; @@ -65,6 +74,16 @@ const hasPowertools = (middlewareStack: string[]): boolean => { return found; }; +/** + * Add the Powertools for AWS Lambda user agent middleware to the + * AWS SDK v3 client provided. + * + * We use this middleware to unbotrusively track the usage of the library + * and secure continued investment in the project. + * + * @param client The AWS SDK v3 client to add the middleware to + * @param feature The feature name to be added to the user agent + */ const addUserAgentMiddleware = (client: unknown, feature: string): void => { try { if (isSdkClient(client)) { diff --git a/packages/commons/src/config/EnvironmentVariablesService.ts b/packages/commons/src/config/EnvironmentVariablesService.ts index 599f4c88d1..f9b2003a44 100644 --- a/packages/commons/src/config/EnvironmentVariablesService.ts +++ b/packages/commons/src/config/EnvironmentVariablesService.ts @@ -1,56 +1,62 @@ import type { ConfigServiceInterface } from '../types/ConfigServiceInterface.js'; /** - * Class EnvironmentVariablesService + * This class is used to fetch environment variables that are available in the execution environment. * - * This class is used to return environment variables that are available in the runtime of - * the current Lambda invocation. * These variables can be a mix of runtime environment variables set by AWS and - * variables that can be set by the developer additionally. + * other environment variables that are set by the developer to configure Powertools for AWS Lambda. + * + * @example + * ```typescript + * import { EnvironmentVariablesService } from '@aws-lambda-powertools/commons/'; + * + * const config = new EnvironmentVariablesService(); + * const serviceName = config.getServiceName(); + * ``` * - * @class - * @extends {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 + * @implements {ConfigServiceInterface} */ class EnvironmentVariablesService implements ConfigServiceInterface { /** - * @see https://docs.powertools.aws.dev/lambda/typescript/latest/#environment-variables - * @protected + * Increase JSON indentation for Logger to ease debugging when running functions locally or in a non-production environment */ protected devModeVariable = 'POWERTOOLS_DEV'; + /** + * Set service name used for tracing namespace, metrics dimension and structured logging + */ protected serviceNameVariable = 'POWERTOOLS_SERVICE_NAME'; - // Reserved environment variables + /** + * AWS X-Ray Trace ID environment variable + * @private + */ private xRayTraceIdVariable = '_X_AMZN_TRACE_ID'; /** - * It returns the value of an environment variable that has given name. + * Get the value of an environment variable by name. * - * @param {string} name - * @returns {string} + * @param {string} name The name of the environment variable to fetch. */ public get(name: string): string { return process.env[name]?.trim() || ''; } /** - * It returns the value of the POWERTOOLS_SERVICE_NAME environment variable. - * - * @returns {string} + * Get the value of the `POWERTOOLS_SERVICE_NAME` environment variable. */ public getServiceName(): string { return this.get(this.serviceNameVariable); } /** - * It returns the value of the _X_AMZN_TRACE_ID environment variable. + * Get the value of the `_X_AMZN_TRACE_ID` environment variable. * * The AWS X-Ray Trace data available in the environment variable has this format: * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, * * The actual Trace ID is: `1-5759e988-bd862e3fe1be46a994272793`. - * - * @returns {string} */ public getXrayTraceId(): string | undefined { const xRayTraceData = this.getXrayTraceData(); @@ -59,12 +65,10 @@ class EnvironmentVariablesService implements ConfigServiceInterface { } /** - * It returns true if the Sampled flag is set in the _X_AMZN_TRACE_ID environment variable. + * Determine if the current invocation is part of a sampled X-Ray trace. * * The AWS X-Ray Trace data available in the environment variable has this format: * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, - * - * @returns {boolean} */ public getXrayTraceSampled(): boolean { const xRayTraceData = this.getXrayTraceData(); @@ -73,19 +77,16 @@ class EnvironmentVariablesService implements ConfigServiceInterface { } /** - * It returns true if the `POWERTOOLS_DEV` environment variable is set to truthy value. - * - * @returns {boolean} + * Determine if the current invocation is running in a development environment. */ public isDevMode(): boolean { return this.isValueTrue(this.get(this.devModeVariable)); } /** - * It returns true if the string value represents a boolean true value. + * Helper function to determine if a value is considered thruthy. * - * @param {string} value - * @returns boolean + * @param value The value to check for truthiness. */ public isValueTrue(value: string): boolean { const truthyValues: string[] = ['1', 'y', 'yes', 't', 'true', 'on']; @@ -94,8 +95,9 @@ class EnvironmentVariablesService implements ConfigServiceInterface { } /** - * It parses the key/value data present in the _X_AMZN_TRACE_ID environment variable - * and returns it as an object when available. + * Get the AWS X-Ray Trace data from the environment variable. + * + * The method parses the environment variable `_X_AMZN_TRACE_ID` and returns an object with the key-value pairs. */ private getXrayTraceData(): Record | undefined { const xRayTraceEnv = this.get(this.xRayTraceIdVariable); diff --git a/packages/commons/src/middleware/cleanupMiddlewares.ts b/packages/commons/src/middleware/cleanupMiddlewares.ts index 4f1046e6f1..231a7ff4c1 100644 --- a/packages/commons/src/middleware/cleanupMiddlewares.ts +++ b/packages/commons/src/middleware/cleanupMiddlewares.ts @@ -6,7 +6,11 @@ import { } from './constants.js'; import type { MiddyLikeRequest, CleanupFunction } from '../types/middy.js'; -// Typeguard to assert that an object is of Function type +/** + * Typeguard to assert that an object is of Function type. + * + * @param obj The object to check + */ const isFunction = (obj: unknown): obj is CleanupFunction => { return typeof obj === 'function'; }; @@ -54,8 +58,8 @@ const isFunction = (obj: unknown): obj is CleanupFunction => { * }; * ``` * - * @param request - The Middy request object - * @param options - An optional object that can be used to pass options to the function + * @param request The Middy request object + * @param options An optional object that can be used to pass options to the function */ const cleanupMiddlewares = async (request: MiddyLikeRequest): Promise => { const cleanupFunctionNames = [ diff --git a/packages/commons/src/middleware/constants.ts b/packages/commons/src/middleware/constants.ts index cf38261995..07ee21df1a 100644 --- a/packages/commons/src/middleware/constants.ts +++ b/packages/commons/src/middleware/constants.ts @@ -4,9 +4,29 @@ * is present and execute it. */ const PREFIX = 'powertools-for-aws'; +/** + * Key to store the tracer instance in the `request.internal` object. + * + * @see {@link cleanupMiddlewares} + */ const TRACER_KEY = `${PREFIX}.tracer`; +/** + * Key to store the metrics instance in the `request.internal` object. + * + * @see {@link cleanupMiddlewares} + */ const METRICS_KEY = `${PREFIX}.metrics`; +/** + * Key to store the logger instance in the `request.internal` object. + * + * @see {@link cleanupMiddlewares} + */ const LOGGER_KEY = `${PREFIX}.logger`; +/** + * Key to store the idempotency instance in the `request.internal` object. + * + * @see {@link cleanupMiddlewares} + */ const IDEMPOTENCY_KEY = `${PREFIX}.idempotency`; export { PREFIX, TRACER_KEY, METRICS_KEY, LOGGER_KEY, IDEMPOTENCY_KEY }; diff --git a/packages/commons/src/typeUtils.ts b/packages/commons/src/typeUtils.ts index 6611aea46c..3cd53b81fc 100644 --- a/packages/commons/src/typeUtils.ts +++ b/packages/commons/src/typeUtils.ts @@ -1,5 +1,15 @@ /** - * Returns true if the passed value is a record (object). + * Check if a value is a record. + * + * @example + * ```typescript + * import { isRecord } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const value = { key: 'value' }; + * if (isRecord(value)) { + * // value is a record + * } + * ``` * * @param value The value to check */ @@ -15,6 +25,16 @@ const isRecord = ( /** * Check if a value is a string. * + * @example + * ```typescript + * import { isString } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const value = 'foo'; + * if (isString(value)) { + * // value is a string + * } + * ``` + * * @param value The value to check */ const isString = (value: unknown): value is string => { @@ -24,6 +44,16 @@ const isString = (value: unknown): value is string => { /** * Check if a value is a number. * + * @example + * ```typescript + * import { isNumber } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const value = 42; + * if (isNumber(value)) { + * // value is a number + * } + * ``` + * * @param value The value to check */ const isNumber = (value: unknown): value is number => { @@ -33,6 +63,16 @@ const isNumber = (value: unknown): value is number => { /** * Check if a value is an integer number. * + * @example + * ```typescript + * import { isIntegerNumber } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const value = 42; + * if (isIntegerNumber(value)) { + * // value is an integer number + * } + * ``` + * * @param value The value to check */ const isIntegerNumber = (value: unknown): value is number => { @@ -42,6 +82,18 @@ const isIntegerNumber = (value: unknown): value is number => { /** * Check if a value is truthy. * + * @example + * ```typescript + * import { isTruthy } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const value = 'yes'; + * if (isTruthy(value)) { + * // value is truthy + * } + * ``` + * + * @see https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/types-grammar/ch4.md#toboolean + * * @param value The value to check */ const isTruthy = (value: unknown): boolean => { @@ -61,7 +113,17 @@ const isTruthy = (value: unknown): boolean => { }; /** - * Check if a value is null. + * Check if a value is `null`. + * + * @example + * ```typescript + * import { isNull } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const value = null; + * if (isNull(value)) { + * // value is null + * } + * ``` * * @param value The value to check */ @@ -70,7 +132,17 @@ const isNull = (value: unknown): value is null => { }; /** - * Check if a value is null or undefined. + * Check if a value is `null` or `undefined`. + * + * @example + * ```typescript + * import { isNullOrUndefined } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const value = null; + * if (isNullOrUndefined(value)) { + * // value is null or undefined + * } + * ``` * * @param value The value to check */ @@ -81,6 +153,16 @@ const isNullOrUndefined = (value: unknown): value is null | undefined => { /** * Get the type of a value as a string. * + * @example + * ```typescript + * import { getType } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const type = getType('foo'); // 'string' + * const otherType = getType(42); // 'number' + * const anotherType = getType({ key: 'value' }); // 'object' + * const unknownType = getType(Symbol('foo')); // 'unknown' + * ``` + * * @param value The value to check */ const getType = (value: unknown): string => { @@ -104,6 +186,21 @@ const getType = (value: unknown): string => { /** * Compare two arrays for strict equality. * + * This function compares each element in the arrays, regardless of order. + * + * @example + * ```typescript + * import { areArraysEqual } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const left = [1, 2, 3]; + * const right = [3, 2, 1]; + * const equal = areArraysEqual(left, right); // true + * + * const otherLeft = [1, 2, 3]; + * const otherRight = [1, 2, 4]; + * const otherEqual = areArraysEqual(otherLeft, otherRight); // false + * ``` + * * @param left The left array to compare * @param right The right array to compare */ @@ -118,6 +215,19 @@ const areArraysEqual = (left: unknown[], right: unknown[]): boolean => { /** * Compare two records for strict equality. * + * @example + * ```typescript + * import { areRecordsEqual } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const left = { key: 'value' }; + * const right = { key: 'value' }; + * const equal = areRecordsEqual(left, right); // true + * + * const otherLeft = { key: 'value' }; + * const otherRight = { key: 'other value' }; + * const otherEqual = areRecordsEqual(otherLeft, otherRight); // false + * ``` + * * @param left The left record to compare * @param right The right record to compare */ @@ -143,6 +253,27 @@ const areRecordsEqual = ( * is compared to the corresponding key and value from right. If the * values are primitives, then they are compared using strict equality. * + * @example + * ```typescript + * import { isStrictEqual } from '@aws-lambda-powertools/commons/typeUtils'; + * + * const left = { key: 'value' }; + * const right = { key: 'value' }; + * const equal = isStrictEqual(left, right); // true + * + * const otherLeft = [1, 2, 3]; + * const otherRight = [3, 2, 1]; + * const otherEqual = isStrictEqual(otherLeft, otherRight); // true + * + * const anotherLeft = 'foo'; + * const anotherRight = 'bar'; + * const anotherEqual = isStrictEqual(anotherLeft, anotherRight); // false + * + * const yetAnotherLeft = 42; + * const yetAnotherRight = 42; + * const yetAnotherEqual = isStrictEqual(yetAnotherLeft, yetAnotherRight); // true + * ``` + * * @param left Left side of strict equality comparison * @param right Right side of strict equality comparison */ diff --git a/packages/commons/src/types/ConfigServiceInterface.ts b/packages/commons/src/types/ConfigServiceInterface.ts index 1e249f9373..2c9e5b2d4f 100644 --- a/packages/commons/src/types/ConfigServiceInterface.ts +++ b/packages/commons/src/types/ConfigServiceInterface.ts @@ -1,49 +1,49 @@ /** - * Abstract class ConfigService + * @abstract ConfigServiceInterface * * This class defines common methods and variables that can be set by the developer * in the runtime. */ interface ConfigServiceInterface { /** - * It returns the value of an environment variable that has given name. + * Get the value of an environment variable by name. * - * @param {string} name - * @returns {string} + * @param {string} name The name of the environment variable to fetch. */ get(name: string): string; /** - * It returns the value of the POWERTOOLS_SERVICE_NAME environment variable. - * - * @returns {string} + * Get the value of the `POWERTOOLS_SERVICE_NAME` environment variable. */ getServiceName(): string; /** - * It returns the value of the _X_AMZN_TRACE_ID environment variable. + * Get the value of the `_X_AMZN_TRACE_ID` environment variable. * * The AWS X-Ray Trace data available in the environment variable has this format: * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, * * The actual Trace ID is: `1-5759e988-bd862e3fe1be46a994272793`. - * - * @returns {string|undefined} */ getXrayTraceId(): string | undefined; /** - * It returns true if the `POWERTOOLS_DEV` environment variable is set to truthy value. + * Determine if the current invocation is part of a sampled X-Ray trace. * - * @returns {boolean} + * The AWS X-Ray Trace data available in the environment variable has this format: + * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, + */ + getXrayTraceSampled(): boolean; + + /** + * Determine if the current invocation is running in a development environment. */ isDevMode(): boolean; /** - * It returns true if the string value represents a boolean true value. + * Helper function to determine if a value is considered thruthy. * - * @param {string} value - * @returns boolean + * @param value The value to check for truthiness. */ isValueTrue(value: string): boolean; } diff --git a/packages/commons/src/types/LambdaInterface.ts b/packages/commons/src/types/LambdaInterface.ts index 7eb6609487..c92d58249c 100644 --- a/packages/commons/src/types/LambdaInterface.ts +++ b/packages/commons/src/types/LambdaInterface.ts @@ -1,20 +1,49 @@ import type { Handler } from 'aws-lambda'; +/** + * A type that represents a synchronous Lambda handler. + * + * @deprecated Use {@link AsyncHandler} instead. + */ type SyncHandler = ( event: Parameters[0], context: Parameters[1], callback: Parameters[2] ) => void; +/** + * A type that represents an asynchronous Lambda handler. + */ type AsyncHandler = ( event: Parameters[0], context: Parameters[1] ) => Promise[2]>[1]>>; +/** + * An interface that represents an object-oriented Lambda handler. + * + * @example + * ```typescript + * import type { Context } from 'aws-lambda'; + * import type { LambdaInterface } from '@aws-lambda-powertools/commons/types'; + * + * class Lambda implements LambdaInterface { + * public handler = async (event: unknown, context: Context) => { + * // Your handler code here + * } + * } + * + * const handlerClass = new Lambda(); + * export const handler = lambda.handler.bind(lambda); + * ``` + */ interface LambdaInterface { handler: SyncHandler | AsyncHandler; } +/** + * A decorator function that can be used to decorate a method in a class that implements the `LambdaInterface`. + */ type HandlerMethodDecorator = ( target: LambdaInterface, propertyKey: string | symbol, diff --git a/packages/commons/src/types/awsSdk.ts b/packages/commons/src/types/awsSdk.ts index f29d1bc8ff..6fb385a97a 100644 --- a/packages/commons/src/types/awsSdk.ts +++ b/packages/commons/src/types/awsSdk.ts @@ -1,6 +1,7 @@ /** + * Minimal interface for an AWS SDK v3 client. + * * @internal - * Minimal interface for an AWS SDK v3 client */ interface SdkClient { send: (args: unknown) => Promise; @@ -14,8 +15,9 @@ interface SdkClient { } /** - * @internal * Minimal type for the arguments passed to a middleware function + * + * @internal */ type MiddlewareArgsLike = { request: { headers: { [key: string]: string } } }; diff --git a/packages/commons/src/types/index.ts b/packages/commons/src/types/index.ts index b57b2dfec4..24fb3b8032 100644 --- a/packages/commons/src/types/index.ts +++ b/packages/commons/src/types/index.ts @@ -1,6 +1,8 @@ export type { + Request, MiddlewareLikeObj, MiddyLikeRequest, + MiddlewareFn, CleanupFunction, } from './middy.js'; export type { SdkClient, MiddlewareArgsLike } from './awsSdk.js'; diff --git a/packages/commons/src/types/json.ts b/packages/commons/src/types/json.ts index 765491c699..2e04a0cdca 100644 --- a/packages/commons/src/types/json.ts +++ b/packages/commons/src/types/json.ts @@ -1,6 +1,18 @@ +/** + * A type that represents base JSON primitives. + */ type JSONPrimitive = string | number | boolean | null | undefined; +/** + * A type that represents a JSON value. + */ type JSONValue = JSONPrimitive | JSONObject | JSONArray; +/** + * A type that represents a JSON object. + */ type JSONObject = { [key: number | string]: JSONValue }; +/** + * A type that represents a JSON array. + */ 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 92c94a0ba5..ab48fcbf6e 100644 --- a/packages/commons/src/types/middy.ts +++ b/packages/commons/src/types/middy.ts @@ -1,11 +1,17 @@ import type { Context } from 'aws-lambda'; /** - * We need to define these types and interfaces here because we can't import them from @middy/core. - * Importing them from @middy/core would introduce a dependency on @middy/core, which we don't want - * because we want to keep it as an optional dependency. Those users who don't use the Powertools for AWS Lambda (TypeScript) middleware - * and use `tsc` to compile their code will get an error if we import from @middy/core, see #1068. - * Given that we use a subset of the @middy/core types, we can define them here and avoid the dependency. + * This type represents the shape of a Middy.js request object. + * + * @note We need to define these types and interfaces here because we can't import them from Middy.js. + * + * Importing them from Middy.js would introduce a dependency on it, which we don't want + * because we want to keep it as an optional dependency. + * + * Those users who don't use the Powertools for AWS Lambda (TypeScript) middleware + * and use `tsc` to compile their code will get an error if we import them directly, see #1068. + * + * Given that we use a subset of Middy.js types, we can define them here and avoid the dependency. */ type Request< TEvent = unknown, @@ -22,6 +28,11 @@ type Request< }; }; +/** + * This type represents the shape of a middleware function that makes up a middleware object. + * + * @see {@link MiddlewareLikeObj} + */ type MiddlewareFn< TEvent = unknown, TResult = unknown, @@ -29,6 +40,9 @@ type MiddlewareFn< TContext extends Context = Context, > = (request: Request) => unknown; +/** + * This type represents the shape of a middleware object that can be passed to the `use` method of a Middy-like middleware. + */ type MiddlewareLikeObj< TEvent = unknown, TResult = unknown, @@ -40,6 +54,9 @@ type MiddlewareLikeObj< onError?: MiddlewareFn; }; +/** + * This type represents the `request` object that is passed to each middleware in the middleware chain. + */ type MiddyLikeRequest = { event: unknown; context: Context; @@ -54,7 +71,15 @@ type MiddyLikeRequest = { * Cleanup function that is used to cleanup resources when a middleware returns early. * Each Powertools for AWS middleware that needs to perform cleanup operations will * store a cleanup function with this signature in the `request.internal` object. + * + * @see {@link cleanupMiddlewares} */ type CleanupFunction = (request: MiddyLikeRequest) => Promise; -export type { MiddlewareLikeObj, MiddyLikeRequest, CleanupFunction }; +export type { + Request, + MiddlewareFn, + MiddlewareLikeObj, + MiddyLikeRequest, + CleanupFunction, +}; diff --git a/packages/commons/typedoc.json b/packages/commons/typedoc.json index 879b1d55e7..d3799003ef 100644 --- a/packages/commons/typedoc.json +++ b/packages/commons/typedoc.json @@ -1,7 +1,10 @@ { - "extends": ["../../typedoc.base.json"], + "extends": [ + "../../typedoc.base.json" + ], "entryPoints": [ - "./src/index.ts" + "./src/index.ts", + "./src/types/index.ts" ], - "readme": "README.md" + "readme": "./README.md" } \ No newline at end of file From ac6fce6729fd1c3946c5e1742f92f8f680a0c27e Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 30 Apr 2024 08:58:31 +0200 Subject: [PATCH 2/2] chore: fix interface in tests --- packages/logger/tests/unit/Logger.test.ts | 3 +++ packages/logger/tests/unit/middleware/middy.test.ts | 3 +++ packages/metrics/tests/unit/Metrics.test.ts | 3 +++ packages/tracer/tests/unit/Tracer.test.ts | 3 +++ 4 files changed, 12 insertions(+) diff --git a/packages/logger/tests/unit/Logger.test.ts b/packages/logger/tests/unit/Logger.test.ts index 0d79edd22a..e9e34beace 100644 --- a/packages/logger/tests/unit/Logger.test.ts +++ b/packages/logger/tests/unit/Logger.test.ts @@ -375,6 +375,9 @@ describe('Class: Logger', () => { getXrayTraceId(): string | undefined { return undefined; }, + getXrayTraceSampled() { + return true; + }, isDevMode(): boolean { return false; }, diff --git a/packages/logger/tests/unit/middleware/middy.test.ts b/packages/logger/tests/unit/middleware/middy.test.ts index 532b923c03..5cc612f5eb 100644 --- a/packages/logger/tests/unit/middleware/middy.test.ts +++ b/packages/logger/tests/unit/middleware/middy.test.ts @@ -295,6 +295,9 @@ describe('Middy middleware', () => { getXrayTraceId(): string | undefined { return undefined; }, + getXrayTraceSampled(): boolean { + return false; + }, isDevMode(): boolean { return false; }, diff --git a/packages/metrics/tests/unit/Metrics.test.ts b/packages/metrics/tests/unit/Metrics.test.ts index a1ee97ac4a..54af8ddfb7 100644 --- a/packages/metrics/tests/unit/Metrics.test.ts +++ b/packages/metrics/tests/unit/Metrics.test.ts @@ -245,6 +245,9 @@ describe('Class: Metrics', () => { getXrayTraceId(): string | undefined { return 'test-trace-id'; }, + getXrayTraceSampled(): boolean { + return true; + }, isDevMode(): boolean { return false; }, diff --git a/packages/tracer/tests/unit/Tracer.test.ts b/packages/tracer/tests/unit/Tracer.test.ts index f293717c58..c958d3d0ad 100644 --- a/packages/tracer/tests/unit/Tracer.test.ts +++ b/packages/tracer/tests/unit/Tracer.test.ts @@ -154,6 +154,9 @@ describe('Class: Tracer', () => { getXrayTraceId() { return '1-abcdef12-3456abcdef123456abcdef12'; }, + getXrayTraceSampled() { + return false; + }, }; // Act