From e97af6857532d44153aa38a22f29783d38593bb5 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Thu, 17 Nov 2022 20:17:42 +0100 Subject: [PATCH 1/8] docs: added main section, SSMProvider, transform, and config --- docs/utilities/parameters.md | 401 +++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 docs/utilities/parameters.md diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md new file mode 100644 index 0000000000..eac58d4e57 --- /dev/null +++ b/docs/utilities/parameters.md @@ -0,0 +1,401 @@ +--- +title: Parameters +description: Utility +--- + +The parameters utility provides high-level functions to retrieve one or multiple parameter values from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html){target="_blank"}, [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/){target="_blank"}, [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html){target="_blank"}, [Amazon DynamoDB](https://aws.amazon.com/dynamodb/){target="_blank"}, or your own parameter store. + +## Key features + +* Retrieve one or multiple parameters from the underlying provider +* Cache parameter values for a given amount of time (defaults to 5 seconds) +* Transform parameter values from JSON or base 64 encoded strings +* Bring Your Own Parameter Store Provider + +## Getting started + +By default, we fetch parameters from System Manager Parameter Store, secrets from Secrets Manager, and application configuration from AppConfig. + +### IAM Permissions + +This utility requires additional permissions to work as expected. + +???+ note + Different parameter providers require different permissions. + +| Provider | Function/Method | IAM Permission | +| --------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| SSM | **`getParameter`**, **`SSMProvider.get`** | **`ssm:GetParameter`** | +| SSM | **`getParameters`**, **`SSMProvider.getMultiple`** | **`ssm:GetParametersByPath`** | +| SSM | **`getParametersByName`**, **`SSMProvider.getParametersByName`** | **`ssm:GetParameter`** and **`ssm:GetParameters`** | +| SSM | If using **`decrypt: true`** | You must add an additional permission **`kms:Decrypt`** | +| Secrets | **`getSecret`**, **`SecretsProvider.get`** | **`secretsmanager:GetSecretValue`** | +| DynamoDB | **`DynamoDBProvider.get`** | **`dynamodb:GetItem`** | +| DynamoDB | **`DynamoDBProvider.getMultiple`** | **`dynamodb:Query`** | +| AppConfig | **`getAppConfig`**, **`AppConfigProvider.getAppConfig`** | **`appconfig:GetLatestConfiguration`** and **`appconfig:StartConfigurationSession`** | + +### Fetching parameters + +You can retrieve a single parameter using `getParameter` high-level function. + +```typescript hl_lines="1 5" title="Fetching a single parameter" +import { getParameter } from '@aws-lambda-powertools/parameters'; + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const value = getParameter('/my/parameter'); +}; +``` + +For multiple parameters, you can use either: + +* `getParameters` to recursively fetch all parameters by path. +* `getParametersByName` to fetch distinct parameters by their full name. It also accepts custom caching, transform, decrypt per parameter. + +=== "getParameters" + + ```typescript hl_lines="1 6" title="Fetching a single parameter" + import { getParameters } from '@aws-lambda-powertools/parameters'; + + export const handler = async (_event, _context): Promise => { + // Retrieve multiple parameters from a path prefix recursively + // This returns an object with the parameter name as key + const values = getParameters('/my/path/prefix'); + for (const [ key, value ] of Object.entries(values)) { + console.log(`${key}: ${value}`); + } + }; + ``` + +=== "getParametersByName" + + ```typescript hl_lines="1 3 12" + import { getParametersByName } from '@aws-lambda-powertools/parameters'; + + const props = { + '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, + '/no_cache_param': { maxAge: 0 }, + // Use default values + '/develop/service/payment/api/capture/url': {}, + }; + + export const handler = async (_event, _context): Promise => { + // This returns an object with the parameter name as key + const values = getParametersByName(props, { maxAge: 60 }); + for (const [ key, value ] of Object.entries(values)) { + console.log(`${key}: ${value}`); + } + }; + ``` + +???+ tip "`getParametersByName` supports graceful error handling" + By default, we will throw a `GetParameterError` when any parameter fails to be fetched. You can override it by setting `throwOnError: false`. + + When disabled, we take the following actions: + + * Add failed parameter name in the `_errors` key, _e.g._, `{ _errors: [ '/param1', '/param2' ] }` + * Keep only successful parameter names and their values in the response + * Throw `GetParameterError` if any of your parameters is named `_errors` + +```typescript hl_lines="1 3 10-11 13" +import { getParametersByName } from '@aws-lambda-powertools/parameters'; + +const props = { + '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, + // Example of non-existent parameter + '/this/param/does/not/exist': {}, +}; + +export const handler = async (_event, _context): Promise => { + const values = getParametersByName(props, { throwOnError: false }); + const errors = values?.errors || []; + + // Handle gracefully, since `/this/param/does/not/exist` will only be available in `_errors` + if (errors.length) { + // ... + } + + for (const [ key, value ] of Object.entries(values)) { + console.log(`${key}: ${value}`); + } +}; +``` + +## Advanced + +### Adjusting cache TTL + +???+ tip + `maxAge` parameter is also available in high level functions like `getParameter`, `getSecret`, etc. + +By default, we cache parameters retrieved in-memory for 5 seconds. + +You can adjust how long we should keep values in cache by using the param `maxAge`, when using `get()` or `getMultiple()` methods across all providers. + +```typescript hl_lines="7 10" title="Caching parameter(s) value in memory for longer than 5 seconds" +import { SSMProvider } from '@aws-lambda-powertools/parameters'; + +const parameters = new SSMProvider(); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const value = parameters.get('/my/parameter', { maxAge: 60 }); // 1 minute + + // Retrieve multiple parameters from a path prefix + const values = parameters.getMultiple('/my/path/prefix', { maxAge: 60 }); + for (const [ key, value ] of Object.entries(values)) { + console.log(`${key}: ${value}`); + } +}; +``` + +### Always fetching the latest + +If you'd like to always ensure you fetch the latest parameter from the store regardless if already available in cache, use `forceFetch` param. + +```typescript hl_lines="5" title="Forcefully fetching the latest parameter whether TTL has expired or not" +import { getParameter } from '@aws-lambda-powertools/parameters'; + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const value = getParameter('/my/parameter', { forceFetch: true }); +}; +``` + +### Built-in provider class + +For greater flexibility such as configuring the underlying SDK client used by built-in providers, you can use their respective Provider Classes directly. + +???+ tip + This can be used to retrieve values from other regions, change the retry behavior, etc. + +#### SSMProvider + +```typescript hl_lines="4 8 11" title="Example with SSMProvider for further extensibility" +import { SSMProvider } from '@aws-lambda-powertools/parameters'; +import type { SSMClientConfig } from '@aws-sdk/client-ssm'; + +const sdkConfig: SSMClientConfig = { region: 'us-east-1' }; +const parameters = new SSMProvider({ sdkConfig }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const value = parameters.get('/my/parameter'); + + // Retrieve multiple parameters from a path prefix + const values = parameters.getMultiple('/my/path/prefix'); + for (const [ key, value ] of Object.entries(values)) { + console.log(`${key}: ${value}`); + } +}; +``` + +The AWS Systems Manager Parameter Store provider supports two additional arguments for the `get()` and `getMultiple()` methods: + +| Parameter | Default | Description | +| ------------- | ------- | --------------------------------------------------------------------------------------------- | +| **decrypt** | `false` | Will automatically decrypt the parameter. | +| **recursive** | `true` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. | + +```typescript hl_lines="6 8" title="Example with get() and getMultiple()" +import { SSMProvider } from '@aws-lambda-powertools/parameters'; + +const parameters = new SSMProvider(); + +export const handler = async (_event, _context): Promise => { + const decryptedValue = parameters.get('/my/encrypted/parameter', { decrypt: true }); + + const noRecursiveValues = parameters.getMultiple('/my/path/prefix', { recursive: false }); +}; +``` + +### Deserializing values with transform parameter + +For parameters stored in JSON or Base64 format, you can use the `transform` argument for deserialization. + +???+ info + The `transform` argument is available across all providers, including the high level functions. + +=== "High level functions" + + ```typescript hl_lines="4" + import { getParameter } from '@aws-lambda-powertools/parameters'; + + export const handler = async (_event, _context): Promise => { + const valueFromJson = getParameter('/my/json/parameter', { transform: 'json' }); + }; + ``` + +=== "Providers" + + ```typescript hl_lines="7 10" + import { SSMProvider } from '@aws-lambda-powertools/parameters'; + + const parameters = new SSMProvider(); + + export const handler = async (_event, _context): Promise => { + // Transform a JSON string + const valueFromJson = parameters.get('/my/json/parameter', { transform: 'json' }); + + // Transform a Base64 encoded string + const valueFromBinary = parameters.getMultiple('/my/binary/parameter', { transform: 'binary' }); + }; + ``` + +#### Partial transform failures with `getMultiple()` + +If you use `transform` with `getMultiple()`, you can have a single malformed parameter value. To prevent failing the entire request, the method will return an `undefined` value for the parameters that failed to transform. + +You can override this by setting the `throwOnTransformError` argument to `true`. If you do so, a single transform error will throw a **`TransformParameterError`** error. + +For example, if you have three parameters, */param/a*, */param/b* and */param/c*, but */param/c* is malformed: + +```typescript hl_lines="10 19" title="Throwing TransformParameterError at first malformed parameter" +import { SSMProvider } from '@aws-lambda-powertools/parameters'; + +const parameters = new SSMProvider(); + +export const handler = async (_event, _context): Promise => { + /** + * This will display: + * /param/a: [some value] + * /param/b: [some value] + * /param/c: undefined + */ + const values = parameters.getMultiple('/param', { transform: 'json' }); + for (const [ key, value ] of Object.entries(values)) { + console.log(`${key}: ${value}`); + } + + try { + // This will throw a TransformParameterError error + const values = parameters.getMultiple('/param', { transform: 'json', throwOnTransformError: true }); + } catch (err) { + //... + } +}; +``` + +#### Auto-transform values on suffix + +If you use `transform` with `getMultiple()`, you might want to retrieve and transform parameters encoded in different formats. + +You can do this with a single request by using `transform: 'auto'`. This will instruct any Parameter to to infer its type based on the suffix and transform it accordingly. + +???+ info + `transform: 'auto'` feature is available across all providers, including the high level functions. + +```typescript hl_lines="6" title="Deserializing parameter values based on their suffix" +import { SSMProvider } from '@aws-lambda-powertools/parameters'; + +const parameters = new SSMProvider(); + +export const handler = async (_event, _context): Promise => { + const values = parameters.getMultiple('/param', { transform: 'auto' }); +}; +``` + +For example, if you have two parameters with the following suffixes `.json` and `.binary`: + +| Parameter name | Parameter value | +| --------------- | -------------------- | +| /param/a.json | [some encoded value] | +| /param/a.binary | [some encoded value] | + +The return of `parameters.getMultiple('/param', transform: 'auto');` call will be an object like: + +```json +{ + "a.json": [some value], + "b.binary": [some value] +} +``` + +### Passing additional SDK arguments + +You can use arbitrary keyword arguments to pass it directly to the underlying SDK method. + +```typescript hl_lines="7 9" title="Specify a VersionId for a secret" +import { SecretsManagerProvider } from '@aws-lambda-powertools/parameters'; +import type { GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; + +const secrets = new SecretsManagerProvider(); + +export const handler = async (_event, _context): Promise => { + const sdkOptions: GetSecretValueCommandInput = { VersionId: 'e62ec170-6b01-48c7-94f3-d7497851a8d2' }; + // The 'VersionId' argument will be passed to the underlying `GetSecretValueCommand` call. + const value = secrets.get('my-secret', { sdkOptions }); +}; +``` + +Here is the mapping between this utility's functions and methods and the underlying SDK: + +| Provider | Function/Method | Client name | Function name | +| ------------------- | ------------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| SSM Parameter Store | `getParameter` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | +| SSM Parameter Store | `getParameters` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | +| SSM Parameter Store | `SSMProvider.get` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | +| SSM Parameter Store | `SSMProvider.getMultiple` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | +| Secrets Manager | `getSecret` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | +| Secrets Manager | `SecretsProvider.get` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | +| DynamoDB | `DynamoDBProvider.get` | `@aws-sdk/client-dynamodb` | [GetItemCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/getitemcommand.html) | +| DynamoDB | `DynamoDBProvider.getMultiple` | `@aws-sdk/client-dynamodb` | [QueryCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/querycommand.html) | +| App Config | `getAppConfig` | `@aws-sdk/client-appconfig` | [GetConfigurationCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfig/classes/getconfigurationcommand.html) | + +### Bring your own AWS SDK v3 client + +You can use `awsSdkV3Client` parameter via any of the available [Provider Classes](#built-in-provider-class). + +| Provider | Client | +| --------------------------------------- | ------------------------------- | +| [SSMProvider](#ssmprovider) | `new boto3.client("ssm")` | +| [SecretsProvider](#secretsprovider) | `new boto3.client("secrets")` | +| [AppConfigProvider](#appconfigprovider) | `new boto3.client("appconfig")` | +| [DynamoDBProvider](#dynamodbprovider) | `new DynamoDBClient();` | + +Bringing them together in a single code snippet would look like this: + +```typescript title="Example: passing a custom boto3 client for each provider" +import { SSMProvider, SecretsProvider, DynamoDBProvider, AppConfigProvider } from '@aws-lambda-powertools/parameters'; +import { SSMClient } from '@aws-sdk/client-ssm'; +import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { AppConfigClient } from '@aws-sdk/client-appconfig'; + +// construct the clients with any custom configuration +const ssm = new SSMClient({ region: 'us-east-1' }); +const secretsManager = SecretsManagerClient({ region: 'us-west-2' }); +const dynamodb = new DynamoDBClient({ region: 'eu-west-1' }); +const appConfig = new AppConfigClient({ region: 'eu-south-2' }); + +const parameters = new SSMProvider({ awsSdkV3Client: ssm }); +const secrets = new SecretsProvider({ awsSdkV3Client: secretsManager }); +const dynamodbProvider = new DynamoDBProvider({ awsSdkV3Client: dynamodb, tableName: 'my-table' }); +const appConfigProvider = new AppConfigProvider({ awsSdkV3Client: appConfig, environment: 'my-env', application: 'my-app' }); +``` + +???+ question "When is this useful?" + Injecting a custom AWS SDK v3 client can make unit/snapshot testing easier, including SDK customizations. + +### Customizing AWS SDK v3 configuration + +The **`sdkConfig`** parameter enables you to pass in a custom [config object](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/configuring-the-jssdk.html) when constructing any of the built-in provider classes. + +???+ tip + You can use a custom session for retrieving parameters cross-account/region and for snapshot testing. + + When using VPC private endpoints, you can pass a custom client altogether. It's also useful for testing when injecting fake instances. + + +```typescript hl_lines="2 4 5" +import { SSMProvider } from '@aws-lambda-powertools/parameters'; +import type { SSMClientConfig } from '@aws-sdk/client-ssm'; + +const sdkConfig: SSMClientConfig = { region: 'us-east-1' }; +const parameters = new SSMProvider({ sdkConfig }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const value = parameters.get('/my/parameter'); +}; +``` From 697a4baf8c9d865c99854c8fa424d2056c5e97ac Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Fri, 25 Nov 2022 14:34:58 +0100 Subject: [PATCH 2/8] chore: added missing new in front of constructor --- docs/utilities/parameters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index eac58d4e57..80a1ca896b 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -364,7 +364,7 @@ import { AppConfigClient } from '@aws-sdk/client-appconfig'; // construct the clients with any custom configuration const ssm = new SSMClient({ region: 'us-east-1' }); -const secretsManager = SecretsManagerClient({ region: 'us-west-2' }); +const secretsManager = new SecretsManagerClient({ region: 'us-west-2' }); const dynamodb = new DynamoDBClient({ region: 'eu-west-1' }); const appConfig = new AppConfigClient({ region: 'eu-south-2' }); From 3d5ee6c4bc6835bca4da834ca094d75f319cbb7f Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 2 Jan 2023 18:59:26 +0100 Subject: [PATCH 3/8] docs: added SecretsProvider sections --- docs/utilities/parameters.md | 50 ++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 80a1ca896b..3e623c125c 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -121,6 +121,19 @@ export const handler = async (_event, _context): Promise => { }; ``` +### Fetching secrets + +You can fetch secrets stored in Secrets Manager using `getSecrets`. + +```typescript hl_lines="1 5" title="Fetching secrets" +import { getSecret } from '@aws-lambda-powertools/parameters'; + +export const handler = async (_event, _context): Promise => { + // Retrieve a single secret + const value = getSecret('my-secret'); +}; +``` + ## Advanced ### Adjusting cache TTL @@ -175,8 +188,8 @@ For greater flexibility such as configuring the underlying SDK client used by bu import { SSMProvider } from '@aws-lambda-powertools/parameters'; import type { SSMClientConfig } from '@aws-sdk/client-ssm'; -const sdkConfig: SSMClientConfig = { region: 'us-east-1' }; -const parameters = new SSMProvider({ sdkConfig }); +const clientConfig: SSMClientConfig = { region: 'us-east-1' }; +const parameters = new SSMProvider({ clientConfig }); export const handler = async (_event, _context): Promise => { // Retrieve a single parameter @@ -209,6 +222,20 @@ export const handler = async (_event, _context): Promise => { }; ``` +#### SecretsProvider + +```typescript hl_lines="5 9" title="Example with SecretsProvider for further extensibility" +import { SecretsProvider } from '@aws-lambda-powertools/parameters'; +import type { SecretsManagerClientConfig } from '@aws-sdk/client-secretsmanager'; + +const clientConfig: SecretsManagerClientConfig = { region: 'us-east-1' }; +const secrets = new SecretsProvider({ clientConfig }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single secret + const value = secrets.getSecret('my-secret'); +``` + ### Deserializing values with transform parameter For parameters stored in JSON or Base64 format, you can use the `transform` argument for deserialization. @@ -315,15 +342,18 @@ The return of `parameters.getMultiple('/param', transform: 'auto');` call will b You can use arbitrary keyword arguments to pass it directly to the underlying SDK method. -```typescript hl_lines="7 9" title="Specify a VersionId for a secret" -import { SecretsManagerProvider } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="8 12" title="Specify a VersionId for a secret" +import { SecretsProvider } from '@aws-lambda-powertools/parameters'; import type { GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; -const secrets = new SecretsManagerProvider(); +const secrets = new SecretsProvider(); export const handler = async (_event, _context): Promise => { - const sdkOptions: GetSecretValueCommandInput = { VersionId: 'e62ec170-6b01-48c7-94f3-d7497851a8d2' }; - // The 'VersionId' argument will be passed to the underlying `GetSecretValueCommand` call. + const sdkOptions: GetSecretValueCommandInput = { + VersionId: 'e62ec170-6b01-48c7-94f3-d7497851a8d2' + }; + // The 'VersionId' argument will be passed to the underlying + // `GetSecretValueCommand` call. const value = secrets.get('my-secret', { sdkOptions }); }; ``` @@ -379,7 +409,7 @@ const appConfigProvider = new AppConfigProvider({ awsSdkV3Client: appConfig, env ### Customizing AWS SDK v3 configuration -The **`sdkConfig`** parameter enables you to pass in a custom [config object](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/configuring-the-jssdk.html) when constructing any of the built-in provider classes. +The **`clientConfig`** parameter enables you to pass in a custom [config object](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/configuring-the-jssdk.html) when constructing any of the built-in provider classes. ???+ tip You can use a custom session for retrieving parameters cross-account/region and for snapshot testing. @@ -391,8 +421,8 @@ The **`sdkConfig`** parameter enables you to pass in a custom [config object](ht import { SSMProvider } from '@aws-lambda-powertools/parameters'; import type { SSMClientConfig } from '@aws-sdk/client-ssm'; -const sdkConfig: SSMClientConfig = { region: 'us-east-1' }; -const parameters = new SSMProvider({ sdkConfig }); +const clientConfig: SSMClientConfig = { region: 'us-east-1' }; +const parameters = new SSMProvider({ clientConfig }); export const handler = async (_event, _context): Promise => { // Retrieve a single parameter From 42f2bc0605b94d621a0d5250d489b37264e7f1e6 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 3 Jan 2023 19:53:05 +0100 Subject: [PATCH 4/8] docs: fixes --- docs/utilities/parameters.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 3e623c125c..e4e48fc741 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -295,7 +295,7 @@ export const handler = async (_event, _context): Promise => { } try { - // This will throw a TransformParameterError error + // This will throw a TransformParameterError const values = parameters.getMultiple('/param', { transform: 'json', throwOnTransformError: true }); } catch (err) { //... @@ -376,12 +376,12 @@ Here is the mapping between this utility's functions and methods and the underly You can use `awsSdkV3Client` parameter via any of the available [Provider Classes](#built-in-provider-class). -| Provider | Client | -| --------------------------------------- | ------------------------------- | -| [SSMProvider](#ssmprovider) | `new boto3.client("ssm")` | -| [SecretsProvider](#secretsprovider) | `new boto3.client("secrets")` | -| [AppConfigProvider](#appconfigprovider) | `new boto3.client("appconfig")` | -| [DynamoDBProvider](#dynamodbprovider) | `new DynamoDBClient();` | +| Provider | Client | +| --------------------------------------- | ----------------------------- | +| [SSMProvider](#ssmprovider) | `new SSMClient();` | +| [SecretsProvider](#secretsprovider) | `new SecretsManagerClient();` | +| [AppConfigProvider](#appconfigprovider) | `new AppConfigDataClient();` | +| [DynamoDBProvider](#dynamodbprovider) | `new DynamoDBClient();` | Bringing them together in a single code snippet would look like this: From 537b46730c6ca68455b9d5ce3a986080a7c266b4 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 3 Jan 2023 20:03:16 +0100 Subject: [PATCH 5/8] docs: added DynamoDBProvider --- docs/utilities/parameters.md | 111 +++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index e4e48fc741..c435e61eab 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -236,6 +236,117 @@ export const handler = async (_event, _context): Promise => { const value = secrets.getSecret('my-secret'); ``` +#### DynamoDBProvider + +The DynamoDB Provider does not have any high-level functions, as it needs to know the name of the DynamoDB table containing the parameters. + +**DynamoDB table structure for single parameters** + +For single parameters, you must use `id` as the [partition key](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey) for that table. + +???+ example + + DynamoDB table with `id` partition key and `value` as attribute + + | id | value | + | ------------ | -------- | + | my-parameter | my-value | + +With this table, `dynamodbProvider.get('my-param')` will return `my-value`. + +=== "index.ts" + ```typescript hl_lines="3 7" + import { DynamoDBProvider } from '@aws-lambda-powertools/parameters'; + + const dynamodbProvider = new DynamoDBProvider({ tableName: 'my-table' }); + + export const handler = async (_event, _context): Promise => { + // Retrieve a value from DynamoDB + const value = dynamodbProvider.get('my-parameter'); + ``` + +=== "DynamoDB Local example" + You can initialize the DynamoDB provider pointing to [DynamoDB Local](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) using the `endpoint` field in the `clientConfig` parameter: + + ```typescript hl_lines="3" + import { DynamoDBProvider } from '@aws-lambda-powertools/parameters'; + + const dynamodbProvider = new DynamoDBProvider({ + tableName: 'my-table', + clientConfig: { + endpoint: 'http://localhost:8000' + } + }); + ``` + +**DynamoDB table structure for multiple values parameters** + +You can retrieve multiple parameters sharing the same `id` by having a sort key named `sk`. + +???+ example + + DynamoDB table with `id` primary key, `sk` as sort key` and `value` as attribute + + | id | sk | value | + | ----------- | ------- | ---------- | + | my-hash-key | param-a | my-value-a | + | my-hash-key | param-b | my-value-b | + | my-hash-key | param-c | my-value-c | + +With this table, `dynamodbProvider.getMultiple('my-hash-key')` will return a dictionary response in the shape of `sk:value`. + +=== "index.ts" + ```typescript hl_lines="3 8" + import { DynamoDBProvider } from '@aws-lambda-powertools/parameters'; + + const dynamodbProvider = new DynamoDBProvider({ tableName: 'my-table' }); + + export const handler = async (_event, _context): Promise => { + // Retrieve multiple values by performing a Query on the DynamoDB table + // This returns a dict with the sort key attribute as dict key. + const parameters = dynamodbProvider.getMultiple('my-hash-key'); + for (const [ key, value ] of Object.entries(values)) { + // key: param-a + // value: my-value-a + console.log(`${key}: ${value}`); + } + ``` + +=== "parameters dict response" + + ```json + { + "param-a": "my-value-a", + "param-b": "my-value-b", + "param-c": "my-value-c" + } + ``` + +**Customizing DynamoDBProvider** + +DynamoDB provider can be customized at initialization to match your table structure: + +| Parameter | Mandatory | Default | Description | +| ------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------- | +| **tableName** | **Yes** | *(N/A)* | Name of the DynamoDB table containing the parameter values. | +| **keyAttr** | No | `id` | Hash key for the DynamoDB table. | +| **sortAttr** | No | `sk` | Range key for the DynamoDB table. You don't need to set this if you don't use the `getMultiple()` method. | +| **valueAttr** | No | `value` | Name of the attribute containing the parameter value. | + +```typescript hl_lines="3-8" title="Customizing DynamoDBProvider to suit your table design" +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters'; + +const dynamodbProvider = new DynamoDBProvider( + tableName='my-table', + keyAttr='MyKeyAttr', + sortAttr='MySortAttr', + valueAttr='MyvalueAttr' +) + +export const handler = async (_event, _context): Promise => { + const value = dynamodbProvider.get('my-parameter') +``` + ### Deserializing values with transform parameter For parameters stored in JSON or Base64 format, you can use the `transform` argument for deserialization. From d84dbe55af2a9ab137b14b1fa443deecbf6bca69 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Sat, 7 Jan 2023 16:39:34 +0100 Subject: [PATCH 6/8] docs: completed first version of docs --- docs/utilities/parameters.md | 522 +++++++++++++++++++++-------------- 1 file changed, 317 insertions(+), 205 deletions(-) diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index c435e61eab..1be0a49777 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -3,6 +3,9 @@ title: Parameters description: Utility --- +???+ warning + This page refers to an **unreleased and upcoming utility**. Please refer to this [GitHub milestone](https://github.com/awslabs/aws-lambda-powertools-typescript/milestone/8) for the latest updates. + The parameters utility provides high-level functions to retrieve one or multiple parameter values from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html){target="_blank"}, [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/){target="_blank"}, [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html){target="_blank"}, [Amazon DynamoDB](https://aws.amazon.com/dynamodb/){target="_blank"}, or your own parameter store. ## Key features @@ -14,7 +17,37 @@ The parameters utility provides high-level functions to retrieve one or multiple ## Getting started -By default, we fetch parameters from System Manager Parameter Store, secrets from Secrets Manager, and application configuration from AppConfig. +By default, we fetch parameters from System Manager Parameter Store (SSM), secrets from Secrets Manager, and application configuration from AppConfig. Additionally, we support a DynamoDB provider to retrieve arbitrary parameters from your tables. + +### Installation + +???+ note + This utility supports **[AWS SDK v3 for JavaScript](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/) only**. This allows the utility to be modular, and you to install only the SDK packages you need and keep your bundle size small. + +Depending on the provider you want to use, install the library and the corresponding AWS SDK package: + +=== "SSMProvider" + ```bash + npm install @aws-lambda-powertools/parameters @aws-sdk/client-ssm + ``` + +=== "SecretsProvider" + ```bash + npm install @aws-lambda-powertools/parameters @aws-sdk/client-secrets-manager + ``` + +=== "AppConfigProvider" + ```bash + npm install @aws-lambda-powertools/parameters @aws-sdk/client-appconfigdata + ``` + +=== "DynamoDBProvider" + ```bash + npm install @aws-lambda-powertools/parameters @aws-sdk/client-dynamodb + ``` + +???+ tip + If you are using the `nodejs18.x` runtime, the AWS SDK for JavaScript v3 is already installed and you can install the utility only. ### IAM Permissions @@ -36,14 +69,14 @@ This utility requires additional permissions to work as expected. ### Fetching parameters -You can retrieve a single parameter using `getParameter` high-level function. +You can retrieve a single parameter using the `getParameter` high-level function. -```typescript hl_lines="1 5" title="Fetching a single parameter" -import { getParameter } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="1 5" title="Fetching a single parameter from SSM" +import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const value = getParameter('/my/parameter'); + // Retrieve a single parameter + const parameter = await getParameter('/my/parameter'); }; ``` @@ -54,37 +87,38 @@ For multiple parameters, you can use either: === "getParameters" - ```typescript hl_lines="1 6" title="Fetching a single parameter" - import { getParameters } from '@aws-lambda-powertools/parameters'; + ```typescript hl_lines="1 8" title="Fetching multiple parameters by path from SSM" + import { getParameters } from '@aws-lambda-powertools/parameters/ssm'; export const handler = async (_event, _context): Promise => { - // Retrieve multiple parameters from a path prefix recursively - // This returns an object with the parameter name as key - const values = getParameters('/my/path/prefix'); - for (const [ key, value ] of Object.entries(values)) { - console.log(`${key}: ${value}`); - } + /** + * Retrieve multiple parameters from a path prefix recursively. + * This returns an object with the parameter name as key + */ + const parameters = await getParameters('/my/path/prefix'); + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } }; ``` === "getParametersByName" - ```typescript hl_lines="1 3 12" - import { getParametersByName } from '@aws-lambda-powertools/parameters'; + ```typescript hl_lines="1 4-6 11" title="Fetching multiple parameters by names from SSM" + import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; const props = { - '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, - '/no_cache_param': { maxAge: 0 }, - // Use default values - '/develop/service/payment/api/capture/url': {}, + '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, + '/no_cache_param': { maxAge: 0 }, + '/develop/service/payment/api/capture/url': {}, // When empty or undefined, it uses default values }; export const handler = async (_event, _context): Promise => { - // This returns an object with the parameter name as key - const values = getParametersByName(props, { maxAge: 60 }); - for (const [ key, value ] of Object.entries(values)) { - console.log(`${key}: ${value}`); - } + // This returns an object with the parameter name as key + const parameters = await getParametersByName(props, { maxAge: 60 }); + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } }; ``` @@ -97,27 +131,28 @@ For multiple parameters, you can use either: * Keep only successful parameter names and their values in the response * Throw `GetParameterError` if any of your parameters is named `_errors` -```typescript hl_lines="1 3 10-11 13" -import { getParametersByName } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="1 4-5 10 15" +import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; const props = { - '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, - // Example of non-existent parameter - '/this/param/does/not/exist': {}, + '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, + '/this/param/does/not/exist': {}, // <- Example of non-existent parameter }; export const handler = async (_event, _context): Promise => { - const values = getParametersByName(props, { throwOnError: false }); - const errors = values?.errors || []; - - // Handle gracefully, since `/this/param/does/not/exist` will only be available in `_errors` - if (errors.length) { - // ... - } + const { + _errors: errors, + ...parameters + } = await getParametersByName(props, { throwOnError: false }); + + // Handle gracefully, since `/this/param/does/not/exist` will only be available in `_errors` + if (errors && errors.length) { + console.error(`Unable to retrieve parameters: ${errors.join(',')}`); + } - for (const [ key, value ] of Object.entries(values)) { - console.log(`${key}: ${value}`); - } + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } }; ``` @@ -126,11 +161,29 @@ export const handler = async (_event, _context): Promise => { You can fetch secrets stored in Secrets Manager using `getSecrets`. ```typescript hl_lines="1 5" title="Fetching secrets" -import { getSecret } from '@aws-lambda-powertools/parameters'; +import { getSecret } from '@aws-lambda-powertools/parameters/secrets'; + +export const handler = async (_event, _context): Promise => { + // Retrieve a single secret + const secret = await getSecret('my-secret'); +}; +``` + +### Fetching app configurations + +You can fetch application configurations in AWS AppConfig using `getAppConfig`. + +The following will retrieve the latest version and store it in the cache. + +```typescript hl_lines="1 5-8" title="Fetching latest config from AppConfig" +import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig'; export const handler = async (_event, _context): Promise => { - // Retrieve a single secret - const value = getSecret('my-secret'); + // Retrieve a configuration, latest version + const config = await getAppConfig('my-configuration', { + environment: 'my-env', + application: 'my-app' + }); }; ``` @@ -145,18 +198,18 @@ By default, we cache parameters retrieved in-memory for 5 seconds. You can adjust how long we should keep values in cache by using the param `maxAge`, when using `get()` or `getMultiple()` methods across all providers. -```typescript hl_lines="7 10" title="Caching parameter(s) value in memory for longer than 5 seconds" -import { SSMProvider } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="7 10" title="Caching parameters values in memory for longer than 5 seconds" +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; -const parameters = new SSMProvider(); +const parametersProvider = new SSMProvider(); export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const value = parameters.get('/my/parameter', { maxAge: 60 }); // 1 minute + // Retrieve a single parameter + const parameter = await parametersProvider.get('/my/parameter', { maxAge: 60 }); // 1 minute - // Retrieve multiple parameters from a path prefix - const values = parameters.getMultiple('/my/path/prefix', { maxAge: 60 }); - for (const [ key, value ] of Object.entries(values)) { + // Retrieve multiple parameters from a path prefix + const parameters = await parametersProvider.getMultiple('/my/path/prefix', { maxAge: 120 }); // 2 minutes + for (const [ key, value ] of Object.entries(parameters)) { console.log(`${key}: ${value}`); } }; @@ -164,14 +217,14 @@ export const handler = async (_event, _context): Promise => { ### Always fetching the latest -If you'd like to always ensure you fetch the latest parameter from the store regardless if already available in cache, use `forceFetch` param. +If you'd like to always ensure you fetch the latest parameter from the store regardless if already available in cache, use the `forceFetch` parameter. ```typescript hl_lines="5" title="Forcefully fetching the latest parameter whether TTL has expired or not" -import { getParameter } from '@aws-lambda-powertools/parameters'; +import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const value = getParameter('/my/parameter', { forceFetch: true }); + // Retrieve a single parameter + const parameter = await getParameter('/my/parameter', { forceFetch: true }); }; ``` @@ -184,20 +237,20 @@ For greater flexibility such as configuring the underlying SDK client used by bu #### SSMProvider -```typescript hl_lines="4 8 11" title="Example with SSMProvider for further extensibility" -import { SSMProvider } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="4-5" title="Example with SSMProvider for further extensibility" +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; import type { SSMClientConfig } from '@aws-sdk/client-ssm'; const clientConfig: SSMClientConfig = { region: 'us-east-1' }; -const parameters = new SSMProvider({ clientConfig }); +const parametersProvider = new SSMProvider({ clientConfig }); export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const value = parameters.get('/my/parameter'); + // Retrieve a single parameter + const parameter = await parametersProvider.get('/my/parameter'); - // Retrieve multiple parameters from a path prefix - const values = parameters.getMultiple('/my/path/prefix'); - for (const [ key, value ] of Object.entries(values)) { + // Retrieve multiple parameters from a path prefix + const parameters = await parametersProvider.getMultiple('/my/path/prefix'); + for (const [ key, value ] of Object.entries(parameters)) { console.log(`${key}: ${value}`); } }; @@ -211,34 +264,61 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen | **recursive** | `true` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. | ```typescript hl_lines="6 8" title="Example with get() and getMultiple()" -import { SSMProvider } from '@aws-lambda-powertools/parameters'; +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; -const parameters = new SSMProvider(); +const parametersProvider = new SSMProvider(); export const handler = async (_event, _context): Promise => { - const decryptedValue = parameters.get('/my/encrypted/parameter', { decrypt: true }); + const decryptedValue = await parametersProvider.get('/my/encrypted/parameter', { decrypt: true }); - const noRecursiveValues = parameters.getMultiple('/my/path/prefix', { recursive: false }); + const noRecursiveValues = await parametersProvider.getMultiple('/my/path/prefix', { recursive: false }); }; ``` #### SecretsProvider -```typescript hl_lines="5 9" title="Example with SecretsProvider for further extensibility" -import { SecretsProvider } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="4-5" title="Example with SecretsProvider for further extensibility" +import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; import type { SecretsManagerClientConfig } from '@aws-sdk/client-secretsmanager'; const clientConfig: SecretsManagerClientConfig = { region: 'us-east-1' }; -const secrets = new SecretsProvider({ clientConfig }); +const secretsProvider = new SecretsProvider({ clientConfig }); export const handler = async (_event, _context): Promise => { // Retrieve a single secret - const value = secrets.getSecret('my-secret'); + const secret = await secretsProvider.get('my-secret'); +}; +``` + +#### AppConfigProvider + +The AWS AppConfig provider requires two arguments when initialized: + +| Parameter | Mandatory in constructor | Alternative | Description | +| --------------- | ------------------------ | -------------------------------------- | -------------------------------------------------------- | +| **application** | No | `POWERTOOLS_SERVICE_NAME` env variable | The application in which your config resides. | +| **environment** | Yes | _(N/A)_ | The environment that corresponds to your current config. | + +```typescript hl_lines="4 8" title="Example with AppConfigProvider for further extensibility" +import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig'; +import type { AppConfigDataClientConfig } from '@aws-sdk/client-appconfigdata'; + +const clientConfig: AppConfigDataClientConfig = { region: 'us-east-1' }; +const configsProvider = new AppConfigProvider({ + application: 'my-app', + environment: 'my-env', + clientConfig, +}); + +export const handler = async (_event, _context): Promise => { + // Retrieve a config + const config = await configsProvider.get('my-config'); +}; ``` #### DynamoDBProvider -The DynamoDB Provider does not have any high-level functions, as it needs to know the name of the DynamoDB table containing the parameters. +The DynamoDB Provider does not have any high-level functions and needs to know the name of the DynamoDB table containing the parameters. **DynamoDB table structure for single parameters** @@ -252,31 +332,32 @@ For single parameters, you must use `id` as the [partition key](https://docs.aws | ------------ | -------- | | my-parameter | my-value | -With this table, `dynamodbProvider.get('my-param')` will return `my-value`. +With this table, `await dynamoDBProvider.get('my-param')` will return `my-value`. -=== "index.ts" +=== "handler.ts" ```typescript hl_lines="3 7" - import { DynamoDBProvider } from '@aws-lambda-powertools/parameters'; + import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - const dynamodbProvider = new DynamoDBProvider({ tableName: 'my-table' }); + const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); export const handler = async (_event, _context): Promise => { - // Retrieve a value from DynamoDB - const value = dynamodbProvider.get('my-parameter'); + // Retrieve a value from DynamoDB + const value = await dynamoDBProvider.get('my-parameter'); + }; ``` === "DynamoDB Local example" You can initialize the DynamoDB provider pointing to [DynamoDB Local](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) using the `endpoint` field in the `clientConfig` parameter: - ```typescript hl_lines="3" - import { DynamoDBProvider } from '@aws-lambda-powertools/parameters'; + ```typescript hl_lines="5-7" + import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - const dynamodbProvider = new DynamoDBProvider({ - tableName: 'my-table', - clientConfig: { - endpoint: 'http://localhost:8000' - } - }); + const dynamoDBProvider = new DynamoDBProvider({ + tableName: 'my-table', + clientConfig: { + endpoint: 'http://localhost:8000' + }, + }); ``` **DynamoDB table structure for multiple values parameters** @@ -285,7 +366,7 @@ You can retrieve multiple parameters sharing the same `id` by having a sort key ???+ example - DynamoDB table with `id` primary key, `sk` as sort key` and `value` as attribute + DynamoDB table with `id` primary key, `sk` as sort key and `value` as attribute | id | sk | value | | ----------- | ------- | ---------- | @@ -293,32 +374,35 @@ You can retrieve multiple parameters sharing the same `id` by having a sort key | my-hash-key | param-b | my-value-b | | my-hash-key | param-c | my-value-c | -With this table, `dynamodbProvider.getMultiple('my-hash-key')` will return a dictionary response in the shape of `sk:value`. +With this table, `await dynamoDBProvider.getMultiple('my-hash-key')` will return a dictionary response in the shape of `sk:value`. -=== "index.ts" - ```typescript hl_lines="3 8" - import { DynamoDBProvider } from '@aws-lambda-powertools/parameters'; +=== "handler.ts" + ```typescript hl_lines="3 10" + import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - const dynamodbProvider = new DynamoDBProvider({ tableName: 'my-table' }); + const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); export const handler = async (_event, _context): Promise => { - // Retrieve multiple values by performing a Query on the DynamoDB table - // This returns a dict with the sort key attribute as dict key. - const parameters = dynamodbProvider.getMultiple('my-hash-key'); - for (const [ key, value ] of Object.entries(values)) { - // key: param-a - // value: my-value-a - console.log(`${key}: ${value}`); - } + /** + * Retrieve multiple values by performing a Query on the DynamoDB table. + * This returns a dict with the sort key attribute as dict key. + */ + const values = await dynamoDBProvider.getMultiple('my-hash-key'); + for (const [ key, value ] of Object.entries(values)) { + // key: param-a + // value: my-value-a + console.log(`${key}: ${value}`); + } + }; ``` -=== "parameters dict response" +=== "values response object" ```json { - "param-a": "my-value-a", - "param-b": "my-value-b", - "param-c": "my-value-c" + "param-a": "my-value-a", + "param-b": "my-value-b", + "param-c": "my-value-c" } ``` @@ -334,17 +418,18 @@ DynamoDB provider can be customized at initialization to match your table struct | **valueAttr** | No | `value` | Name of the attribute containing the parameter value. | ```typescript hl_lines="3-8" title="Customizing DynamoDBProvider to suit your table design" -import { DynamoDBProvider } from '@aws-lambda-powertools/parameters'; +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; -const dynamodbProvider = new DynamoDBProvider( - tableName='my-table', - keyAttr='MyKeyAttr', - sortAttr='MySortAttr', - valueAttr='MyvalueAttr' -) +const dynamoDBProvider = new DynamoDBProvider({ + tableName:'my-table', + keyAttr:'key', + sortAttr:'sort', + valueAttr:'val' +}); export const handler = async (_event, _context): Promise => { - const value = dynamodbProvider.get('my-parameter') + const value = await dynamoDBProvider.get('my-parameter'); +}; ``` ### Deserializing values with transform parameter @@ -355,30 +440,28 @@ For parameters stored in JSON or Base64 format, you can use the `transform` argu The `transform` argument is available across all providers, including the high level functions. === "High level functions" + ```typescript hl_lines="4" + import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; - ```typescript hl_lines="4" - import { getParameter } from '@aws-lambda-powertools/parameters'; - - export const handler = async (_event, _context): Promise => { - const valueFromJson = getParameter('/my/json/parameter', { transform: 'json' }); - }; - ``` + export const handler = async (_event, _context): Promise => { + const valueFromJson = await getParameter('/my/json/parameter', { transform: 'json' }); + }; + ``` === "Providers" + ```typescript hl_lines="7 10" + import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; - ```typescript hl_lines="7 10" - import { SSMProvider } from '@aws-lambda-powertools/parameters'; - - const parameters = new SSMProvider(); + const secretsProvider = new SecretsProvider(); - export const handler = async (_event, _context): Promise => { - // Transform a JSON string - const valueFromJson = parameters.get('/my/json/parameter', { transform: 'json' }); + export const handler = async (_event, _context): Promise => { + // Transform a JSON string + const json = await secretsProvider.get('my-secret-json', { transform: 'json' }); - // Transform a Base64 encoded string - const valueFromBinary = parameters.getMultiple('/my/binary/parameter', { transform: 'binary' }); - }; - ``` + // Transform a Base64 encoded string (e.g. binary) + const binary = await secretsProvider.getMultiple('my-secret-binary', { transform: 'binary' }); + }; + ``` #### Partial transform failures with `getMultiple()` @@ -388,29 +471,32 @@ You can override this by setting the `throwOnTransformError` argument to `true`. For example, if you have three parameters, */param/a*, */param/b* and */param/c*, but */param/c* is malformed: -```typescript hl_lines="10 19" title="Throwing TransformParameterError at first malformed parameter" -import { SSMProvider } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="19-22" title="Throwing TransformParameterError at first malformed parameter" +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; -const parameters = new SSMProvider(); +const parametersProvider = new SSMProvider(); export const handler = async (_event, _context): Promise => { - /** - * This will display: - * /param/a: [some value] - * /param/b: [some value] - * /param/c: undefined - */ - const values = parameters.getMultiple('/param', { transform: 'json' }); - for (const [ key, value ] of Object.entries(values)) { - console.log(`${key}: ${value}`); - } + /** + * This will display: + * /param/a: [some value] + * /param/b: [some value] + * /param/c: undefined + */ + const parameters = await parametersProvider.getMultiple('/param', { transform: 'json' }); + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } - try { - // This will throw a TransformParameterError - const values = parameters.getMultiple('/param', { transform: 'json', throwOnTransformError: true }); - } catch (err) { - //... - } + try { + // This will throw a TransformParameterError + const parameters2 = await parametersProvider.getMultiple('/param', { + transform: 'json', + throwOnTransformError: true + }); + } catch (err) { + console.error(err); + } }; ``` @@ -418,18 +504,18 @@ export const handler = async (_event, _context): Promise => { If you use `transform` with `getMultiple()`, you might want to retrieve and transform parameters encoded in different formats. -You can do this with a single request by using `transform: 'auto'`. This will instruct any Parameter to to infer its type based on the suffix and transform it accordingly. +You can do this with a single request by using `transform: 'auto'`. This will instruct any provider to to infer its type based on the suffix and transform it accordingly. ???+ info `transform: 'auto'` feature is available across all providers, including the high level functions. ```typescript hl_lines="6" title="Deserializing parameter values based on their suffix" -import { SSMProvider } from '@aws-lambda-powertools/parameters'; +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; -const parameters = new SSMProvider(); +const parametersProvider = new SSMProvider(); export const handler = async (_event, _context): Promise => { - const values = parameters.getMultiple('/param', { transform: 'auto' }); + const values = await parametersProvider.getMultiple('/param', { transform: 'auto' }); }; ``` @@ -440,52 +526,55 @@ For example, if you have two parameters with the following suffixes `.json` and | /param/a.json | [some encoded value] | | /param/a.binary | [some encoded value] | -The return of `parameters.getMultiple('/param', transform: 'auto');` call will be an object like: +The return of `await parametersProvider.getMultiple('/param', transform: 'auto');` call will be an object like: ```json { - "a.json": [some value], - "b.binary": [some value] + "a.json": [some value], + "b.binary": [some value] } ``` ### Passing additional SDK arguments -You can use arbitrary keyword arguments to pass it directly to the underlying SDK method. +You can use a special `sdkOptions` object argument to pass any supported option directly to the underlying SDK method. -```typescript hl_lines="8 12" title="Specify a VersionId for a secret" -import { SecretsProvider } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="8 14" title="Specify a VersionId for a secret" +import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; import type { GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; -const secrets = new SecretsProvider(); +const secretsProvider = new SecretsProvider(); export const handler = async (_event, _context): Promise => { - const sdkOptions: GetSecretValueCommandInput = { - VersionId: 'e62ec170-6b01-48c7-94f3-d7497851a8d2' - }; - // The 'VersionId' argument will be passed to the underlying - // `GetSecretValueCommand` call. - const value = secrets.get('my-secret', { sdkOptions }); + const sdkOptions: GetSecretValueCommandInput = { + VersionId: 'e62ec170-6b01-48c7-94f3-d7497851a8d2' + }; + /** + * The 'VersionId' argument will be passed to the underlying + * `GetSecretValueCommand` call. + */ + const secret = await secretsProvider.get('my-secret', { sdkOptions }); }; ``` Here is the mapping between this utility's functions and methods and the underlying SDK: -| Provider | Function/Method | Client name | Function name | -| ------------------- | ------------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -| SSM Parameter Store | `getParameter` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | -| SSM Parameter Store | `getParameters` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | -| SSM Parameter Store | `SSMProvider.get` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | -| SSM Parameter Store | `SSMProvider.getMultiple` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | -| Secrets Manager | `getSecret` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | -| Secrets Manager | `SecretsProvider.get` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | -| DynamoDB | `DynamoDBProvider.get` | `@aws-sdk/client-dynamodb` | [GetItemCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/getitemcommand.html) | -| DynamoDB | `DynamoDBProvider.getMultiple` | `@aws-sdk/client-dynamodb` | [QueryCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/querycommand.html) | -| App Config | `getAppConfig` | `@aws-sdk/client-appconfig` | [GetConfigurationCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfig/classes/getconfigurationcommand.html) | +| Provider | Function/Method | Client name | Function name | +| ------------------- | ------------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SSM Parameter Store | `getParameter` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | +| SSM Parameter Store | `getParameters` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | +| SSM Parameter Store | `SSMProvider.get` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | +| SSM Parameter Store | `SSMProvider.getMultiple` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | +| Secrets Manager | `getSecret` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | +| Secrets Manager | `SecretsProvider.get` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | +| AppConfig | `AppConfigProvider.get` | `@aws-sdk/client-appconfigdata` | [GetLatestConfigurationCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfigdata/classes/getlatestconfigurationcommand.html) | +| AppConfig | `getAppConfig` | `@aws-sdk/client-appconfigdata` | [GetLatestConfigurationCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfigdata/classes/getlatestconfigurationcommand.html) | +| DynamoDB | `DynamoDBProvider.get` | `@aws-sdk/client-dynamodb` | [GetItemCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/getitemcommand.html) | +| DynamoDB | `DynamoDBProvider.getMultiple` | `@aws-sdk/client-dynamodb` | [QueryCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/querycommand.html) | ### Bring your own AWS SDK v3 client -You can use `awsSdkV3Client` parameter via any of the available [Provider Classes](#built-in-provider-class). +You can use the `awsSdkV3Client` parameter via any of the available [Provider Classes](#built-in-provider-class). | Provider | Client | | --------------------------------------- | ----------------------------- | @@ -494,29 +583,52 @@ You can use `awsSdkV3Client` parameter via any of the available [Provider Classe | [AppConfigProvider](#appconfigprovider) | `new AppConfigDataClient();` | | [DynamoDBProvider](#dynamodbprovider) | `new DynamoDBClient();` | -Bringing them together in a single code snippet would look like this: - -```typescript title="Example: passing a custom boto3 client for each provider" -import { SSMProvider, SecretsProvider, DynamoDBProvider, AppConfigProvider } from '@aws-lambda-powertools/parameters'; -import { SSMClient } from '@aws-sdk/client-ssm'; -import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { AppConfigClient } from '@aws-sdk/client-appconfig'; - -// construct the clients with any custom configuration -const ssm = new SSMClient({ region: 'us-east-1' }); -const secretsManager = new SecretsManagerClient({ region: 'us-west-2' }); -const dynamodb = new DynamoDBClient({ region: 'eu-west-1' }); -const appConfig = new AppConfigClient({ region: 'eu-south-2' }); - -const parameters = new SSMProvider({ awsSdkV3Client: ssm }); -const secrets = new SecretsProvider({ awsSdkV3Client: secretsManager }); -const dynamodbProvider = new DynamoDBProvider({ awsSdkV3Client: dynamodb, tableName: 'my-table' }); -const appConfigProvider = new AppConfigProvider({ awsSdkV3Client: appConfig, environment: 'my-env', application: 'my-app' }); -``` - ???+ question "When is this useful?" - Injecting a custom AWS SDK v3 client can make unit/snapshot testing easier, including SDK customizations. + Injecting a custom AWS SDK v3 client allows you to [apply tracing](/core/tracer/#patching-aws-sdk-clients) or make unit/snapshot testing easier, including SDK customizations. + +=== "SSMProvider" + ```typescript hl_lines="5 7" + import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; + import { SSMClient } from '@aws-sdk/client-ssm'; + + // construct your clients with any custom configuration + const ssmClient = new SSMClient({ region: 'us-east-1' }); + // pass the client to the provider + const parametersProvider = new SSMProvider({ awsSdkV3Client: ssmClient }); + ``` + +=== "SecretsProvider" + ```typescript hl_lines="5 7" + import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; + import { SSMClient } from '@aws-sdk/client-secrets-manager'; + + // construct your clients with any custom configuration + const secretsManagerClient = new SecretsManagerClient({ region: 'us-east-1' }); + // pass the client to the provider + const secretsProvider = new SecretsProvider({ awsSdkV3Client: secretsManagerClient }); + ``` + +=== "AppConfigProvider" + ```typescript hl_lines="5 7" + import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig'; + import { AppConfigDataClient } from '@aws-sdk/client-appconfigdata'; + + // construct your clients with any custom configuration + const appConfigClient = new AppConfigDataClient({ region: 'us-east-1' }); + // pass the client to the provider + const configsProvider = new AppConfigProvider({ awsSdkV3Client: appConfigClient }); + ``` + +=== "DynamoDBProvider" + ```typescript hl_lines="5 7" + import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; + 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 }); + ``` ### Customizing AWS SDK v3 configuration @@ -528,15 +640,15 @@ The **`clientConfig`** parameter enables you to pass in a custom [config object] When using VPC private endpoints, you can pass a custom client altogether. It's also useful for testing when injecting fake instances. -```typescript hl_lines="2 4 5" -import { SSMProvider } from '@aws-lambda-powertools/parameters'; +```typescript hl_lines="2 4-5" +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; import type { SSMClientConfig } from '@aws-sdk/client-ssm'; const clientConfig: SSMClientConfig = { region: 'us-east-1' }; -const parameters = new SSMProvider({ clientConfig }); +const parametersProvider = new SSMProvider({ clientConfig }); export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const value = parameters.get('/my/parameter'); + // Retrieve a single parameter + const value = await parametersProvider.get('/my/parameter'); }; ``` From 87dd2bb2295402f22689fa8a256c123c984cb2ca Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 9 Jan 2023 17:42:21 +0100 Subject: [PATCH 7/8] docs: extract code snippets --- docs/snippets/parameters/adjustingCacheTTL.ts | 15 + docs/snippets/parameters/appConfigProvider.ts | 15 + .../appConfigProviderCustomClient.ts | 12 + docs/snippets/parameters/clientConfig.ts | 11 + docs/snippets/parameters/dynamoDBProvider.ts | 9 + .../dynamoDBProviderCustomClient.ts | 13 + .../dynamoDBProviderCustomizeTable.ts | 13 + .../parameters/dynamoDBProviderLocal.ts | 14 + .../parameters/dynamoDBProviderMultiple.ts | 16 + docs/snippets/parameters/forceFetch.ts | 7 + docs/snippets/parameters/getAppConfig.ts | 10 + docs/snippets/parameters/getParameter.ts | 7 + docs/snippets/parameters/getParameters.ts | 12 + .../parameters/getParametersByName.ts | 15 + ...etParametersByNameGracefulErrorHandling.ts | 22 ++ docs/snippets/parameters/getSecret.ts | 7 + docs/snippets/parameters/sdkOptions.ts | 16 + docs/snippets/parameters/secretsProvider.ts | 11 + .../parameters/secretsProviderCustomClient.ts | 13 + docs/snippets/parameters/ssmProvider.ts | 17 + .../parameters/ssmProviderCustomClient.ts | 13 + .../ssmProviderDecryptAndRecursive.ts | 13 + docs/snippets/parameters/transform.ts | 6 + docs/snippets/parameters/transformAuto.ts | 10 + .../parameters/transformPartialFailures.ts | 29 ++ docs/snippets/parameters/transformProvider.ts | 13 + docs/utilities/parameters.md | 311 ++---------------- 27 files changed, 365 insertions(+), 285 deletions(-) create mode 100644 docs/snippets/parameters/adjustingCacheTTL.ts create mode 100644 docs/snippets/parameters/appConfigProvider.ts create mode 100644 docs/snippets/parameters/appConfigProviderCustomClient.ts create mode 100644 docs/snippets/parameters/clientConfig.ts create mode 100644 docs/snippets/parameters/dynamoDBProvider.ts create mode 100644 docs/snippets/parameters/dynamoDBProviderCustomClient.ts create mode 100644 docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts create mode 100644 docs/snippets/parameters/dynamoDBProviderLocal.ts create mode 100644 docs/snippets/parameters/dynamoDBProviderMultiple.ts create mode 100644 docs/snippets/parameters/forceFetch.ts create mode 100644 docs/snippets/parameters/getAppConfig.ts create mode 100644 docs/snippets/parameters/getParameter.ts create mode 100644 docs/snippets/parameters/getParameters.ts create mode 100644 docs/snippets/parameters/getParametersByName.ts create mode 100644 docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts create mode 100644 docs/snippets/parameters/getSecret.ts create mode 100644 docs/snippets/parameters/sdkOptions.ts create mode 100644 docs/snippets/parameters/secretsProvider.ts create mode 100644 docs/snippets/parameters/secretsProviderCustomClient.ts create mode 100644 docs/snippets/parameters/ssmProvider.ts create mode 100644 docs/snippets/parameters/ssmProviderCustomClient.ts create mode 100644 docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts create mode 100644 docs/snippets/parameters/transform.ts create mode 100644 docs/snippets/parameters/transformAuto.ts create mode 100644 docs/snippets/parameters/transformPartialFailures.ts create mode 100644 docs/snippets/parameters/transformProvider.ts diff --git a/docs/snippets/parameters/adjustingCacheTTL.ts b/docs/snippets/parameters/adjustingCacheTTL.ts new file mode 100644 index 0000000000..2b006e18a4 --- /dev/null +++ b/docs/snippets/parameters/adjustingCacheTTL.ts @@ -0,0 +1,15 @@ +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; + +const parametersProvider = new SSMProvider(); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const parameter = await parametersProvider.get('/my/parameter', { maxAge: 60 }); // 1 minute + console.log(parameter); + + // Retrieve multiple parameters from a path prefix + const parameters = await parametersProvider.getMultiple('/my/path/prefix', { maxAge: 120 }); // 2 minutes + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/appConfigProvider.ts b/docs/snippets/parameters/appConfigProvider.ts new file mode 100644 index 0000000000..b5d4d88228 --- /dev/null +++ b/docs/snippets/parameters/appConfigProvider.ts @@ -0,0 +1,15 @@ +import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig'; +import type { AppConfigDataClientConfig } from '@aws-sdk/client-appconfigdata'; + +const clientConfig: AppConfigDataClientConfig = { region: 'us-east-1' }; +const configsProvider = new AppConfigProvider({ + application: 'my-app', + environment: 'my-env', + clientConfig, +}); + +export const handler = async (_event, _context): Promise => { + // Retrieve a config + const config = await configsProvider.get('my-config'); + console.log(config); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/appConfigProviderCustomClient.ts b/docs/snippets/parameters/appConfigProviderCustomClient.ts new file mode 100644 index 0000000000..1c5fe14097 --- /dev/null +++ b/docs/snippets/parameters/appConfigProviderCustomClient.ts @@ -0,0 +1,12 @@ +import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig'; +import { AppConfigDataClient } from '@aws-sdk/client-appconfigdata'; + +// construct your clients with any custom configuration +const appConfigClient = new AppConfigDataClient({ region: 'us-east-1' }); +// pass the client to the provider +const configsProvider = new AppConfigProvider({ awsSdkV3Client: appConfigClient }); + +export const handler = async (_event, _context): Promise => { + const config = await configsProvider.get('my-config'); + console.log(config); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/clientConfig.ts b/docs/snippets/parameters/clientConfig.ts new file mode 100644 index 0000000000..bfd9f76590 --- /dev/null +++ b/docs/snippets/parameters/clientConfig.ts @@ -0,0 +1,11 @@ +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; +import type { SSMClientConfig } from '@aws-sdk/client-ssm'; + +const clientConfig: SSMClientConfig = { region: 'us-east-1' }; +const parametersProvider = new SSMProvider({ clientConfig }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const value = await parametersProvider.get('/my/parameter'); + console.log(value); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/dynamoDBProvider.ts b/docs/snippets/parameters/dynamoDBProvider.ts new file mode 100644 index 0000000000..5938e94907 --- /dev/null +++ b/docs/snippets/parameters/dynamoDBProvider.ts @@ -0,0 +1,9 @@ +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; + +const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a value from DynamoDB + const value = await dynamoDBProvider.get('my-parameter'); + console.log(value); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/dynamoDBProviderCustomClient.ts b/docs/snippets/parameters/dynamoDBProviderCustomClient.ts new file mode 100644 index 0000000000..ef51039b1e --- /dev/null +++ b/docs/snippets/parameters/dynamoDBProviderCustomClient.ts @@ -0,0 +1,13 @@ +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; +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 }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single value + const value = await valuesProvider.get('my-value'); + console.log(value); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts b/docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts new file mode 100644 index 0000000000..001d23aa2f --- /dev/null +++ b/docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts @@ -0,0 +1,13 @@ +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; + +const dynamoDBProvider = new DynamoDBProvider({ + tableName:'my-table', + keyAttr:'key', + sortAttr:'sort', + valueAttr:'val' +}); + +export const handler = async (_event, _context): Promise => { + const value = await dynamoDBProvider.get('my-parameter'); + console.log(value); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/dynamoDBProviderLocal.ts b/docs/snippets/parameters/dynamoDBProviderLocal.ts new file mode 100644 index 0000000000..cd724e5dc9 --- /dev/null +++ b/docs/snippets/parameters/dynamoDBProviderLocal.ts @@ -0,0 +1,14 @@ +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; + +const dynamoDBProvider = new DynamoDBProvider({ + tableName: 'my-table', + clientConfig: { + endpoint: 'http://localhost:8000' + }, +}); + +export const handler = async (_event, _context): Promise => { + // Retrieve a value from DynamoDB + const value = await dynamoDBProvider.get('my-parameter'); + console.log(value); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/dynamoDBProviderMultiple.ts b/docs/snippets/parameters/dynamoDBProviderMultiple.ts new file mode 100644 index 0000000000..9d12b6578e --- /dev/null +++ b/docs/snippets/parameters/dynamoDBProviderMultiple.ts @@ -0,0 +1,16 @@ +import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; + +const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); + +export const handler = async (_event, _context): Promise => { + /** + * Retrieve multiple values by performing a Query on the DynamoDB table. + * This returns a dict with the sort key attribute as dict key. + */ + const values = await dynamoDBProvider.getMultiple('my-hash-key'); + for (const [ key, value ] of Object.entries(values)) { + // key: param-a + // value: my-value-a + console.log(`${key}: ${value}`); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/forceFetch.ts b/docs/snippets/parameters/forceFetch.ts new file mode 100644 index 0000000000..a0a5f7019e --- /dev/null +++ b/docs/snippets/parameters/forceFetch.ts @@ -0,0 +1,7 @@ +import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const parameter = await getParameter('/my/parameter', { forceFetch: true }); + console.log(parameter); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/getAppConfig.ts b/docs/snippets/parameters/getAppConfig.ts new file mode 100644 index 0000000000..efd4eda26a --- /dev/null +++ b/docs/snippets/parameters/getAppConfig.ts @@ -0,0 +1,10 @@ +import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig'; + +export const handler = async (_event, _context): Promise => { + // Retrieve a configuration, latest version + const config = await getAppConfig('my-configuration', { + environment: 'my-env', + application: 'my-app' + }); + console.log(config); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/getParameter.ts b/docs/snippets/parameters/getParameter.ts new file mode 100644 index 0000000000..3c1d6eb251 --- /dev/null +++ b/docs/snippets/parameters/getParameter.ts @@ -0,0 +1,7 @@ +import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const parameter = await getParameter('/my/parameter'); + console.log(parameter); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/getParameters.ts b/docs/snippets/parameters/getParameters.ts new file mode 100644 index 0000000000..8702a6faf5 --- /dev/null +++ b/docs/snippets/parameters/getParameters.ts @@ -0,0 +1,12 @@ +import { getParameters } from '@aws-lambda-powertools/parameters/ssm'; + +export const handler = async (_event, _context): Promise => { + /** + * Retrieve multiple parameters from a path prefix recursively. + * This returns an object with the parameter name as key + */ + const parameters = await getParameters('/my/path/prefix'); + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/getParametersByName.ts b/docs/snippets/parameters/getParametersByName.ts new file mode 100644 index 0000000000..cc9c162f04 --- /dev/null +++ b/docs/snippets/parameters/getParametersByName.ts @@ -0,0 +1,15 @@ +import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; + +const props = { + '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, + '/no_cache_param': { maxAge: 0 }, + '/develop/service/payment/api/capture/url': {}, // When empty or undefined, it uses default values +}; + +export const handler = async (_event, _context): Promise => { + // This returns an object with the parameter name as key + const parameters = await getParametersByName(props, { maxAge: 60 }); + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts b/docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts new file mode 100644 index 0000000000..17b042b660 --- /dev/null +++ b/docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts @@ -0,0 +1,22 @@ +import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; + +const props = { + '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, + '/this/param/does/not/exist': {}, // <- Example of non-existent parameter +}; + +export const handler = async (_event, _context): Promise => { + const { + _errors: errors, + ...parameters + } = await getParametersByName(props, { throwOnError: false }); + + // Handle gracefully, since `/this/param/does/not/exist` will only be available in `_errors` + if (errors && errors.length) { + console.error(`Unable to retrieve parameters: ${errors.join(',')}`); + } + + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/getSecret.ts b/docs/snippets/parameters/getSecret.ts new file mode 100644 index 0000000000..deb62222d8 --- /dev/null +++ b/docs/snippets/parameters/getSecret.ts @@ -0,0 +1,7 @@ +import { getSecret } from '@aws-lambda-powertools/parameters/secrets'; + +export const handler = async (_event, _context): Promise => { + // Retrieve a single secret + const secret = await getSecret('my-secret'); + console.log(secret); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/sdkOptions.ts b/docs/snippets/parameters/sdkOptions.ts new file mode 100644 index 0000000000..ee35ab51a2 --- /dev/null +++ b/docs/snippets/parameters/sdkOptions.ts @@ -0,0 +1,16 @@ +import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; +import type { GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; + +const secretsProvider = new SecretsProvider(); + +export const handler = async (_event, _context): Promise => { + const sdkOptions: Partial = { + VersionId: 'e62ec170-6b01-48c7-94f3-d7497851a8d2' + }; + /** + * The 'VersionId' argument will be passed to the underlying + * `GetSecretValueCommand` call. + */ + const secret = await secretsProvider.get('my-secret', { sdkOptions }); + console.log(secret); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/secretsProvider.ts b/docs/snippets/parameters/secretsProvider.ts new file mode 100644 index 0000000000..a9337f4842 --- /dev/null +++ b/docs/snippets/parameters/secretsProvider.ts @@ -0,0 +1,11 @@ +import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; +import type { SecretsManagerClientConfig } from '@aws-sdk/client-secretsmanager'; + +const clientConfig: SecretsManagerClientConfig = { region: 'us-east-1' }; +const secretsProvider = new SecretsProvider({ clientConfig }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single secret + const secret = await secretsProvider.get('my-secret'); + console.log(secret); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/secretsProviderCustomClient.ts b/docs/snippets/parameters/secretsProviderCustomClient.ts new file mode 100644 index 0000000000..a14db58b10 --- /dev/null +++ b/docs/snippets/parameters/secretsProviderCustomClient.ts @@ -0,0 +1,13 @@ +import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; +import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager'; + +// construct your clients with any custom configuration +const secretsManagerClient = new SecretsManagerClient({ region: 'us-east-1' }); +// pass the client to the provider +const secretsProvider = new SecretsProvider({ awsSdkV3Client: secretsManagerClient }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single secret + const secret = await secretsProvider.get('my-secret'); + console.log(secret); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/ssmProvider.ts b/docs/snippets/parameters/ssmProvider.ts new file mode 100644 index 0000000000..6e22ad97a6 --- /dev/null +++ b/docs/snippets/parameters/ssmProvider.ts @@ -0,0 +1,17 @@ +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; +import type { SSMClientConfig } from '@aws-sdk/client-ssm'; + +const clientConfig: SSMClientConfig = { region: 'us-east-1' }; +const parametersProvider = new SSMProvider({ clientConfig }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const parameter = await parametersProvider.get('/my/parameter'); + console.log(parameter); + + // Retrieve multiple parameters from a path prefix + const parameters = await parametersProvider.getMultiple('/my/path/prefix'); + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/ssmProviderCustomClient.ts b/docs/snippets/parameters/ssmProviderCustomClient.ts new file mode 100644 index 0000000000..5ef4278c6e --- /dev/null +++ b/docs/snippets/parameters/ssmProviderCustomClient.ts @@ -0,0 +1,13 @@ +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; +import { SSMClient } from '@aws-sdk/client-ssm'; + +// construct your clients with any custom configuration +const ssmClient = new SSMClient({ region: 'us-east-1' }); +// pass the client to the provider +const parametersProvider = new SSMProvider({ awsSdkV3Client: ssmClient }); + +export const handler = async (_event, _context): Promise => { + // Retrieve a single parameter + const parameter = await parametersProvider.get('/my/parameter'); + console.log(parameter); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts b/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts new file mode 100644 index 0000000000..15b7b777aa --- /dev/null +++ b/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts @@ -0,0 +1,13 @@ +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; + +const parametersProvider = new SSMProvider(); + +export const handler = async (_event, _context): Promise => { + const decryptedValue = await parametersProvider.get('/my/encrypted/parameter', { decrypt: true }); + console.log(decryptedValue); + + const noRecursiveValues = await parametersProvider.getMultiple('/my/path/prefix', { recursive: false }); + for (const [ key, value ] of Object.entries(noRecursiveValues)) { + console.log(`${key}: ${value}`); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/transform.ts b/docs/snippets/parameters/transform.ts new file mode 100644 index 0000000000..194b6c9164 --- /dev/null +++ b/docs/snippets/parameters/transform.ts @@ -0,0 +1,6 @@ +import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; + +export const handler = async (_event, _context): Promise => { + const valueFromJson = await getParameter('/my/json/parameter', { transform: 'json' }); + console.log(valueFromJson); +}; \ No newline at end of file diff --git a/docs/snippets/parameters/transformAuto.ts b/docs/snippets/parameters/transformAuto.ts new file mode 100644 index 0000000000..ca9a150d90 --- /dev/null +++ b/docs/snippets/parameters/transformAuto.ts @@ -0,0 +1,10 @@ +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; + +const parametersProvider = new SSMProvider(); + +export const handler = async (_event, _context): Promise => { + const values = await parametersProvider.getMultiple('/param', { transform: 'auto' }); + for (const [ key, value ] of Object.entries(values)) { + console.log(`${key}: ${value}`); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/transformPartialFailures.ts b/docs/snippets/parameters/transformPartialFailures.ts new file mode 100644 index 0000000000..dfdc9a9384 --- /dev/null +++ b/docs/snippets/parameters/transformPartialFailures.ts @@ -0,0 +1,29 @@ +import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; + +const parametersProvider = new SSMProvider(); + +export const handler = async (_event, _context): Promise => { + /** + * This will display: + * /param/a: [some value] + * /param/b: [some value] + * /param/c: undefined + */ + const parameters = await parametersProvider.getMultiple('/param', { transform: 'json' }); + for (const [ key, value ] of Object.entries(parameters)) { + console.log(`${key}: ${value}`); + } + + try { + // This will throw a TransformParameterError + const parameters2 = await parametersProvider.getMultiple('/param', { + transform: 'json', + throwOnTransformError: true + }); + for (const [ key, value ] of Object.entries(parameters2)) { + console.log(`${key}: ${value}`); + } + } catch (err) { + console.error(err); + } +}; \ No newline at end of file diff --git a/docs/snippets/parameters/transformProvider.ts b/docs/snippets/parameters/transformProvider.ts new file mode 100644 index 0000000000..14974ad3b7 --- /dev/null +++ b/docs/snippets/parameters/transformProvider.ts @@ -0,0 +1,13 @@ +import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; + +const secretsProvider = new SecretsProvider(); + +export const handler = async (_event, _context): Promise => { + // Transform a JSON string + const json = await secretsProvider.get('my-secret-json', { transform: 'json' }); + console.log(json); + + // Transform a Base64 encoded string (e.g. binary) + const binary = await secretsProvider.getMultiple('my-secret-binary', { transform: 'binary' }); + console.log(binary); +}; \ No newline at end of file diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 1be0a49777..6f7264951d 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -72,12 +72,7 @@ This utility requires additional permissions to work as expected. You can retrieve a single parameter using the `getParameter` high-level function. ```typescript hl_lines="1 5" title="Fetching a single parameter from SSM" -import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; - -export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const parameter = await getParameter('/my/parameter'); -}; +--8<-- "docs/snippets/parameters/getParameter.ts" ``` For multiple parameters, you can use either: @@ -88,38 +83,13 @@ For multiple parameters, you can use either: === "getParameters" ```typescript hl_lines="1 8" title="Fetching multiple parameters by path from SSM" - import { getParameters } from '@aws-lambda-powertools/parameters/ssm'; - - export const handler = async (_event, _context): Promise => { - /** - * Retrieve multiple parameters from a path prefix recursively. - * This returns an object with the parameter name as key - */ - const parameters = await getParameters('/my/path/prefix'); - for (const [ key, value ] of Object.entries(parameters)) { - console.log(`${key}: ${value}`); - } - }; + --8<-- "docs/snippets/parameters/getParameters.ts" ``` === "getParametersByName" ```typescript hl_lines="1 4-6 11" title="Fetching multiple parameters by names from SSM" - import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; - - const props = { - '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, - '/no_cache_param': { maxAge: 0 }, - '/develop/service/payment/api/capture/url': {}, // When empty or undefined, it uses default values - }; - - export const handler = async (_event, _context): Promise => { - // This returns an object with the parameter name as key - const parameters = await getParametersByName(props, { maxAge: 60 }); - for (const [ key, value ] of Object.entries(parameters)) { - console.log(`${key}: ${value}`); - } - }; + --8<-- "docs/snippets/parameters/getParametersByName.ts" ``` ???+ tip "`getParametersByName` supports graceful error handling" @@ -132,28 +102,7 @@ For multiple parameters, you can use either: * Throw `GetParameterError` if any of your parameters is named `_errors` ```typescript hl_lines="1 4-5 10 15" -import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; - -const props = { - '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, - '/this/param/does/not/exist': {}, // <- Example of non-existent parameter -}; - -export const handler = async (_event, _context): Promise => { - const { - _errors: errors, - ...parameters - } = await getParametersByName(props, { throwOnError: false }); - - // Handle gracefully, since `/this/param/does/not/exist` will only be available in `_errors` - if (errors && errors.length) { - console.error(`Unable to retrieve parameters: ${errors.join(',')}`); - } - - for (const [ key, value ] of Object.entries(parameters)) { - console.log(`${key}: ${value}`); - } -}; +--8<-- "docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts" ``` ### Fetching secrets @@ -161,12 +110,7 @@ export const handler = async (_event, _context): Promise => { You can fetch secrets stored in Secrets Manager using `getSecrets`. ```typescript hl_lines="1 5" title="Fetching secrets" -import { getSecret } from '@aws-lambda-powertools/parameters/secrets'; - -export const handler = async (_event, _context): Promise => { - // Retrieve a single secret - const secret = await getSecret('my-secret'); -}; +--8<-- "docs/snippets/parameters/getSecret.ts" ``` ### Fetching app configurations @@ -176,15 +120,7 @@ You can fetch application configurations in AWS AppConfig using `getAppConfig`. The following will retrieve the latest version and store it in the cache. ```typescript hl_lines="1 5-8" title="Fetching latest config from AppConfig" -import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig'; - -export const handler = async (_event, _context): Promise => { - // Retrieve a configuration, latest version - const config = await getAppConfig('my-configuration', { - environment: 'my-env', - application: 'my-app' - }); -}; +--8<-- "docs/snippets/parameters/getAppConfig.ts" ``` ## Advanced @@ -199,20 +135,7 @@ By default, we cache parameters retrieved in-memory for 5 seconds. You can adjust how long we should keep values in cache by using the param `maxAge`, when using `get()` or `getMultiple()` methods across all providers. ```typescript hl_lines="7 10" title="Caching parameters values in memory for longer than 5 seconds" -import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; - -const parametersProvider = new SSMProvider(); - -export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const parameter = await parametersProvider.get('/my/parameter', { maxAge: 60 }); // 1 minute - - // Retrieve multiple parameters from a path prefix - const parameters = await parametersProvider.getMultiple('/my/path/prefix', { maxAge: 120 }); // 2 minutes - for (const [ key, value ] of Object.entries(parameters)) { - console.log(`${key}: ${value}`); - } -}; +--8<-- "docs/snippets/parameters/adjustingCacheTTL.ts" ``` ### Always fetching the latest @@ -220,12 +143,7 @@ export const handler = async (_event, _context): Promise => { If you'd like to always ensure you fetch the latest parameter from the store regardless if already available in cache, use the `forceFetch` parameter. ```typescript hl_lines="5" title="Forcefully fetching the latest parameter whether TTL has expired or not" -import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; - -export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const parameter = await getParameter('/my/parameter', { forceFetch: true }); -}; +--8<-- "docs/snippets/parameters/forceFetch.ts" ``` ### Built-in provider class @@ -238,22 +156,7 @@ For greater flexibility such as configuring the underlying SDK client used by bu #### SSMProvider ```typescript hl_lines="4-5" title="Example with SSMProvider for further extensibility" -import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; -import type { SSMClientConfig } from '@aws-sdk/client-ssm'; - -const clientConfig: SSMClientConfig = { region: 'us-east-1' }; -const parametersProvider = new SSMProvider({ clientConfig }); - -export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const parameter = await parametersProvider.get('/my/parameter'); - - // Retrieve multiple parameters from a path prefix - const parameters = await parametersProvider.getMultiple('/my/path/prefix'); - for (const [ key, value ] of Object.entries(parameters)) { - console.log(`${key}: ${value}`); - } -}; +--8<-- "docs/snippets/parameters/ssmProvider.ts" ``` The AWS Systems Manager Parameter Store provider supports two additional arguments for the `get()` and `getMultiple()` methods: @@ -264,30 +167,13 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen | **recursive** | `true` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. | ```typescript hl_lines="6 8" title="Example with get() and getMultiple()" -import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; - -const parametersProvider = new SSMProvider(); - -export const handler = async (_event, _context): Promise => { - const decryptedValue = await parametersProvider.get('/my/encrypted/parameter', { decrypt: true }); - - const noRecursiveValues = await parametersProvider.getMultiple('/my/path/prefix', { recursive: false }); -}; +--8<-- "docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts" ``` #### SecretsProvider ```typescript hl_lines="4-5" title="Example with SecretsProvider for further extensibility" -import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; -import type { SecretsManagerClientConfig } from '@aws-sdk/client-secretsmanager'; - -const clientConfig: SecretsManagerClientConfig = { region: 'us-east-1' }; -const secretsProvider = new SecretsProvider({ clientConfig }); - -export const handler = async (_event, _context): Promise => { - // Retrieve a single secret - const secret = await secretsProvider.get('my-secret'); -}; +--8<-- "docs/snippets/parameters/secretsProvider.ts" ``` #### AppConfigProvider @@ -300,20 +186,7 @@ The AWS AppConfig provider requires two arguments when initialized: | **environment** | Yes | _(N/A)_ | The environment that corresponds to your current config. | ```typescript hl_lines="4 8" title="Example with AppConfigProvider for further extensibility" -import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig'; -import type { AppConfigDataClientConfig } from '@aws-sdk/client-appconfigdata'; - -const clientConfig: AppConfigDataClientConfig = { region: 'us-east-1' }; -const configsProvider = new AppConfigProvider({ - application: 'my-app', - environment: 'my-env', - clientConfig, -}); - -export const handler = async (_event, _context): Promise => { - // Retrieve a config - const config = await configsProvider.get('my-config'); -}; +--8<-- "docs/snippets/parameters/appConfigProvider.ts" ``` #### DynamoDBProvider @@ -336,28 +209,14 @@ With this table, `await dynamoDBProvider.get('my-param')` will return `my-value` === "handler.ts" ```typescript hl_lines="3 7" - import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - - const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); - - export const handler = async (_event, _context): Promise => { - // Retrieve a value from DynamoDB - const value = await dynamoDBProvider.get('my-parameter'); - }; + --8<-- "docs/snippets/parameters/dynamoDBProvider.ts" ``` === "DynamoDB Local example" You can initialize the DynamoDB provider pointing to [DynamoDB Local](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) using the `endpoint` field in the `clientConfig` parameter: ```typescript hl_lines="5-7" - import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - - const dynamoDBProvider = new DynamoDBProvider({ - tableName: 'my-table', - clientConfig: { - endpoint: 'http://localhost:8000' - }, - }); + --8<-- "docs/snippets/parameters/dynamoDBProviderLocal.ts" ``` **DynamoDB table structure for multiple values parameters** @@ -378,22 +237,7 @@ With this table, `await dynamoDBProvider.getMultiple('my-hash-key')` will return === "handler.ts" ```typescript hl_lines="3 10" - import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - - const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); - - export const handler = async (_event, _context): Promise => { - /** - * Retrieve multiple values by performing a Query on the DynamoDB table. - * This returns a dict with the sort key attribute as dict key. - */ - const values = await dynamoDBProvider.getMultiple('my-hash-key'); - for (const [ key, value ] of Object.entries(values)) { - // key: param-a - // value: my-value-a - console.log(`${key}: ${value}`); - } - }; + --8<-- "docs/snippets/parameters/dynamoDBProviderMultiple.ts" ``` === "values response object" @@ -418,18 +262,7 @@ DynamoDB provider can be customized at initialization to match your table struct | **valueAttr** | No | `value` | Name of the attribute containing the parameter value. | ```typescript hl_lines="3-8" title="Customizing DynamoDBProvider to suit your table design" -import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - -const dynamoDBProvider = new DynamoDBProvider({ - tableName:'my-table', - keyAttr:'key', - sortAttr:'sort', - valueAttr:'val' -}); - -export const handler = async (_event, _context): Promise => { - const value = await dynamoDBProvider.get('my-parameter'); -}; +--8<-- "docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts" ``` ### Deserializing values with transform parameter @@ -441,26 +274,12 @@ For parameters stored in JSON or Base64 format, you can use the `transform` argu === "High level functions" ```typescript hl_lines="4" - import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; - - export const handler = async (_event, _context): Promise => { - const valueFromJson = await getParameter('/my/json/parameter', { transform: 'json' }); - }; + --8<-- "docs/snippets/parameters/transform.ts" ``` === "Providers" ```typescript hl_lines="7 10" - import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; - - const secretsProvider = new SecretsProvider(); - - export const handler = async (_event, _context): Promise => { - // Transform a JSON string - const json = await secretsProvider.get('my-secret-json', { transform: 'json' }); - - // Transform a Base64 encoded string (e.g. binary) - const binary = await secretsProvider.getMultiple('my-secret-binary', { transform: 'binary' }); - }; + --8<-- "docs/snippets/parameters/transformProvider.ts" ``` #### Partial transform failures with `getMultiple()` @@ -472,32 +291,7 @@ You can override this by setting the `throwOnTransformError` argument to `true`. For example, if you have three parameters, */param/a*, */param/b* and */param/c*, but */param/c* is malformed: ```typescript hl_lines="19-22" title="Throwing TransformParameterError at first malformed parameter" -import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; - -const parametersProvider = new SSMProvider(); - -export const handler = async (_event, _context): Promise => { - /** - * This will display: - * /param/a: [some value] - * /param/b: [some value] - * /param/c: undefined - */ - const parameters = await parametersProvider.getMultiple('/param', { transform: 'json' }); - for (const [ key, value ] of Object.entries(parameters)) { - console.log(`${key}: ${value}`); - } - - try { - // This will throw a TransformParameterError - const parameters2 = await parametersProvider.getMultiple('/param', { - transform: 'json', - throwOnTransformError: true - }); - } catch (err) { - console.error(err); - } -}; +--8<-- "docs/snippets/parameters/transformPartialFailures.ts" ``` #### Auto-transform values on suffix @@ -510,13 +304,7 @@ You can do this with a single request by using `transform: 'auto'`. This will in `transform: 'auto'` feature is available across all providers, including the high level functions. ```typescript hl_lines="6" title="Deserializing parameter values based on their suffix" -import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; - -const parametersProvider = new SSMProvider(); - -export const handler = async (_event, _context): Promise => { - const values = await parametersProvider.getMultiple('/param', { transform: 'auto' }); -}; +--8<-- "docs/snippets/parameters/transformAuto.ts" ``` For example, if you have two parameters with the following suffixes `.json` and `.binary`: @@ -540,21 +328,7 @@ The return of `await parametersProvider.getMultiple('/param', transform: 'auto') You can use a special `sdkOptions` object argument to pass any supported option directly to the underlying SDK method. ```typescript hl_lines="8 14" title="Specify a VersionId for a secret" -import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; -import type { GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager'; - -const secretsProvider = new SecretsProvider(); - -export const handler = async (_event, _context): Promise => { - const sdkOptions: GetSecretValueCommandInput = { - VersionId: 'e62ec170-6b01-48c7-94f3-d7497851a8d2' - }; - /** - * The 'VersionId' argument will be passed to the underlying - * `GetSecretValueCommand` call. - */ - const secret = await secretsProvider.get('my-secret', { sdkOptions }); -}; +--8<-- "docs/snippets/parameters/sdkOptions.ts" ``` Here is the mapping between this utility's functions and methods and the underlying SDK: @@ -588,46 +362,22 @@ You can use the `awsSdkV3Client` parameter via any of the available [Provider Cl === "SSMProvider" ```typescript hl_lines="5 7" - import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; - import { SSMClient } from '@aws-sdk/client-ssm'; - - // construct your clients with any custom configuration - const ssmClient = new SSMClient({ region: 'us-east-1' }); - // pass the client to the provider - const parametersProvider = new SSMProvider({ awsSdkV3Client: ssmClient }); + --8<-- "docs/snippets/parameters/ssmProviderCustomClient.ts" ``` === "SecretsProvider" ```typescript hl_lines="5 7" - import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; - import { SSMClient } from '@aws-sdk/client-secrets-manager'; - - // construct your clients with any custom configuration - const secretsManagerClient = new SecretsManagerClient({ region: 'us-east-1' }); - // pass the client to the provider - const secretsProvider = new SecretsProvider({ awsSdkV3Client: secretsManagerClient }); + --8<-- "docs/snippets/parameters/secretsProviderCustomClient.ts" ``` === "AppConfigProvider" ```typescript hl_lines="5 7" - import { AppConfigProvider } from '@aws-lambda-powertools/parameters/appconfig'; - import { AppConfigDataClient } from '@aws-sdk/client-appconfigdata'; - - // construct your clients with any custom configuration - const appConfigClient = new AppConfigDataClient({ region: 'us-east-1' }); - // pass the client to the provider - const configsProvider = new AppConfigProvider({ awsSdkV3Client: appConfigClient }); + --8<-- "docs/snippets/parameters/appConfigProviderCustomClient.ts" ``` === "DynamoDBProvider" ```typescript hl_lines="5 7" - import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; - 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 }); + --8<-- "docs/snippets/parameters/dynamoDBProviderCustomClient.ts" ``` ### Customizing AWS SDK v3 configuration @@ -641,14 +391,5 @@ The **`clientConfig`** parameter enables you to pass in a custom [config object] ```typescript hl_lines="2 4-5" -import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; -import type { SSMClientConfig } from '@aws-sdk/client-ssm'; - -const clientConfig: SSMClientConfig = { region: 'us-east-1' }; -const parametersProvider = new SSMProvider({ clientConfig }); - -export const handler = async (_event, _context): Promise => { - // Retrieve a single parameter - const value = await parametersProvider.get('/my/parameter'); -}; +--8<-- "docs/snippets/parameters/clientConfig.ts" ``` From a8ee8d16a2a1c9f9463e0d8bca3406ed6b9953c7 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Tue, 10 Jan 2023 16:41:23 +0100 Subject: [PATCH 8/8] docs: review comments --- docs/snippets/parameters/adjustingCacheTTL.ts | 4 +- docs/snippets/parameters/appConfigProvider.ts | 2 +- .../appConfigProviderCustomClient.ts | 2 +- docs/snippets/parameters/clientConfig.ts | 2 +- docs/snippets/parameters/dynamoDBProvider.ts | 2 +- .../dynamoDBProviderCustomClient.ts | 2 +- .../dynamoDBProviderCustomizeTable.ts | 2 +- .../parameters/dynamoDBProviderLocal.ts | 2 +- .../parameters/dynamoDBProviderMultiple.ts | 4 +- docs/snippets/parameters/forceFetch.ts | 2 +- docs/snippets/parameters/getAppConfig.ts | 2 +- docs/snippets/parameters/getParameter.ts | 2 +- docs/snippets/parameters/getParameters.ts | 4 +- .../parameters/getParametersByName.ts | 7 ++- ...etParametersByNameGracefulErrorHandling.ts | 7 ++- docs/snippets/parameters/getSecret.ts | 2 +- docs/snippets/parameters/sdkOptions.ts | 2 +- docs/snippets/parameters/secretsProvider.ts | 2 +- .../parameters/secretsProviderCustomClient.ts | 2 +- docs/snippets/parameters/ssmProvider.ts | 4 +- .../parameters/ssmProviderCustomClient.ts | 2 +- .../ssmProviderDecryptAndRecursive.ts | 4 +- docs/snippets/parameters/transform.ts | 2 +- docs/snippets/parameters/transformAuto.ts | 4 +- .../parameters/transformPartialFailures.ts | 6 +-- docs/snippets/parameters/transformProvider.ts | 2 +- docs/snippets/parameters/tsconfig.json | 48 +++++++++++++++++ docs/utilities/parameters.md | 52 ++++++++++--------- 28 files changed, 118 insertions(+), 60 deletions(-) create mode 100644 docs/snippets/parameters/tsconfig.json diff --git a/docs/snippets/parameters/adjustingCacheTTL.ts b/docs/snippets/parameters/adjustingCacheTTL.ts index 2b006e18a4..795c1b01d4 100644 --- a/docs/snippets/parameters/adjustingCacheTTL.ts +++ b/docs/snippets/parameters/adjustingCacheTTL.ts @@ -2,14 +2,14 @@ import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; const parametersProvider = new SSMProvider(); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single parameter const parameter = await parametersProvider.get('/my/parameter', { maxAge: 60 }); // 1 minute console.log(parameter); // Retrieve multiple parameters from a path prefix const parameters = await parametersProvider.getMultiple('/my/path/prefix', { maxAge: 120 }); // 2 minutes - for (const [ key, value ] of Object.entries(parameters)) { + for (const [ key, value ] of Object.entries(parameters || {})) { console.log(`${key}: ${value}`); } }; \ No newline at end of file diff --git a/docs/snippets/parameters/appConfigProvider.ts b/docs/snippets/parameters/appConfigProvider.ts index b5d4d88228..048e49927e 100644 --- a/docs/snippets/parameters/appConfigProvider.ts +++ b/docs/snippets/parameters/appConfigProvider.ts @@ -8,7 +8,7 @@ const configsProvider = new AppConfigProvider({ clientConfig, }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a config const config = await configsProvider.get('my-config'); console.log(config); diff --git a/docs/snippets/parameters/appConfigProviderCustomClient.ts b/docs/snippets/parameters/appConfigProviderCustomClient.ts index 1c5fe14097..361d72dcc2 100644 --- a/docs/snippets/parameters/appConfigProviderCustomClient.ts +++ b/docs/snippets/parameters/appConfigProviderCustomClient.ts @@ -6,7 +6,7 @@ const appConfigClient = new AppConfigDataClient({ region: 'us-east-1' }); // pass the client to the provider const configsProvider = new AppConfigProvider({ awsSdkV3Client: appConfigClient }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { const config = await configsProvider.get('my-config'); console.log(config); }; \ No newline at end of file diff --git a/docs/snippets/parameters/clientConfig.ts b/docs/snippets/parameters/clientConfig.ts index bfd9f76590..83a4560e73 100644 --- a/docs/snippets/parameters/clientConfig.ts +++ b/docs/snippets/parameters/clientConfig.ts @@ -4,7 +4,7 @@ import type { SSMClientConfig } from '@aws-sdk/client-ssm'; const clientConfig: SSMClientConfig = { region: 'us-east-1' }; const parametersProvider = new SSMProvider({ clientConfig }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single parameter const value = await parametersProvider.get('/my/parameter'); console.log(value); diff --git a/docs/snippets/parameters/dynamoDBProvider.ts b/docs/snippets/parameters/dynamoDBProvider.ts index 5938e94907..af8a323a73 100644 --- a/docs/snippets/parameters/dynamoDBProvider.ts +++ b/docs/snippets/parameters/dynamoDBProvider.ts @@ -2,7 +2,7 @@ import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a value from DynamoDB const value = await dynamoDBProvider.get('my-parameter'); console.log(value); diff --git a/docs/snippets/parameters/dynamoDBProviderCustomClient.ts b/docs/snippets/parameters/dynamoDBProviderCustomClient.ts index ef51039b1e..16e610d3ac 100644 --- a/docs/snippets/parameters/dynamoDBProviderCustomClient.ts +++ b/docs/snippets/parameters/dynamoDBProviderCustomClient.ts @@ -6,7 +6,7 @@ const dynamoDBClient = new DynamoDBClient({ region: 'us-east-1' }); // pass the client to the provider const valuesProvider = new DynamoDBProvider({ awsSdkV3Client: dynamoDBClient }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single value const value = await valuesProvider.get('my-value'); console.log(value); diff --git a/docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts b/docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts index 001d23aa2f..86bbdc1916 100644 --- a/docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts +++ b/docs/snippets/parameters/dynamoDBProviderCustomizeTable.ts @@ -7,7 +7,7 @@ const dynamoDBProvider = new DynamoDBProvider({ valueAttr:'val' }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { const value = await dynamoDBProvider.get('my-parameter'); console.log(value); }; \ No newline at end of file diff --git a/docs/snippets/parameters/dynamoDBProviderLocal.ts b/docs/snippets/parameters/dynamoDBProviderLocal.ts index cd724e5dc9..7cee7a0476 100644 --- a/docs/snippets/parameters/dynamoDBProviderLocal.ts +++ b/docs/snippets/parameters/dynamoDBProviderLocal.ts @@ -7,7 +7,7 @@ const dynamoDBProvider = new DynamoDBProvider({ }, }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a value from DynamoDB const value = await dynamoDBProvider.get('my-parameter'); console.log(value); diff --git a/docs/snippets/parameters/dynamoDBProviderMultiple.ts b/docs/snippets/parameters/dynamoDBProviderMultiple.ts index 9d12b6578e..d7bcea66ba 100644 --- a/docs/snippets/parameters/dynamoDBProviderMultiple.ts +++ b/docs/snippets/parameters/dynamoDBProviderMultiple.ts @@ -2,13 +2,13 @@ import { DynamoDBProvider } from '@aws-lambda-powertools/parameters/dynamodb'; const dynamoDBProvider = new DynamoDBProvider({ tableName: 'my-table' }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { /** * Retrieve multiple values by performing a Query on the DynamoDB table. * This returns a dict with the sort key attribute as dict key. */ const values = await dynamoDBProvider.getMultiple('my-hash-key'); - for (const [ key, value ] of Object.entries(values)) { + for (const [ key, value ] of Object.entries(values || {})) { // key: param-a // value: my-value-a console.log(`${key}: ${value}`); diff --git a/docs/snippets/parameters/forceFetch.ts b/docs/snippets/parameters/forceFetch.ts index a0a5f7019e..a7de4195ac 100644 --- a/docs/snippets/parameters/forceFetch.ts +++ b/docs/snippets/parameters/forceFetch.ts @@ -1,6 +1,6 @@ import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single parameter const parameter = await getParameter('/my/parameter', { forceFetch: true }); console.log(parameter); diff --git a/docs/snippets/parameters/getAppConfig.ts b/docs/snippets/parameters/getAppConfig.ts index efd4eda26a..37cfd38108 100644 --- a/docs/snippets/parameters/getAppConfig.ts +++ b/docs/snippets/parameters/getAppConfig.ts @@ -1,6 +1,6 @@ import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig'; -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a configuration, latest version const config = await getAppConfig('my-configuration', { environment: 'my-env', diff --git a/docs/snippets/parameters/getParameter.ts b/docs/snippets/parameters/getParameter.ts index 3c1d6eb251..ed32576d93 100644 --- a/docs/snippets/parameters/getParameter.ts +++ b/docs/snippets/parameters/getParameter.ts @@ -1,6 +1,6 @@ import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single parameter const parameter = await getParameter('/my/parameter'); console.log(parameter); diff --git a/docs/snippets/parameters/getParameters.ts b/docs/snippets/parameters/getParameters.ts index 8702a6faf5..10484e13f6 100644 --- a/docs/snippets/parameters/getParameters.ts +++ b/docs/snippets/parameters/getParameters.ts @@ -1,12 +1,12 @@ import { getParameters } from '@aws-lambda-powertools/parameters/ssm'; -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { /** * Retrieve multiple parameters from a path prefix recursively. * This returns an object with the parameter name as key */ const parameters = await getParameters('/my/path/prefix'); - for (const [ key, value ] of Object.entries(parameters)) { + for (const [ key, value ] of Object.entries(parameters || {})) { console.log(`${key}: ${value}`); } }; \ No newline at end of file diff --git a/docs/snippets/parameters/getParametersByName.ts b/docs/snippets/parameters/getParametersByName.ts index cc9c162f04..9210d896da 100644 --- a/docs/snippets/parameters/getParametersByName.ts +++ b/docs/snippets/parameters/getParametersByName.ts @@ -1,12 +1,15 @@ import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; +import type { + SSMGetParametersByNameOptionsInterface +} from '@aws-lambda-powertools/parameters/ssm'; -const props = { +const props: Record = { '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, '/no_cache_param': { maxAge: 0 }, '/develop/service/payment/api/capture/url': {}, // When empty or undefined, it uses default values }; -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // This returns an object with the parameter name as key const parameters = await getParametersByName(props, { maxAge: 60 }); for (const [ key, value ] of Object.entries(parameters)) { diff --git a/docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts b/docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts index 17b042b660..5aee4277b4 100644 --- a/docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts +++ b/docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts @@ -1,11 +1,14 @@ import { getParametersByName } from '@aws-lambda-powertools/parameters/ssm'; +import type { + SSMGetParametersByNameOptionsInterface +} from '@aws-lambda-powertools/parameters/ssm'; -const props = { +const props: Record = { '/develop/service/commons/telemetry/config': { maxAge: 300, transform: 'json' }, '/this/param/does/not/exist': {}, // <- Example of non-existent parameter }; -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { const { _errors: errors, ...parameters diff --git a/docs/snippets/parameters/getSecret.ts b/docs/snippets/parameters/getSecret.ts index deb62222d8..417ed32572 100644 --- a/docs/snippets/parameters/getSecret.ts +++ b/docs/snippets/parameters/getSecret.ts @@ -1,6 +1,6 @@ import { getSecret } from '@aws-lambda-powertools/parameters/secrets'; -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single secret const secret = await getSecret('my-secret'); console.log(secret); diff --git a/docs/snippets/parameters/sdkOptions.ts b/docs/snippets/parameters/sdkOptions.ts index ee35ab51a2..60c4306b1d 100644 --- a/docs/snippets/parameters/sdkOptions.ts +++ b/docs/snippets/parameters/sdkOptions.ts @@ -3,7 +3,7 @@ import type { GetSecretValueCommandInput } from '@aws-sdk/client-secrets-manager const secretsProvider = new SecretsProvider(); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { const sdkOptions: Partial = { VersionId: 'e62ec170-6b01-48c7-94f3-d7497851a8d2' }; diff --git a/docs/snippets/parameters/secretsProvider.ts b/docs/snippets/parameters/secretsProvider.ts index a9337f4842..511b8b2c67 100644 --- a/docs/snippets/parameters/secretsProvider.ts +++ b/docs/snippets/parameters/secretsProvider.ts @@ -4,7 +4,7 @@ import type { SecretsManagerClientConfig } from '@aws-sdk/client-secretsmanager' const clientConfig: SecretsManagerClientConfig = { region: 'us-east-1' }; const secretsProvider = new SecretsProvider({ clientConfig }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single secret const secret = await secretsProvider.get('my-secret'); console.log(secret); diff --git a/docs/snippets/parameters/secretsProviderCustomClient.ts b/docs/snippets/parameters/secretsProviderCustomClient.ts index a14db58b10..3c5e1ed4ac 100644 --- a/docs/snippets/parameters/secretsProviderCustomClient.ts +++ b/docs/snippets/parameters/secretsProviderCustomClient.ts @@ -6,7 +6,7 @@ const secretsManagerClient = new SecretsManagerClient({ region: 'us-east-1' }); // pass the client to the provider const secretsProvider = new SecretsProvider({ awsSdkV3Client: secretsManagerClient }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single secret const secret = await secretsProvider.get('my-secret'); console.log(secret); diff --git a/docs/snippets/parameters/ssmProvider.ts b/docs/snippets/parameters/ssmProvider.ts index 6e22ad97a6..bda1379fee 100644 --- a/docs/snippets/parameters/ssmProvider.ts +++ b/docs/snippets/parameters/ssmProvider.ts @@ -4,14 +4,14 @@ import type { SSMClientConfig } from '@aws-sdk/client-ssm'; const clientConfig: SSMClientConfig = { region: 'us-east-1' }; const parametersProvider = new SSMProvider({ clientConfig }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single parameter const parameter = await parametersProvider.get('/my/parameter'); console.log(parameter); // Retrieve multiple parameters from a path prefix const parameters = await parametersProvider.getMultiple('/my/path/prefix'); - for (const [ key, value ] of Object.entries(parameters)) { + for (const [ key, value ] of Object.entries(parameters || {})) { console.log(`${key}: ${value}`); } }; \ No newline at end of file diff --git a/docs/snippets/parameters/ssmProviderCustomClient.ts b/docs/snippets/parameters/ssmProviderCustomClient.ts index 5ef4278c6e..5110aa4a0f 100644 --- a/docs/snippets/parameters/ssmProviderCustomClient.ts +++ b/docs/snippets/parameters/ssmProviderCustomClient.ts @@ -6,7 +6,7 @@ const ssmClient = new SSMClient({ region: 'us-east-1' }); // pass the client to the provider const parametersProvider = new SSMProvider({ awsSdkV3Client: ssmClient }); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Retrieve a single parameter const parameter = await parametersProvider.get('/my/parameter'); console.log(parameter); diff --git a/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts b/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts index 15b7b777aa..ca58165d18 100644 --- a/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts +++ b/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts @@ -2,12 +2,12 @@ import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; const parametersProvider = new SSMProvider(); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { const decryptedValue = await parametersProvider.get('/my/encrypted/parameter', { decrypt: true }); console.log(decryptedValue); const noRecursiveValues = await parametersProvider.getMultiple('/my/path/prefix', { recursive: false }); - for (const [ key, value ] of Object.entries(noRecursiveValues)) { + for (const [ key, value ] of Object.entries(noRecursiveValues || {})) { console.log(`${key}: ${value}`); } }; \ No newline at end of file diff --git a/docs/snippets/parameters/transform.ts b/docs/snippets/parameters/transform.ts index 194b6c9164..c50159636e 100644 --- a/docs/snippets/parameters/transform.ts +++ b/docs/snippets/parameters/transform.ts @@ -1,6 +1,6 @@ import { getParameter } from '@aws-lambda-powertools/parameters/ssm'; -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { const valueFromJson = await getParameter('/my/json/parameter', { transform: 'json' }); console.log(valueFromJson); }; \ No newline at end of file diff --git a/docs/snippets/parameters/transformAuto.ts b/docs/snippets/parameters/transformAuto.ts index ca9a150d90..6ee31047c7 100644 --- a/docs/snippets/parameters/transformAuto.ts +++ b/docs/snippets/parameters/transformAuto.ts @@ -2,9 +2,9 @@ import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; const parametersProvider = new SSMProvider(); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { const values = await parametersProvider.getMultiple('/param', { transform: 'auto' }); - for (const [ key, value ] of Object.entries(values)) { + for (const [ key, value ] of Object.entries(values || {})) { console.log(`${key}: ${value}`); } }; \ No newline at end of file diff --git a/docs/snippets/parameters/transformPartialFailures.ts b/docs/snippets/parameters/transformPartialFailures.ts index dfdc9a9384..975de75928 100644 --- a/docs/snippets/parameters/transformPartialFailures.ts +++ b/docs/snippets/parameters/transformPartialFailures.ts @@ -2,7 +2,7 @@ import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; const parametersProvider = new SSMProvider(); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { /** * This will display: * /param/a: [some value] @@ -10,7 +10,7 @@ export const handler = async (_event, _context): Promise => { * /param/c: undefined */ const parameters = await parametersProvider.getMultiple('/param', { transform: 'json' }); - for (const [ key, value ] of Object.entries(parameters)) { + for (const [ key, value ] of Object.entries(parameters || {})) { console.log(`${key}: ${value}`); } @@ -20,7 +20,7 @@ export const handler = async (_event, _context): Promise => { transform: 'json', throwOnTransformError: true }); - for (const [ key, value ] of Object.entries(parameters2)) { + for (const [ key, value ] of Object.entries(parameters2 || {})) { console.log(`${key}: ${value}`); } } catch (err) { diff --git a/docs/snippets/parameters/transformProvider.ts b/docs/snippets/parameters/transformProvider.ts index 14974ad3b7..417ac2b314 100644 --- a/docs/snippets/parameters/transformProvider.ts +++ b/docs/snippets/parameters/transformProvider.ts @@ -2,7 +2,7 @@ import { SecretsProvider } from '@aws-lambda-powertools/parameters/secrets'; const secretsProvider = new SecretsProvider(); -export const handler = async (_event, _context): Promise => { +export const handler = async (): Promise => { // Transform a JSON string const json = await secretsProvider.get('my-secret-json', { transform: 'json' }); console.log(json); diff --git a/docs/snippets/parameters/tsconfig.json b/docs/snippets/parameters/tsconfig.json new file mode 100644 index 0000000000..1530f2b93a --- /dev/null +++ b/docs/snippets/parameters/tsconfig.json @@ -0,0 +1,48 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "noImplicitAny": true, + "target": "ES2020", + "module": "commonjs", + "declaration": true, + "declarationMap": true, + "outDir": "lib", + "removeComments": false, + "strict": true, + "inlineSourceMap": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "pretty": true, + "esModuleInterop": true, + "allowJs": true, + "baseUrl": ".", + "paths": { + "@aws-lambda-powertools/parameters/ssm": [ + "../../../packages/parameters/lib/ssm" + ], + "@aws-lambda-powertools/parameters/secrets": [ + "../../../packages/parameters/lib/secrets" + ], + "@aws-lambda-powertools/parameters/appconfig": [ + "../../../packages/parameters/lib/appconfig" + ], + "@aws-lambda-powertools/parameters/dynamodb": [ + "../../../packages/parameters/lib/dynamodb" + ], + }, + }, + "exclude": [ + "./node_modules" + ], + "watchOptions": { + "watchFile": "useFsEvents", + "watchDirectory": "useFsEvents", + "fallbackPolling": "dynamicPriority" + }, + "lib": [ + "ES2020" + ], + "types": [ + "node" + ] +} \ No newline at end of file diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 6f7264951d..9c659e1293 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -6,7 +6,7 @@ description: Utility ???+ warning This page refers to an **unreleased and upcoming utility**. Please refer to this [GitHub milestone](https://github.com/awslabs/aws-lambda-powertools-typescript/milestone/8) for the latest updates. -The parameters utility provides high-level functions to retrieve one or multiple parameter values from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html){target="_blank"}, [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/){target="_blank"}, [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html){target="_blank"}, [Amazon DynamoDB](https://aws.amazon.com/dynamodb/){target="_blank"}, or your own parameter store. +The Parameters utility provides high-level functions to retrieve one or multiple parameter values from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html){target="_blank"}, [AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html){target="_blank"}, [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html){target="_blank"}, [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html){target="_blank"}, or your own parameter store. ## Key features @@ -17,7 +17,7 @@ The parameters utility provides high-level functions to retrieve one or multiple ## Getting started -By default, we fetch parameters from System Manager Parameter Store (SSM), secrets from Secrets Manager, and application configuration from AppConfig. Additionally, we support a DynamoDB provider to retrieve arbitrary parameters from your tables. +The Parameters Utility helps to retrieve parameters from the System Manager Parameter Store (SSM), secrets from the Secrets Manager, and application configuration from AppConfig. Additionally, the utility also offers support for a DynamoDB provider, enabling the retrieval of arbitrary parameters from specified tables. ### Installation @@ -88,20 +88,20 @@ For multiple parameters, you can use either: === "getParametersByName" - ```typescript hl_lines="1 4-6 11" title="Fetching multiple parameters by names from SSM" + ```typescript hl_lines="1-4 7-9 14" title="Fetching multiple parameters by names from SSM" --8<-- "docs/snippets/parameters/getParametersByName.ts" ``` ???+ tip "`getParametersByName` supports graceful error handling" - By default, we will throw a `GetParameterError` when any parameter fails to be fetched. You can override it by setting `throwOnError: false`. + By default, the provider will throw a `GetParameterError` when any parameter fails to be fetched. You can override it by setting `throwOnError: false`. - When disabled, we take the following actions: + When disabled, instead the provider will take the following actions: * Add failed parameter name in the `_errors` key, _e.g._, `{ _errors: [ '/param1', '/param2' ] }` * Keep only successful parameter names and their values in the response * Throw `GetParameterError` if any of your parameters is named `_errors` -```typescript hl_lines="1 4-5 10 15" +```typescript hl_lines="1-4 7-8 13 15 18" --8<-- "docs/snippets/parameters/getParametersByNameGracefulErrorHandling.ts" ``` @@ -130,9 +130,9 @@ The following will retrieve the latest version and store it in the cache. ???+ tip `maxAge` parameter is also available in high level functions like `getParameter`, `getSecret`, etc. -By default, we cache parameters retrieved in-memory for 5 seconds. +By default, the provider will cache parameters retrieved in-memory for 5 seconds. -You can adjust how long we should keep values in cache by using the param `maxAge`, when using `get()` or `getMultiple()` methods across all providers. +You can adjust how long values should be kept in cache by using the param `maxAge`, when using `get()` or `getMultiple()` methods across all providers. ```typescript hl_lines="7 10" title="Caching parameters values in memory for longer than 5 seconds" --8<-- "docs/snippets/parameters/adjustingCacheTTL.ts" @@ -163,7 +163,7 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen | Parameter | Default | Description | | ------------- | ------- | --------------------------------------------------------------------------------------------- | -| **decrypt** | `false` | Will automatically decrypt the parameter. | +| **decrypt** | `false` | Will automatically decrypt the parameter (see required [IAM Permissions](#iam-permissions)). | | **recursive** | `true` | For `getMultiple()` only, will fetch all parameter values recursively based on a path prefix. | ```typescript hl_lines="6 8" title="Example with get() and getMultiple()" @@ -307,10 +307,11 @@ You can do this with a single request by using `transform: 'auto'`. This will in --8<-- "docs/snippets/parameters/transformAuto.ts" ``` -For example, if you have two parameters with the following suffixes `.json` and `.binary`: +For example, if you have three parameters: two with the following suffixes `.json` and `.binary` and one without any suffix: | Parameter name | Parameter value | | --------------- | -------------------- | +| /param/a | [some encoded value] | | /param/a.json | [some encoded value] | | /param/a.binary | [some encoded value] | @@ -318,11 +319,14 @@ The return of `await parametersProvider.getMultiple('/param', transform: 'auto') ```json { - "a.json": [some value], - "b.binary": [some value] + "a": [some encoded value], + "a.json": [some decoded value], + "b.binary": [some decoded value] } ``` +The two parameters with a suffix will be decoded, while the one without a suffix will be returned as is. + ### Passing additional SDK arguments You can use a special `sdkOptions` object argument to pass any supported option directly to the underlying SDK method. @@ -333,18 +337,18 @@ You can use a special `sdkOptions` object argument to pass any supported option Here is the mapping between this utility's functions and methods and the underlying SDK: -| Provider | Function/Method | Client name | Function name | -| ------------------- | ------------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| SSM Parameter Store | `getParameter` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | -| SSM Parameter Store | `getParameters` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | -| SSM Parameter Store | `SSMProvider.get` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | -| SSM Parameter Store | `SSMProvider.getMultiple` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | -| Secrets Manager | `getSecret` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | -| Secrets Manager | `SecretsProvider.get` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | -| AppConfig | `AppConfigProvider.get` | `@aws-sdk/client-appconfigdata` | [GetLatestConfigurationCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfigdata/classes/getlatestconfigurationcommand.html) | -| AppConfig | `getAppConfig` | `@aws-sdk/client-appconfigdata` | [GetLatestConfigurationCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfigdata/classes/getlatestconfigurationcommand.html) | -| DynamoDB | `DynamoDBProvider.get` | `@aws-sdk/client-dynamodb` | [GetItemCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/getitemcommand.html) | -| DynamoDB | `DynamoDBProvider.getMultiple` | `@aws-sdk/client-dynamodb` | [QueryCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/querycommand.html) | +| Provider | Function/Method | Client name | Function name | +| ------------------- | ------------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SSM Parameter Store | `getParameter` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | +| SSM Parameter Store | `getParameters` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | +| SSM Parameter Store | `SSMProvider.get` | `@aws-sdk/client-ssm` | [GetParameterCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametercommand.html) | +| SSM Parameter Store | `SSMProvider.getMultiple` | `@aws-sdk/client-ssm` | [GetParametersByPathCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ssm/classes/getparametersbypathcommand.html) | +| Secrets Manager | `getSecret` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | +| Secrets Manager | `SecretsProvider.get` | `@aws-sdk/client-secrets-manager` | [GetSecretValueCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-secrets-manager/classes/getsecretvaluecommand.html) | +| AppConfig | `AppConfigProvider.get` | `@aws-sdk/client-appconfigdata` | [StartConfigurationSessionCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfigdata/classes/startconfigurationsessioncommand.html) & [GetLatestConfigurationCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfigdata/classes/getlatestconfigurationcommand.html) | +| AppConfig | `getAppConfig` | `@aws-sdk/client-appconfigdata` | [StartConfigurationSessionCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfigdata/classes/startconfigurationsessioncommand.html) & [GetLatestConfigurationCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-appconfigdata/classes/getlatestconfigurationcommand.html) | +| DynamoDB | `DynamoDBProvider.get` | `@aws-sdk/client-dynamodb` | [GetItemCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/getitemcommand.html) | +| DynamoDB | `DynamoDBProvider.getMultiple` | `@aws-sdk/client-dynamodb` | [QueryCommand](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/classes/querycommand.html) | ### Bring your own AWS SDK v3 client