UNPKG

@aws-lambda-powertools/parameters

Version:
157 lines (156 loc) 6.86 kB
"use strict"; 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;