diff --git a/docs/index.md b/docs/index.md index 2076c48f01..f4b55347dc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -293,20 +293,22 @@ Core utilities such as Tracing, Logging, and Metrics will be available across al ???+ info Explicit parameters take precedence over environment variables -| Environment variable | Description | Utility | Default | -| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------- | ------------------- | -| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | `service_undefined` | -| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | `default_namespace` | -| **POWERTOOLS_TRACE_ENABLED** | Explicitly disables tracing | [Tracer](./core/tracer) | `true` | -| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Captures Lambda or method return as metadata. | [Tracer](./core/tracer) | `true` | -| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Captures Lambda or method exception as metadata. | [Tracer](./core/tracer) | `true` | -| **POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS** | Captures HTTP(s) requests as segments. | [Tracer](./core/tracer) | `true` | -| **POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming event | [Logger](./core/logger) | `false` | -| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logger](./core/logger) | `0` | -| **POWERTOOLS_DEV** | Increase JSON indentation to ease debugging when running functions locally or in a non-production environment | [Logger](./core/logger) | `false` | -| **LOG_LEVEL** | Sets logging level | [Logger](./core/logger) | `INFO` | - -Each Utility page provides information on example values and allowed values +| Environment variable | Description | Utility | Default | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------------------- | +| **POWERTOOLS_SERVICE_NAME** | Sets service name used for tracing namespace, metrics dimension and structured logging | All | `service_undefined` | +| **POWERTOOLS_METRICS_NAMESPACE** | Sets namespace used for metrics | [Metrics](./core/metrics) | `default_namespace` | +| **POWERTOOLS_TRACE_ENABLED** | Explicitly disables tracing | [Tracer](./core/tracer) | `true` | +| **POWERTOOLS_TRACER_CAPTURE_RESPONSE** | Captures Lambda or method return as metadata. | [Tracer](./core/tracer) | `true` | +| **POWERTOOLS_TRACER_CAPTURE_ERROR** | Captures Lambda or method exception as metadata. | [Tracer](./core/tracer) | `true` | +| **POWERTOOLS_TRACER_CAPTURE_HTTPS_REQUESTS** | Captures HTTP(s) requests as segments. | [Tracer](./core/tracer) | `true` | +| **POWERTOOLS_LOGGER_LOG_EVENT** | Logs incoming event | [Logger](./core/logger) | `false` | +| **POWERTOOLS_LOGGER_SAMPLE_RATE** | Debug log sampling | [Logger](./core/logger) | `0` | +| **POWERTOOLS_DEV** | Increase JSON indentation to ease debugging when running functions locally or in a non-production environment | [Logger](./core/logger) | `false` | +| **LOG_LEVEL** | Sets logging level | [Logger](./core/logger) | `INFO` | +| **POWERTOOLS_PARAMETERS_MAX_AGE** | Adjust how long values are kept in cache (in seconds) | [Parameters](./utilities/parameters) | `5` | +| **POWERTOOLS_PARAMETERS_SSM_DECRYPT** | Sets whether to decrypt or not values retrieved from AWS Systems Manager Parameters Store | [Parameters](./utilities/parameters) | `false` | + +Each Utility page provides information on example values and allowed values. ## Tenets diff --git a/docs/snippets/parameters/adjustingCacheTTL.ts b/docs/snippets/parameters/adjustingCacheTTL.ts index 795c1b01d4..3207c76a44 100644 --- a/docs/snippets/parameters/adjustingCacheTTL.ts +++ b/docs/snippets/parameters/adjustingCacheTTL.ts @@ -3,12 +3,12 @@ import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; const parametersProvider = new SSMProvider(); export const handler = async (): Promise => { - // Retrieve a single parameter - const parameter = await parametersProvider.get('/my/parameter', { maxAge: 60 }); // 1 minute + // Retrieve a single parameter and cache it for 1 minute + const parameter = await parametersProvider.get('/my/parameter', { maxAge: 60 }); // (1) console.log(parameter); - // Retrieve multiple parameters from a path prefix - const parameters = await parametersProvider.getMultiple('/my/path/prefix', { maxAge: 120 }); // 2 minutes + // Retrieve multiple parameters from a path prefix and cache them for 2 minutes + const parameters = await parametersProvider.getMultiple('/my/path/prefix', { maxAge: 120 }); for (const [ key, value ] of Object.entries(parameters || {})) { console.log(`${key}: ${value}`); } diff --git a/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts b/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts index ca58165d18..3824be82e3 100644 --- a/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts +++ b/docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts @@ -3,7 +3,7 @@ import { SSMProvider } from '@aws-lambda-powertools/parameters/ssm'; const parametersProvider = new SSMProvider(); export const handler = async (): Promise => { - const decryptedValue = await parametersProvider.get('/my/encrypted/parameter', { decrypt: true }); + const decryptedValue = await parametersProvider.get('/my/encrypted/parameter', { decrypt: true }); // (1) console.log(decryptedValue); const noRecursiveValues = await parametersProvider.getMultiple('/my/path/prefix', { recursive: false }); diff --git a/docs/utilities/parameters.md b/docs/utilities/parameters.md index 6409149cf5..8663faed5d 100644 --- a/docs/utilities/parameters.md +++ b/docs/utilities/parameters.md @@ -127,17 +127,22 @@ The following will retrieve the latest version and store it in the cache. ### Adjusting cache TTL -???+ tip - `maxAge` parameter is also available in high level functions like `getParameter`, `getSecret`, etc. - By default, the provider will cache parameters retrieved in-memory for 5 seconds. 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. +???+ tip + If you want to set the same TTL for all parameters, you can set the `POWERTOOLS_PARAMETERS_MAX_AGE` environment variable. **This will override the default TTL of 5 seconds but can be overridden by the `maxAge` parameter**. + ```typescript hl_lines="7 11" title="Caching parameters values in memory for longer than 5 seconds" --8<-- "docs/snippets/parameters/adjustingCacheTTL.ts" ``` +1. Options passed to `get()`, `getMultiple()`, and `getParametersByName()` will override the values set in `POWERTOOLS_PARAMETERS_MAX_AGE` environment variable. + +???+ info + The `maxAge` parameter is also available in high level functions like `getParameter`, `getSecret`, etc. + ### 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 the `forceFetch` parameter. @@ -166,10 +171,15 @@ The AWS Systems Manager Parameter Store provider supports two additional argumen | **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. | +???+ tip + If you want to always decrypt parameters, you can set the `POWERTOOLS_PARAMETERS_SSM_DECRYPT=true` environment variable. **This will override the default value of `false` but can be overridden by the `decrypt` parameter**. + ```typescript hl_lines="6 9" title="Example with get() and getMultiple()" --8<-- "docs/snippets/parameters/ssmProviderDecryptAndRecursive.ts" ``` +1. Options passed to `get()`, `getMultiple()`, and `getParametersByName()` will override the values set in `POWERTOOLS_PARAMETERS_SSM_DECRYPT` environment variable. + #### SecretsProvider ```typescript hl_lines="4-5" title="Example with SecretsProvider for further extensibility" diff --git a/package-lock.json b/package-lock.json index 14da5b820d..49f9ce936e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16764,6 +16764,7 @@ "version": "1.7.0", "license": "MIT-0", "dependencies": { + "@aws-lambda-powertools/commons": "^1.7.0", "@aws-sdk/util-base64-node": "^3.209.0" }, "devDependencies": { @@ -17055,6 +17056,7 @@ "@aws-lambda-powertools/parameters": { "version": "file:packages/parameters", "requires": { + "@aws-lambda-powertools/commons": "*", "@aws-sdk/client-appconfigdata": "^3.241.0", "@aws-sdk/client-dynamodb": "^3.245.0", "@aws-sdk/client-secrets-manager": "^3.238.0", diff --git a/packages/commons/src/config/ConfigService.ts b/packages/commons/src/config/ConfigService.ts index 6e8f4adf6b..22b8fae232 100644 --- a/packages/commons/src/config/ConfigService.ts +++ b/packages/commons/src/config/ConfigService.ts @@ -24,6 +24,25 @@ abstract class ConfigService { */ public abstract getServiceName(): string; + /** + * It returns the value of the _X_AMZN_TRACE_ID environment variable. + * + * The AWS X-Ray Trace data available in the environment variable has this format: + * `Root=1-5759e988-bd862e3fe1be46a994272793;Parent=557abcec3ee5a047;Sampled=1`, + * + * The actual Trace ID is: `1-5759e988-bd862e3fe1be46a994272793`. + * + * @returns {string|undefined} + */ + public abstract getXrayTraceId(): string | undefined; + + /** + * It returns true if the string value represents a boolean true value. + * + * @param {string} value + * @returns boolean + */ + public abstract isValueTrue(value: string): boolean; } export { diff --git a/packages/commons/src/config/EnvironmentVariablesService.ts b/packages/commons/src/config/EnvironmentVariablesService.ts index 380bbc0458..2f2d0c7165 100644 --- a/packages/commons/src/config/EnvironmentVariablesService.ts +++ b/packages/commons/src/config/EnvironmentVariablesService.ts @@ -60,6 +60,18 @@ class EnvironmentVariablesService extends ConfigService { return xRayTraceId.split(';')[0].replace('Root=', ''); } + /** + * It returns true if the string value represents a boolean true value. + * + * @param {string} value + * @returns boolean + */ + public isValueTrue(value: string): boolean { + const truthyValues: string[] = [ '1', 'y', 'yes', 't', 'true', 'on' ]; + + return truthyValues.includes(value.toLowerCase()); + } + } export { diff --git a/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts index 692a5b1ff0..c9b56b22ef 100644 --- a/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/commons/tests/unit/config/EnvironmentVariablesService.test.ts @@ -110,4 +110,31 @@ describe('Class: EnvironmentVariablesService', () => { }); + describe('Method: isValueTrue', () => { + + const valuesToTest: Array> = [ + [ '1', true ], + [ 'y', true ], + [ 'yes', true ], + [ 't', true ], + [ 'TRUE', true ], + [ 'on', true ], + [ '', false ], + [ 'false', false ], + [ 'fasle', false ], + [ 'somethingsilly', false ], + [ '0', false ] + ]; + + test.each(valuesToTest)('it takes string "%s" and returns %s', (input, output) => { + // Prepare + const service = new EnvironmentVariablesService(); + // Act + const value = service.isValueTrue(input as string); + // Assess + expect(value).toBe(output); + }); + + }); + }); \ No newline at end of file diff --git a/packages/logger/src/config/EnvironmentVariablesService.ts b/packages/logger/src/config/EnvironmentVariablesService.ts index 52fe910784..c7813539e0 100644 --- a/packages/logger/src/config/EnvironmentVariablesService.ts +++ b/packages/logger/src/config/EnvironmentVariablesService.ts @@ -117,18 +117,6 @@ class EnvironmentVariablesService extends CommonEnvironmentVariablesService impl return this.isValueTrue(value); } - /** - * It returns true if the string value represents a boolean true value. - * - * @param {string} value - * @returns boolean - */ - public isValueTrue(value: string): boolean { - const truthyValues: string[] = [ '1', 'y', 'yes', 't', 'true', 'on' ]; - - return truthyValues.includes(value.toLowerCase()); - } - } export { diff --git a/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts index 637c9ca615..662976b339 100644 --- a/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts +++ b/packages/logger/tests/unit/config/EnvironmentVariablesService.test.ts @@ -249,31 +249,4 @@ describe('Class: EnvironmentVariablesService', () => { }); - describe('Method: isValueTrue', () => { - - const valuesToTest: Array> = [ - [ '1', true ], - [ 'y', true ], - [ 'yes', true ], - [ 't', true ], - [ 'TRUE', true ], - [ 'on', true ], - [ '', false ], - [ 'false', false ], - [ 'fasle', false ], - [ 'somethingsilly', false ], - [ '0', false ] - ]; - - test.each(valuesToTest)('it takes string "%s" and returns %s', (input, output) => { - // Prepare - const service = new EnvironmentVariablesService(); - // Act - const value = service.isValueTrue(input as string); - // Assess - expect(value).toBe(output); - }); - - }); - }); \ No newline at end of file diff --git a/packages/parameters/package.json b/packages/parameters/package.json index 89ec7cfb25..c603033d54 100644 --- a/packages/parameters/package.json +++ b/packages/parameters/package.json @@ -65,6 +65,7 @@ "aws-sdk-client-mock-jest": "^2.0.1" }, "dependencies": { + "@aws-lambda-powertools/commons": "^1.7.0", "@aws-sdk/util-base64-node": "^3.209.0" } } diff --git a/packages/parameters/src/BaseProvider.ts b/packages/parameters/src/BaseProvider.ts index 865483d810..46f7e5acc1 100644 --- a/packages/parameters/src/BaseProvider.ts +++ b/packages/parameters/src/BaseProvider.ts @@ -4,7 +4,13 @@ import { GetMultipleOptions } from './GetMultipleOptions'; import { ExpirableValue } from './ExpirableValue'; import { TRANSFORM_METHOD_BINARY, TRANSFORM_METHOD_JSON } from './constants'; import { GetParameterError, TransformParameterError } from './Exceptions'; -import type { BaseProviderInterface, GetMultipleOptionsInterface, GetOptionsInterface, TransformOptions } from './types'; +import { EnvironmentVariablesService } from './config/EnvironmentVariablesService'; +import type { + BaseProviderInterface, + GetMultipleOptionsInterface, + GetOptionsInterface, + TransformOptions +} from './types'; // These providers are dinamycally intialized on first use of the helper functions const DEFAULT_PROVIDERS: Record = {}; @@ -29,10 +35,12 @@ const DEFAULT_PROVIDERS: Record = {}; * this should be an acceptable tradeoff. */ abstract class BaseProvider implements BaseProviderInterface { + public envVarsService: EnvironmentVariablesService; protected store: Map; public constructor() { this.store = new Map(); + this.envVarsService = new EnvironmentVariablesService(); } /** @@ -62,7 +70,7 @@ abstract class BaseProvider implements BaseProviderInterface { * @param {GetOptionsInterface} options - Options to configure maximum age, trasformation, AWS SDK options, or force fetch */ public async get(name: string, options?: GetOptionsInterface): Promise> { - const configs = new GetOptions(options); + const configs = new GetOptions(options, this.envVarsService); const key = [ name, configs.transform ].toString(); if (!configs.forceFetch && !this.hasKeyExpiredInCache(key)) { @@ -97,7 +105,7 @@ abstract class BaseProvider implements BaseProviderInterface { * @returns */ public async getMultiple(path: string, options?: GetMultipleOptionsInterface): Promise> { - const configs = new GetMultipleOptions(options || {}); + const configs = new GetMultipleOptions(options, this.envVarsService); const key = [ path, configs.transform ].toString(); if (!configs.forceFetch && !this.hasKeyExpiredInCache(key)) { diff --git a/packages/parameters/src/GetMultipleOptions.ts b/packages/parameters/src/GetMultipleOptions.ts index 1b1b0180c6..53b57c9262 100644 --- a/packages/parameters/src/GetMultipleOptions.ts +++ b/packages/parameters/src/GetMultipleOptions.ts @@ -1,20 +1,21 @@ -import { DEFAULT_MAX_AGE_SECS } from './constants'; -import type { GetMultipleOptionsInterface, TransformOptions } from './types'; +import { GetOptions } from './GetOptions'; +import { EnvironmentVariablesService } from './config/EnvironmentVariablesService'; +import type { GetMultipleOptionsInterface } from './types'; /** * Options for the `getMultiple` method. * - * It merges the default options with the provided options. + * Extends the `GetOptions` class and adds the `throwOnTransformError` option. */ -class GetMultipleOptions implements GetMultipleOptionsInterface { - public forceFetch: boolean = false; - public maxAge: number = DEFAULT_MAX_AGE_SECS; - public sdkOptions?: unknown; +class GetMultipleOptions extends GetOptions implements GetMultipleOptionsInterface { public throwOnTransformError: boolean = false; - public transform?: TransformOptions; - public constructor(options: GetMultipleOptionsInterface) { - Object.assign(this, options); + public constructor(options: GetMultipleOptionsInterface = {}, envVarsService: EnvironmentVariablesService) { + super(options, envVarsService); + + if (options.throwOnTransformError !== undefined) { + this.throwOnTransformError = options.throwOnTransformError; + } } } diff --git a/packages/parameters/src/GetOptions.ts b/packages/parameters/src/GetOptions.ts index 254412f127..b3cb97c2c3 100644 --- a/packages/parameters/src/GetOptions.ts +++ b/packages/parameters/src/GetOptions.ts @@ -1,4 +1,5 @@ import { DEFAULT_MAX_AGE_SECS } from './constants'; +import { EnvironmentVariablesService } from './config/EnvironmentVariablesService'; import type { GetOptionsInterface, TransformOptions } from './types'; /** @@ -8,12 +9,16 @@ import type { GetOptionsInterface, TransformOptions } from './types'; */ class GetOptions implements GetOptionsInterface { public forceFetch: boolean = false; - public maxAge: number = DEFAULT_MAX_AGE_SECS; + public maxAge!: number; public sdkOptions?: unknown; public transform?: TransformOptions; - public constructor(options: GetOptionsInterface = {}) { + public constructor(options: GetOptionsInterface = {}, envVarsService: EnvironmentVariablesService) { Object.assign(this, options); + + if (options.maxAge === undefined) { + this.maxAge = envVarsService.getParametersMaxAge() ?? DEFAULT_MAX_AGE_SECS; + } } } diff --git a/packages/parameters/src/appconfig/AppConfigProvider.ts b/packages/parameters/src/appconfig/AppConfigProvider.ts index f663ab6de8..1d82b156c5 100644 --- a/packages/parameters/src/appconfig/AppConfigProvider.ts +++ b/packages/parameters/src/appconfig/AppConfigProvider.ts @@ -165,8 +165,8 @@ import type { */ class AppConfigProvider extends BaseProvider { public client: AppConfigDataClient; - protected configurationTokenStore: Map = new Map(); - protected valueStore: Map = new Map(); + protected configurationTokenStore = new Map(); + protected valueStore = new Map(); private application?: string; private environment: string; @@ -187,13 +187,12 @@ class AppConfigProvider extends BaseProvider { this.client = new AppConfigDataClient(options.clientConfig || {}); } - if (!options?.application && !process.env['POWERTOOLS_SERVICE_NAME']) { + this.application = options?.application || this.envVarsService.getServiceName(); + if (!this.application || this.application.trim().length === 0) { throw new Error( 'Application name is not defined or POWERTOOLS_SERVICE_NAME is not set' ); } - this.application = - options.application || process.env['POWERTOOLS_SERVICE_NAME']; this.environment = options.environment; } diff --git a/packages/parameters/src/config/ConfigServiceInterface.ts b/packages/parameters/src/config/ConfigServiceInterface.ts new file mode 100644 index 0000000000..9ea8ff66fa --- /dev/null +++ b/packages/parameters/src/config/ConfigServiceInterface.ts @@ -0,0 +1,15 @@ +interface ConfigServiceInterface { + + get?(name: string): string + + getServiceName(): string + + getParametersMaxAge(): number | undefined + + getSSMDecrypt(): string + +} + +export { + ConfigServiceInterface, +}; \ No newline at end of file diff --git a/packages/parameters/src/config/EnvironmentVariablesService.ts b/packages/parameters/src/config/EnvironmentVariablesService.ts new file mode 100644 index 0000000000..e528a76d05 --- /dev/null +++ b/packages/parameters/src/config/EnvironmentVariablesService.ts @@ -0,0 +1,34 @@ +import { ConfigServiceInterface } from './ConfigServiceInterface'; +import { DEFAULT_MAX_AGE_SECS } from '../constants'; +import { EnvironmentVariablesService as CommonEnvironmentVariablesService } from '@aws-lambda-powertools/commons'; + +class EnvironmentVariablesService extends CommonEnvironmentVariablesService implements ConfigServiceInterface { + + // Environment variables + private parametersMaxAgeVariable = 'POWERTOOLS_PARAMETERS_MAX_AGE'; + private ssmDecryptVariable = 'POWERTOOLS_PARAMETERS_SSM_DECRYPT'; + + public getParametersMaxAge(): number | undefined { + const maxAge = this.get(this.parametersMaxAgeVariable); + + if (maxAge.length === 0) return undefined; + + const maxAgeAsNumber = parseInt(maxAge, 10); + if (isNaN(maxAgeAsNumber)) { + console.warn( + `Invalid value for ${this.parametersMaxAgeVariable} environment variable: [${maxAge}], using default value of ${DEFAULT_MAX_AGE_SECS} seconds` + ); + } else { + return maxAgeAsNumber; + } + } + + public getSSMDecrypt(): string { + return this.get(this.ssmDecryptVariable); + } + +} + +export { + EnvironmentVariablesService, +}; \ No newline at end of file diff --git a/packages/parameters/src/ssm/SSMProvider.ts b/packages/parameters/src/ssm/SSMProvider.ts index 28818a8aaa..14fd6793c2 100644 --- a/packages/parameters/src/ssm/SSMProvider.ts +++ b/packages/parameters/src/ssm/SSMProvider.ts @@ -414,7 +414,7 @@ class SSMProvider extends BaseProvider { options?: SSMGetParametersByNameOptionsInterface ): Promise> { const configs = { ...{ - decrypt: false, + decrypt: this.resolveDecryptionConfigValue({}) || false, maxAge: DEFAULT_MAX_AGE_SECS, throwOnError: true, }, ...options }; @@ -477,8 +477,7 @@ class SSMProvider extends BaseProvider { ...(options?.sdkOptions || {}), Name: name, }; - sdkOptions.WithDecryption = options?.decrypt !== undefined ? - options.decrypt : sdkOptions.WithDecryption; + sdkOptions.WithDecryption = this.resolveDecryptionConfigValue(options, sdkOptions); const result = await this.client.send(new GetParameterCommand(sdkOptions)); return result.Parameter?.Value; @@ -501,8 +500,7 @@ class SSMProvider extends BaseProvider { const paginationOptions: PaginationConfiguration = { client: this.client }; - sdkOptions.WithDecryption = options?.decrypt !== undefined ? - options.decrypt : sdkOptions.WithDecryption; + sdkOptions.WithDecryption = this.resolveDecryptionConfigValue(options, sdkOptions); sdkOptions.Recursive = options?.recursive !== undefined ? options.recursive : sdkOptions.Recursive; paginationOptions.pageSize = sdkOptions.MaxResults !== undefined ? @@ -734,6 +732,19 @@ class SSMProvider extends BaseProvider { return errors; } + protected resolveDecryptionConfigValue( + options: SSMGetOptionsInterface | SSMGetMultipleOptionsInterface = {}, + sdkOptions?: GetParameterCommandInput | GetParametersByPathCommandInput + ): boolean | undefined { + if (options?.decrypt !== undefined) return options.decrypt; + if (sdkOptions?.WithDecryption !== undefined) return sdkOptions.WithDecryption; + if (this.envVarsService.getSSMDecrypt() !== '') { + return this.envVarsService.isValueTrue(this.envVarsService.getSSMDecrypt()); + } + + return undefined; + } + /** * Split parameters that can be fetched by GetParameters vs GetParameter. * diff --git a/packages/parameters/tests/unit/SSMProvider.test.ts b/packages/parameters/tests/unit/SSMProvider.test.ts index 856f2debec..52b1316202 100644 --- a/packages/parameters/tests/unit/SSMProvider.test.ts +++ b/packages/parameters/tests/unit/SSMProvider.test.ts @@ -26,9 +26,11 @@ import { toBase64 } from '@aws-sdk/util-base64'; const encoder = new TextEncoder(); describe('Class: SSMProvider', () => { + const ENVIRONMENT_VARIABLES = process.env; beforeEach(() => { jest.clearAllMocks(); + process.env = { ...ENVIRONMENT_VARIABLES }; }); describe('Method: constructor', () => { @@ -305,6 +307,31 @@ describe('Class: SSMProvider', () => { describe('Method: _get', () => { + test('when called without any options but with POWERTOOLS_PARAMETERS_SSM_DECRYPT env var enabled, it gets the parameter with decryption', async () => { + + // Prepare + process.env.POWERTOOLS_PARAMETERS_SSM_DECRYPT = 'true'; + const provider = new SSMProvider(); + const parameterName = 'foo'; + const parameterValue = 'foo'; + const client = mockClient(SSMClient).on(GetParameterCommand).resolves({ + Parameter: { + Value: parameterValue, + }, + }); + + // Act + const value = await provider.get(parameterName); + + // Assess + expect(client).toReceiveCommandWith(GetParameterCommand, { + Name: parameterName, + WithDecryption: true, + }); + expect(value).toBe(parameterValue); + + }); + test('when called without sdkOptions, it gets the parameter using the name and with no decryption', async () => { // Prepare diff --git a/packages/parameters/tests/unit/config/EnvironmentVariablesService.test.ts b/packages/parameters/tests/unit/config/EnvironmentVariablesService.test.ts new file mode 100644 index 0000000000..b6822885e1 --- /dev/null +++ b/packages/parameters/tests/unit/config/EnvironmentVariablesService.test.ts @@ -0,0 +1,88 @@ +/** + * Test Parameters EnvironmentVariablesService class + * + * @group unit/parameters/config + */ +import { EnvironmentVariablesService } from '../../../src/config/EnvironmentVariablesService'; + +describe('Class: EnvironmentVariablesService', () => { + + const ENVIRONMENT_VARIABLES = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...ENVIRONMENT_VARIABLES }; + }); + + afterAll(() => { + process.env = ENVIRONMENT_VARIABLES; + }); + + describe('Method: getParametersMaxAge', () => { + + test('it returns undefined if the POWERTOOLS_PARAMETERS_MAX_AGE is empty', () => { + + // Prepare + const service = new EnvironmentVariablesService(); + + // Act + const value = service.getParametersMaxAge(); + + // Assess + expect(value).toEqual(undefined); + + }); + + test('it returns a number if the POWERTOOLS_PARAMETERS_MAX_AGE has a numeric value', () => { + + // Prepare + process.env.POWERTOOLS_PARAMETERS_MAX_AGE = '36'; + const service = new EnvironmentVariablesService(); + + // Act + const value = service.getParametersMaxAge(); + + // Assess + expect(value).toEqual(36); + + }); + + test('it logs a warning if the POWERTOOLS_PARAMETERS_MAX_AGE has a non-numeric value', () => { + + // Prepare + process.env.POWERTOOLS_PARAMETERS_MAX_AGE = 'invalid'; + const service = new EnvironmentVariablesService(); + const warnLogspy = jest.spyOn(console, 'warn').mockImplementation(); + + // Act + const value = service.getParametersMaxAge(); + + // Assess + expect(value).toEqual(undefined); + expect(warnLogspy) + .toHaveBeenCalledWith( + 'Invalid value for POWERTOOLS_PARAMETERS_MAX_AGE environment variable: [invalid], using default value of 5 seconds' + ); + + }); + + }); + + describe('Method: getSSMDecrypt', () => { + + test('it returns the value of the environment variable POWERTOOLS_PARAMETERS_SSM_DECRYPT', () => { + + // Prepare + process.env.POWERTOOLS_PARAMETERS_SSM_DECRYPT = 'true'; + const service = new EnvironmentVariablesService(); + + // Act + const value = service.getSSMDecrypt(); + + // Assess + expect(value).toEqual('true'); + }); + + }); + +}); \ No newline at end of file