@aws-lambda-powertools/parameters
Version:
The parameters package for the Powertools for AWS Lambda (TypeScript) library
157 lines (156 loc) • 6.86 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseProvider = void 0;
const commons_1 = require("@aws-lambda-powertools/commons");
const EnvironmentVariablesService_js_1 = require("../config/EnvironmentVariablesService.js");
const errors_js_1 = require("../errors.js");
const ExpirableValue_js_1 = require("./ExpirableValue.js");
const GetMultipleOptions_js_1 = require("./GetMultipleOptions.js");
const GetOptions_js_1 = require("./GetOptions.js");
const transformValue_js_1 = require("./transformValue.js");
/**
* Base class for all providers.
*
* As an abstract class, it should not be used directly, but rather extended by other providers.
*
* It implements the common logic for all providers, such as caching, transformation, etc.
* Each provider that extends this class must implement the `_get` and `_getMultiple` abstract methods.
*
* These methods are responsible for retrieving the values from the underlying parameter store. They are
* called by the `get` and `getMultiple` methods, which are responsible for caching and transformation.
*
* If there are multiple calls to the same parameter but in a different transform, they will be stored multiple times.
* This allows us to optimize by transforming the data only once per retrieval, thus there is no need to transform cached values multiple times.
*
* However, this means that we need to make multiple calls to the underlying parameter store if we need to return it in different transforms.
*
* Since the number of supported transform is small and the probability that a given parameter will always be used in a specific transform,
* this should be an acceptable tradeoff.
*/
class BaseProvider {
envVarsService;
client;
store;
constructor({ awsSdkV3Client, clientConfig, awsSdkV3ClientPrototype, }) {
this.store = new Map();
this.envVarsService = new EnvironmentVariablesService_js_1.EnvironmentVariablesService();
if (awsSdkV3Client) {
if (!(0, commons_1.isSdkClient)(awsSdkV3Client) && awsSdkV3ClientPrototype) {
console.warn('awsSdkV3Client is not an AWS SDK v3 client, using default client');
this.client = new awsSdkV3ClientPrototype(clientConfig ?? {});
}
else {
this.client = awsSdkV3Client;
}
}
else if (awsSdkV3ClientPrototype) {
this.client = new awsSdkV3ClientPrototype(clientConfig ?? {});
}
if ((0, commons_1.isSdkClient)(this.client)) {
(0, commons_1.addUserAgentMiddleware)(this.client, 'parameters');
}
}
/**
* Add a value to the cache.
*
* @param {string} key - Key of the cached value
* @param {string | Uint8Array | Record<string, unknown>} value - Value to be cached
* @param {number} maxAge - Maximum age in seconds for the value to be cached
*/
addToCache(key, value, maxAge) {
if (maxAge <= 0)
return;
this.store.set(key, new ExpirableValue_js_1.ExpirableValue(value, maxAge));
}
/**
* Clear the cache.
*/
clearCache() {
this.store.clear();
}
/**
* Retrieve a parameter value or return the cached value.
*
* @param {string} name - Parameter name
* @param {GetOptionsInterface} options - Options to configure maximum age, trasformation, AWS SDK options, or force fetch
*/
async get(name, options) {
const configs = new GetOptions_js_1.GetOptions(this.envVarsService, options);
const key = [name, configs.transform].toString();
if (!configs.forceFetch && !this.hasKeyExpiredInCache(key)) {
return this.store.get(key)?.value;
}
try {
let value = await this._get(name, options);
if ((0, commons_1.isNullOrUndefined)(value))
return undefined;
if (configs.transform &&
((0, commons_1.isString)(value) || value instanceof Uint8Array)) {
value = (0, transformValue_js_1.transformValue)(value, configs.transform, true, name);
}
this.addToCache(key, value, configs.maxAge);
return value;
}
catch (error) {
if (error instanceof errors_js_1.TransformParameterError)
throw error;
throw new errors_js_1.GetParameterError(error.message);
}
}
/**
* Retrieve multiple parameter values or return the cached values.
*
* @param {string} path - Parameters path
* @param {GetMultipleOptionsInterface} options - Options to configure maximum age, trasformation, AWS SDK options, or force fetch
* @returns
*/
async getMultiple(path, options) {
const configs = new GetMultipleOptions_js_1.GetMultipleOptions(this.envVarsService, options);
const key = [path, configs.transform].toString();
if (!configs.forceFetch && !this.hasKeyExpiredInCache(key)) {
// biome-ignore lint/style/noNonNullAssertion: If the code enters in this block, then the key must exist & not have been expired
return this.store.get(key).value;
}
let values;
try {
values = await this._getMultiple(path, options);
if (!(0, commons_1.isRecord)(values)) {
throw new errors_js_1.GetParameterError(`Expected result to be a Record<string, unknown> but got ${typeof values}`);
}
}
catch (error) {
throw new errors_js_1.GetParameterError(error.message);
}
if (configs.transform) {
for (const [entryKey, entryValue] of Object.entries(values)) {
if (!((0, commons_1.isString)(entryValue) || entryValue instanceof Uint8Array))
continue;
try {
values[entryKey] = (0, transformValue_js_1.transformValue)(entryValue, configs.transform, configs.throwOnTransformError, entryKey);
}
catch (error) {
if (configs.throwOnTransformError)
throw new errors_js_1.TransformParameterError(configs.transform, error.message);
}
}
}
if (Object.keys(values).length !== 0) {
this.addToCache(key, values, configs.maxAge);
}
return values;
}
/**
* Check whether a key has expired in the cache or not.
*
* It returns true if the key is expired or not present in the cache.
*
* @param {string} key - Stringified representation of the key to retrieve
*/
hasKeyExpiredInCache(key) {
const value = this.store.get(key);
if (value)
return value.isExpired();
return true;
}
}
exports.BaseProvider = BaseProvider;
;