Skip to content

chore(commons): consolidate boolean parsing #3970

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 46 additions & 102 deletions packages/commons/src/envUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ const getNumberFromEnv = ({
return parsedValue;
};

const truthyValues = new Set(['1', 'y', 'yes', 't', 'true', 'on']);
const falsyValues = new Set(['0', 'n', 'no', 'f', 'false', 'off']);

/**
* Get a boolean from the environment variables.
*
Expand Down Expand Up @@ -140,128 +143,67 @@ const getNumberFromEnv = ({
* });
* ```
*
* @param options - The options for getting the boolean.
* @param options.key - The key of the environment variable.
* @param options.defaultValue - The default value to return if the environment variable is not set.
* @param options.errorMessage - Optional error message to throw if the environment variable is not set and no default value is provided. Defaults to `"Environment variable <key> is required"`.
*/
const getBooleanFromEnv = ({
key,
defaultValue,
errorMessage,
}: GetBooleanFromEnvOptions): boolean => {
const value = getStringFromEnv({
key,
defaultValue: String(defaultValue),
errorMessage,
});

const parsedValue = value.toLowerCase();

if (parsedValue !== 'true' && parsedValue !== 'false') {
throw new Error(`Environment variable ${key} must be a boolean`);
}

return parsedValue === 'true';
};

const truthyValues = new Set(['1', 'y', 'yes', 't', 'true', 'on']);

/**
* Get a truthy boolean from the environment variables.
*
* Truthy values are: `1`, `y`, `yes`, `t`, `true`, `on`.
* By default, the value is parsed as a boolean. You can also provide an option to extend the parsing of the boolean value to include common string representations.
*
* @example
* ```ts
* import { getTruthyBooleanFromEnv } from '@aws-lambda-powertools/commons/utils/env';
* import { getBooleanFromEnv } from '@aws-lambda-powertools/commons/utils/env';
*
* const myEnvVar = getTruthyBooleanFromEnv({
* const myEnvVar = getBooleanFromEnv({
* key: 'MY_ENV_VAR',
* errorMessage: 'MY_ENV_VAR is required for this function',
* defaultValue: true,
* extendedParsing: true,
* });
* ```
*
* By default, the value is trimmed before being converted to a boolean and always required.
*
* You can also provide a default value, which will be returned if the environment variable is not set instead of throwing an error.
*
* @example
* ```ts
* import { getTruthyBooleanFromEnv } from '@aws-lambda-powertools/commons/utils/env';
* The following values are considered `true`:
* - `"true"`
* - `"1"`
* - `"yes"`
* - `"on"`
* - `"y"`
*
* const myEnvVar = getTruthyBooleanFromEnv({
* key: 'MY_ENV_VAR',
* defaultValue: true,
* });
* ```
* The following values are considered `false`:
* - `"false"`
* - `"0"`
* - `"no"`
* - `"off"`
* - `"n"`
*
* @param options - The options for getting the truthy boolean.
* @param options - The options for getting the boolean.
* @param options.key - The key of the environment variable.
* @param options.defaultValue - The default value to return if the environment variable is not set.
* @param options.errorMessage - Optional error message to throw if the environment variable is not set and no default value is provided. Defaults to `"Environment variable <key> is required"`.
* @param options.extendedParsing - Whether to extend the parsing of the boolean value to include common string representations like `'1'`, `'y'`, `'yes'`, `'t'`, `'true'`, `'on'` for `true` and `'0'`, `'n'`, `'no'`, `'f'`, `'false'`, `'off'` for `false`.
*/
const getTruthyBooleanFromEnv = ({
const getBooleanFromEnv = ({
key,
defaultValue,
errorMessage,
extendedParsing,
}: GetBooleanFromEnvOptions): boolean => {
const value = getStringFromEnv({
key,
defaultValue: String(defaultValue),
errorMessage,
});

return truthyValues.has(value.toLowerCase());
};
const parsedValue = value.toLowerCase();

const falsyValues = new Set(['0', 'n', 'no', 'f', 'false', 'off']);
if (extendedParsing) {
if (truthyValues.has(parsedValue)) {
return true;
}
if (falsyValues.has(parsedValue)) {
return false;
}
}

/**
* Get a falsy boolean from the environment variables.
*
* Falsy values are: `0`, `n`, `no`, `f`, `false`, `off`.
*
* @example
* ```ts
* import { getFalsyBooleanFromEnv } from '@aws-lambda-powertools/commons/utils/env';
*
* const myEnvVar = getFalsyBooleanFromEnv({
* key: 'MY_ENV_VAR',
* errorMessage: 'MY_ENV_VAR is required for this function',
* });
* ```
*
* By default, the value is trimmed before being converted to a boolean and always required.
*
* You can also provide a default value, which will be returned if the environment variable is not set instead of throwing an error.
*
* @example
* ```ts
* import { getFalsyBooleanFromEnv } from '@aws-lambda-powertools/commons/utils/env';
*
* const myEnvVar = getFalsyBooleanFromEnv({
* key: 'MY_ENV_VAR',
* defaultValue: false,
* });
* ```
*
* @param options - The options for getting the falsy boolean.
* @param options.key - The key of the environment variable.
* @param options.defaultValue - The default value to return if the environment variable is not set.
* @param options.errorMessage - Optional error message to throw if the environment variable is not set and no default value is provided. Defaults to `"Environment variable <key> is required"`.
*/
const getFalsyBooleanFromEnv = ({
key,
defaultValue,
errorMessage,
}: GetBooleanFromEnvOptions): boolean => {
const value = getStringFromEnv({
key,
defaultValue: String(defaultValue),
errorMessage,
});
return falsyValues.has(value.toLowerCase());
if (parsedValue !== 'true' && parsedValue !== 'false') {
throw new Error(`Environment variable ${key} must be a boolean`);
}

return parsedValue === 'true';
};

/**
Expand All @@ -277,10 +219,14 @@ const getFalsyBooleanFromEnv = ({
* ```
*/
const isDevMode = (): boolean => {
return getTruthyBooleanFromEnv({
key: POWERTOOLS_DEV_ENV_VAR,
defaultValue: false,
});
try {
return getBooleanFromEnv({
key: POWERTOOLS_DEV_ENV_VAR,
extendedParsing: true,
});
} catch (error) {
return false;
}
};

/**
Expand Down Expand Up @@ -359,8 +305,6 @@ export {
getStringFromEnv,
getNumberFromEnv,
getBooleanFromEnv,
getTruthyBooleanFromEnv,
getFalsyBooleanFromEnv,
isDevMode,
getServiceName,
getXrayTraceDataFromEnv,
Expand Down
20 changes: 20 additions & 0 deletions packages/commons/src/types/envUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ type GetBooleanFromEnvOptions = {
* @default "Environment variable <key> is required"
*/
errorMessage?: string;
/**
* Whether to extend the parsing of the boolean value to include common string representations.
*
* The following values are considered `true`:
* - `"true"`
* - `"1"`
* - `"yes"`
* - `"on"`
* - `"y"`
*
* The following values are considered `false`:
* - `"false"`
* - `"0"`
* - `"no"`
* - `"off"`
* - `"n"`
*
* @default false
*/
extendedParsing?: boolean;
};

export type {
Expand Down
97 changes: 13 additions & 84 deletions packages/commons/tests/unit/envUtils.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { beforeEach, describe, expect, it } from 'vitest';
import {
getBooleanFromEnv,
getFalsyBooleanFromEnv,
getNumberFromEnv,
getServiceName,
getStringFromEnv,
getTruthyBooleanFromEnv,
getXRayTraceIdFromEnv,
isDevMode,
isRequestXRaySampled,
Expand Down Expand Up @@ -162,9 +160,7 @@ describe('Functions: envUtils', () => {
'Environment variable TEST_ENV must be a boolean'
);
});
});

describe('Function: getTruthyBooleanFromEnv', () => {
it.each([
['1', true],
['y', true],
Expand All @@ -179,106 +175,39 @@ describe('Functions: envUtils', () => {
process.env.TEST_ENV = value;

// Act
const result = getTruthyBooleanFromEnv({ key: 'TEST_ENV' });
const result = getBooleanFromEnv({
key: 'TEST_ENV',
extendedParsing: true,
});

// Assess
expect(result).toBe(expected);
}
);

it.each([
['', false],
['false', false],
['fasle', false],
['somethingsilly', false],
['0', false],
['n', false],
['no', false],
['f', false],
['FALSE', false],
['off', false],
])(
'returns false if the environment variable is set to a falsy value: %s',
(value, expected) => {
// Prepare
process.env.TEST_ENV = value;

// Act
const result = getTruthyBooleanFromEnv({ key: 'TEST_ENV' });

// Assess
expect(result).toBe(expected);
}
);

it('returns the default value if the environment variable is not set', () => {
// Prepare
process.env.TEST_ENV = undefined;

// Act
const result = getTruthyBooleanFromEnv({
key: 'TEST_ENV',
defaultValue: true,
});

// Assess
expect(result).toBe(true);
});
});

describe('Function: getFalsyBooleanFromEnv', () => {
it.each([
['0', true],
['n', true],
['no', true],
['f', true],
['FALSE', true],
['off', true],
])(
'returns true if the environment variable is set to a truthy value: %s',
(value, expected) => {
// Prepare
process.env.TEST_ENV = value;

// Act
const result = getFalsyBooleanFromEnv({ key: 'TEST_ENV' });

// Assess
expect(result).toBe(expected);
}
);

it.each([
['1', false],
['y', false],
['yes', false],
['t', false],
['TRUE', false],
['on', false],
['', false],
['somethingsilly', false],
])(
'returns false if the environment variable is set to a falsy value: %s',
(value, expected) => {
// Prepare
process.env.TEST_ENV = value;

// Act
const result = getFalsyBooleanFromEnv({ key: 'TEST_ENV' });
const result = getBooleanFromEnv({
key: 'TEST_ENV',
extendedParsing: true,
});

// Assess
expect(result).toBe(expected);
}
);

it('returns the default value if the environment variable is not set', () => {
// Prepare
process.env.TEST_ENV = undefined;

// Act
const result = getFalsyBooleanFromEnv({
key: 'TEST_ENV',
defaultValue: false,
});

// Assess
expect(result).toBe(true);
});
});

describe('Function: isDevMode', () => {
Expand Down