aws-core-utils
Advanced tools
Comparing version 2.0.1 to 2.1.0
'use strict'; | ||
let AWS = require("aws-sdk"); | ||
let kinesis = null; // simple, single module-scope cache for an AWS.Kinesis instance | ||
// Module-scope cache of AWS.Kinesis instances by region key | ||
let kinesisByRegionKey = new WeakMap(); | ||
// Module-scope cache of the Kinesis options used to construct the AWS.Kinesis instances by region key | ||
let kinesisOptionsByRegionKey = new WeakMap(); | ||
// A map of region key objects by region, which is only needed, because WeakMaps can ONLY have object keys | ||
const regionKeysByRegion = new Map(); | ||
const regions = require('./regions'); | ||
const Strings = require('core-functions/strings'); | ||
const stringify = Strings.stringify; | ||
const deepEqual = require('deep-equal'); | ||
const strict = {strict:true}; | ||
/** | ||
* Utilities for working with AWS.Kinesis and a simple, module-scope cache for a single AWS.Kinesis instance for Lambda. | ||
* Utilities for working with AWS.Kinesis and a module-scope cache of AWS.Kinesis instances by region for Lambda. | ||
* @module aws-core-utils/kinesis-utils | ||
@@ -14,2 +27,6 @@ * @author Byron du Preez | ||
module.exports = { | ||
setKinesis: setKinesis, | ||
getKinesis: getKinesis, | ||
getKinesisOptionsUsed: getKinesisOptionsUsed, | ||
deleteKinesis: deleteKinesis, | ||
configureKinesis: configureKinesis | ||
@@ -19,43 +36,145 @@ }; | ||
/** | ||
* Configures the given context, if it does not already have a context.kinesis, with the cached kinesis instance (if | ||
* any and if its region & maxRetries matches the current region and given maxRetries); otherwise with a new AWS.Kinesis | ||
* instance, which will be cached if there is no existing cached instance. | ||
* Creates and caches a new AWS Kinesis instance with the given Kinesis constructor options for either the region | ||
* specified in the given options (if any and region specified) or for the current region (if not) UNLESS a previously | ||
* cached Kinesis instance exists and the given options EITHER match the options used to construct it OR are undefined, | ||
* empty or only region, in which case no new instance will be created and the cached instance will be returned instead. | ||
* If the given options do not match existing options and are not empty and not only region, then logs a warning that | ||
* the previously cached Kinesis instance is being replaced and returns the new AWS Kinesis instance. | ||
* | ||
* Logging should be configured before calling this function (see {@linkcode logging-utils#configureLogging} | ||
* | ||
* @param {Object} context - the context to configure | ||
* @param {number} maxRetries - the maximum number of retries to configure on the AWS.Kinesis instance | ||
* @returns {Object} the given context | ||
* @param {Object|undefined} [kinesisOptions] - the optional Kinesis constructor options to use | ||
* @param {string|undefined} [kinesisOptions.region] - an optional region to use instead of the current region | ||
* @param {Object|undefined} [context] - the context, which is just used for logging | ||
* @returns {Object} an cached or new AWS Kinesis instance created and cached for the specified or current region | ||
*/ | ||
function configureKinesis(context, maxRetries) { | ||
const region = regions.getRegion(); | ||
function setKinesis(kinesisOptions, context) { | ||
// If no options were specified, then use an empty object | ||
const options = kinesisOptions ? kinesisOptions : {}; | ||
const contextKinesisIncompatible = context.kinesis && context.kinesis.config && | ||
(context.kinesis.config.region !== region || context.kinesis.config.maxRetries !== maxRetries); | ||
if (contextKinesisIncompatible) { | ||
if (context.warnEnabled) context.warn(`Existing context.kinesis with region (${context.kinesis.config.region}) & maxRetries (${context.kinesis.config.maxRetries}) is incompatible with region (${region}) & maxRetries (${maxRetries}) and will be replaced!`); | ||
// If no region was specified in the given kinesis options, then set it to the current region | ||
let region = options.region; | ||
if (!region) { | ||
const currentRegion = regions.getRegion(); | ||
options.region = currentRegion; | ||
region = currentRegion; | ||
} | ||
const regionKey = getRegionKey(region); | ||
if (!context.kinesis || contextKinesisIncompatible) { | ||
if (kinesis && kinesis.config) { | ||
if (kinesis.config.region === region && kinesis.config.maxRetries === maxRetries) { | ||
// Use cached kinesis instance | ||
if (context.debugEnabled) context.debug(`Using cached kinesis instance with region (${kinesis.config.region}) & maxRetries (${kinesis.config.maxRetries})`); | ||
context.kinesis = kinesis; | ||
} else { | ||
// Cached kinesis instance has the wrong region and/or maxRetries, so create a new one | ||
if (context.debugEnabled) context.debug(`Cached kinesis has incompatible region (${kinesis.config.region}) & maxRetries (${kinesis.config.maxRetries}), so creating a new context.kinesis with region (${region}) & maxRetries (${maxRetries})`); | ||
context.kinesis = new AWS.Kinesis({region: region, maxRetries: maxRetries}); | ||
} | ||
// Check if there is already a Kinesis instance cached for this region | ||
let kinesis = kinesisByRegionKey.get(regionKey); | ||
if (kinesis) { | ||
const logInfo = context && context.info ? context.info : console.log; | ||
// If caller specified no options, then accept the cached instance for the current region (regardless of its options) | ||
if (!kinesisOptions || Object.getOwnPropertyNames(kinesisOptions).length === 0) { | ||
logInfo(`Reusing cached Kinesis instance for region (${region}) with ANY options, since no options were specified`); | ||
return kinesis; | ||
} | ||
// If caller ONLY specified a region, then accept the cached instance for the region (regardless of its options) | ||
if (Object.getOwnPropertyNames(options).length === 1) { | ||
logInfo(`Reusing cached Kinesis instance for region (${region}) with ANY options, since only region was specified`); | ||
return kinesis; | ||
} | ||
// If the given options match the options used to construct the cached instance, then returns the cached instance | ||
const optionsUsed = kinesisOptionsByRegionKey.get(regionKey); | ||
// context.debug(`options = ${JSON.stringify(options)}`); | ||
// context.debug(`optionsUsed = ${JSON.stringify(optionsUsed)}`); | ||
// context.debug(`optionsUsed == options = ${optionsUsed == options}`); | ||
// context.debug(`optionsUsed === options = ${optionsUsed === options}`); | ||
// context.debug(`deepEqual(optionsUsed, options) = ${deepEqual(optionsUsed, options)}`); | ||
if (deepEqual(optionsUsed, options, strict)) { | ||
// Use the cached instance if its config is identical to the modified options | ||
logInfo(`Reusing cached Kinesis instance for region (${region}) with identical options`); | ||
return kinesis; | ||
} else { | ||
// No cached kinesis yet, so create one and cache it | ||
kinesis = new AWS.Kinesis({region: region, maxRetries: maxRetries}); | ||
if (context.debugEnabled) context.debug(`Cached a new kinesis instance with region (${kinesis.config.region}) & maxRetries (${kinesis.config.maxRetries})`); | ||
context.kinesis = kinesis; | ||
const logWarn = context && context.warn ? context.warn : console.warn; | ||
logWarn(`Replacing cached Kinesis instance (${stringify(optionsUsed)}) for region (${region}) with new instance (${stringify(options)})`); | ||
} | ||
} else { | ||
if (context.debugEnabled) context.debug(`Using compatible, existing context.kinesis instance with region (${context.kinesis.config.region}) & maxRetries (${context.kinesis.config.maxRetries})`); | ||
} | ||
// Create a new kinesis instance with the modified options | ||
kinesis = new AWS.Kinesis(options); | ||
// Cache the new instance and the options used to create it | ||
kinesisByRegionKey.set(regionKey, kinesis); | ||
kinesisOptionsByRegionKey.set(regionKey, options); | ||
return kinesis; | ||
} | ||
/** | ||
* Deletes the Kinesis instance cached for the given region (if any) and returns true if successfully deleted or false | ||
* if it did not exist. | ||
* @param {string} region - the AWS region to use as a key | ||
* @returns {boolean} true if existed and deleted; false otherwise | ||
*/ | ||
function deleteKinesis(region) { | ||
const regionKey = getRegionKey(region); | ||
kinesisOptionsByRegionKey.delete(regionKey); | ||
return kinesisByRegionKey.delete(regionKey); | ||
} | ||
/** | ||
* Gets the Kinesis instance cached for the given region (if specified and if previously cached); otherwise for the | ||
* current region (if previously cached); otherwise returns undefined. | ||
* @param {string} [region] - the optional AWS region to use - defaults to current AWS region if not specified | ||
* @returns {Object|undefined} the Kinesis instance cached for the given or current region (if any); otherwise returns undefined | ||
*/ | ||
function getKinesis(region) { | ||
const regionKey = getRegionKey(region ? region : regions.getRegion()); | ||
return kinesisByRegionKey.get(regionKey); | ||
} | ||
/** | ||
* Gets the kinesis options used to construct the Kinesis instance cached for the given region (if specified and if | ||
* previously cached); otherwise for the current region (if previously cached); otherwise returns undefined. | ||
* @param {string} [region] - the optional AWS region to use - defaults to current AWS region if not specified | ||
* @returns {Object|undefined} the kinesis options used to construct the Kinesis instance cached for the given or | ||
* current region (if any); otherwise returns undefined | ||
*/ | ||
function getKinesisOptionsUsed(region) { | ||
const regionKey = getRegionKey(region ? region : regions.getRegion()); | ||
return kinesisOptionsByRegionKey.get(regionKey); | ||
} | ||
function getRegionKey(region) { | ||
let regionKey = regionKeysByRegion.get(region); | ||
if (!regionKey) { | ||
regionKey = {region: region}; | ||
regionKeysByRegion.set(region, regionKey); | ||
} | ||
return regionKey; | ||
} | ||
/** | ||
* Creates and caches a new AWS Kinesis instance with the given Kinesis constructor options for either the region | ||
* specified in the given options (if any and region specified) or for the current region (if not) UNLESS a previously | ||
* cached Kinesis instance exists and the given options either match the options used to construct it or are undefined, | ||
* empty or only region was specified, in which case no new instance will be created and the cached instance will | ||
* be returned instead. If the given options do not match existing options and are not empty and not only region, then | ||
* logs a warning that the previously cached Kinesis instance is being replaced and returns the new AWS Kinesis | ||
* instance. | ||
* | ||
* Logging should be configured before calling this function (see {@linkcode logging-utils#configureLogging} | ||
* | ||
* Configures the given context, if it does not already have a context.kinesis, with the cached kinesis instance for | ||
* either the region specified in the given default kinesis options (if any and region specified) or for the current | ||
* region (if not); otherwise with a new AWS.Kinesis instance created and cached by {@linkcode setKinesis} for | ||
* the specified or current region using the given default Kinesis constructor options. | ||
* | ||
* Note that the given default Kinesis constructor options will ONLY be used if no cached Kinesis instance exists. | ||
* | ||
* Logging should be configured before calling this function (see {@linkcode logging-utils#configureLogging} | ||
* | ||
* @param {Object} context - the context to configure | ||
* @param {Object} [context.kinesis] - the current Kinesis instance cached on the context (if any) | ||
* @param {Object|undefined} [kinesisOptions] - the optional Kinesis constructor options to use if no cached Kinesis | ||
* instance exists | ||
* @param {string|undefined} [kinesisOptions.region] - an optional region to use instead of the current region | ||
* @returns {Object} the given context | ||
*/ | ||
function configureKinesis(context, kinesisOptions) { | ||
if (!context.kinesis) { | ||
context.kinesis = setKinesis(kinesisOptions, context); | ||
} | ||
return context; | ||
} |
{ | ||
"name": "aws-core-utils", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"description": "Core utilities for working with Amazon Web Services (AWS), including ARNs, regions, stages, Lambdas, AWS errors, stream events, etc.", | ||
@@ -15,3 +15,4 @@ "author": "Byron du Preez", | ||
"core-functions": "^2.0.2", | ||
"logging-utils": "^1.0.5" | ||
"logging-utils": "^1.0.5", | ||
"deep-equal": "^1.0.1" | ||
}, | ||
@@ -18,0 +19,0 @@ "devDependencies": { |
@@ -1,2 +0,2 @@ | ||
# aws-core-utils v2.0.1 | ||
# aws-core-utils v2.1.0 | ||
@@ -12,3 +12,3 @@ Core utilities for working with Amazon Web Services (AWS), including ARNs, regions, stages, Kinesis, Lambdas, AWS errors, stream events, etc. | ||
- kinesis-utils.js | ||
- Utilities for working with AWS.Kinesis and a module-scope cache for a single AWS.Kinesis instance for Lambda. | ||
- Utilities for working with AWS.Kinesis and a module-scope cache of AWS.Kinesis instances by region for Lambda. | ||
- lambdas.js | ||
@@ -67,9 +67,38 @@ - Utilities for working with AWS Lambda, which enable extraction of function names, versions and, most importantly, | ||
* To use the Kinesis utilities | ||
* To use the Kinesis utilities to cache and configure an AWS Kinesis instance per region | ||
```js | ||
const kinesisUtils = require('aws-core-utils/kinesis-utils'); | ||
// Preamble to create a context and configure logging on the context | ||
const context = {}; | ||
const logging = require('logging-utils'); | ||
logging.configureDefaultLogging(context); | ||
// Define the Kinesis constructor options that you want to use, e.g. | ||
const kinesisOptions = { | ||
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Kinesis.html#constructor-property for full details | ||
maxRetries: 0 | ||
// ... | ||
}; | ||
// To create and cache a new AWS Kinesis instance with the given Kinesis constructor options for either the current | ||
// region or the region specified in the given options OR reuse a previously cached Kinesis instance (if any) that is | ||
// compatible with the given options | ||
const kinesis = kinesisUtils.setKinesis(kinesisOptions, context); | ||
// To configure a new AWS.Kinesis instance (or re-use a cached instance) on a context | ||
// Currently only creates a new AWS.Kinesis instance with the current AWS region & given maxRetries | ||
kinesisUtils.configureKinesis(context, maxRetries); | ||
kinesisUtils.configureKinesis(context, kinesisOptions); | ||
console.log(context.kinesis); | ||
// To get a previously set or configured AWS Kinesis instance for the current AWS region | ||
const kinesis1 = kinesisUtils.getKinesis(); | ||
// ... or for a specified region | ||
const kinesis2 = kinesisUtils.getKinesis('us-west-2'); | ||
// To get the original options that were used to construct a cached AWS Kinesis instance for the current or specified AWS region | ||
const optionsUsed1 = kinesisUtils.getKinesisOptionsUsed(); | ||
const optionsUsed2 = kinesisUtils.getKinesisOptionsUsed('us-west-1'); | ||
// To delete and remove a cached Kinesis instance from the cache | ||
const deleted = kinesisUtils.deleteKinesis('eu-west-1'); | ||
``` | ||
@@ -121,7 +150,29 @@ | ||
// To configure completely customised stage handling of the above 4 functions | ||
stages.configureStageHandling(context, customToStage, convertAliasToStage, | ||
injectStageIntoStreamName, extractStageFromStreamName, streamNameStageSeparator, | ||
injectStageIntoResourceName, extractStageFromResourceName, resourceNameStageSeparator, | ||
injectInCase, extractInCase, defaultStage, forceConfiguration); | ||
// 5. To qualify an unqualified resource name with a stage | ||
const unqualifiedResourceName = 'TestResource'; | ||
const stageQualifiedResourceName = stages.toStageQualifiedResourceName(unqualifiedResourceName, stage, context); | ||
// 6. To extract a stage from a qualified resource name | ||
const qualifiedResourceName = 'TestResource_QA'; | ||
const stage3 = stages.extractStageFromQualifiedResourceName(qualifiedResourceName, context); | ||
// To configure completely customised stage handling of the above 6 functions | ||
const settings = { | ||
customToStage: customToStage, | ||
convertAliasToStage: convertAliasToStage, | ||
injectStageIntoStreamName: injectStageIntoStreamName, | ||
extractStageFromStreamName: extractStageFromStreamName, | ||
streamNameStageSeparator: streamNameStageSeparator, | ||
injectStageIntoResourceName: injectStageIntoResourceName, | ||
extractStageFromResourceName: extractStageFromResourceName, | ||
resourceNameStageSeparator: resourceNameStageSeparator, | ||
injectInCase: injectInCase, | ||
extractInCase: extractInCase, | ||
defaultStage: defaultStage, | ||
} | ||
stages.configureStageHandling(context, settings, forceConfiguration); | ||
@@ -177,2 +228,15 @@ // To check if stage handling is configured | ||
### 2.1.0 | ||
- Changes to `stages` module: | ||
- Changed API of `configureStageHandling` function to accept a setting object instead of the multiple fixed parameters, | ||
to simplify configuration of new, custom settings. | ||
- Minor changes and fixes to code & unit tests to accommodate this change. | ||
- Major overhaul of `kinesis-utils` module to enable full configuration of an AWS Kinesis instance and caching of a Kinesis | ||
instance per region. | ||
- Added `setKinesis`, `getKinesis`, `getKinesisOptionsUsed` & `deleteKinesis` functions and unit tests for same. | ||
- Rewrote and changed API of `configureKinesis` function to use the new `setKinesis` function and patched its unit tests. | ||
- Technically should have been a 3.0.0 release semantically speaking, since I changed the APIs of two existing functions, | ||
but it did not seem warranted. | ||
### 2.0.1 | ||
@@ -179,0 +243,0 @@ - Added new `kinesis-utils` module to provide basic configuration and caching of an AWS.Kinesis instance for Lambda |
116
stages.js
@@ -92,8 +92,10 @@ 'use strict'; | ||
/** | ||
* Configures the given context with the given stage handling settings, but only if stage handling is not already | ||
* configured on the given context OR if forceConfiguration is true. The stage handling settings determine how | ||
* {@linkcode resolveStage}, {@linkcode toStageQualifiedStreamName}, {@linkcode extractStageFromQualifiedStreamName}, | ||
* {@linkcode toStageQualifiedResourceName}, {@linkcode extractStageFromQualifiedStreamName} and other internal | ||
* functions will behave when invoked. | ||
* Stage handling settings used for configuring and customising stage handling behaviour. The stage handling settings | ||
* determine how {@linkcode resolveStage}, {@linkcode toStageQualifiedStreamName}, | ||
* {@linkcode extractStageFromQualifiedStreamName}, {@linkcode toStageQualifiedResourceName}, | ||
* {@linkcode extractStageFromQualifiedStreamName} and other internal functions will behave when invoked. | ||
* | ||
* NB: Add any, new custom settings that you need for any custom implementations of some or all of the stage handling | ||
* functions that you develop and configure via {@linkcode configureStageHandling}. | ||
* | ||
* Notes: | ||
@@ -108,48 +110,44 @@ * - If injectInCase is set to 'upper' then extractInCase should typically be set to 'lower'. | ||
* | ||
* @param {Object} context the context onto which to configure stage handling settings | ||
* | ||
* @param {Function|undefined} [customToStage] - an optional custom function that accepts: an AWS event; an AWS context; | ||
* @typedef {Object} StageHandlingSettings | ||
* @property {Function|undefined} [customToStage] - an optional custom function that accepts: an AWS event; an AWS context; | ||
* and a context, and somehow extracts a usable stage from the AWS event and/or AWS context. | ||
* | ||
* @param {Function|undefined} [convertAliasToStage] - an optional function that accepts: an extracted alias (if any); | ||
* @property {Function|undefined} [convertAliasToStage] - an optional function that accepts: an extracted alias (if any); | ||
* an AWS event; an AWS context; and a context, and converts the alias into a stage | ||
* @param {Function|undefined} [injectStageIntoStreamName] - an optional function that accepts: an unqualified stream | ||
* @property {Function|undefined} [injectStageIntoStreamName] - an optional function that accepts: an unqualified stream | ||
* name; a stage; and a context, and returns a stage-qualified stream name (effectively the reverse function of the | ||
* extractStageFromStreamName function) | ||
* | ||
* @param {Function|undefined} [extractStageFromStreamName] - an optional function that accepts: a stage-qualified | ||
* @property {Function|undefined} [extractStageFromStreamName] - an optional function that accepts: a stage-qualified | ||
* stream name; and a context, and extracts a stage from the stream name | ||
* | ||
* @param {string|undefined} [streamNameStageSeparator] - an optional non-blank separator to use to extract a stage from | ||
* @property {string|undefined} [streamNameStageSeparator] - an optional non-blank separator to use to extract a stage from | ||
* a stage-qualified stream name or inject a stage into an unqualified stream name | ||
* | ||
* @param {Function|undefined} [injectStageIntoResourceName] - an optional function that accepts: an unqualified | ||
* @property {Function|undefined} [injectStageIntoResourceName] - an optional function that accepts: an unqualified | ||
* resource name; a stage; and a context, and returns a stage-qualified resource name (effectively the reverse function | ||
* of the extractStageFromResourceName function) | ||
* | ||
* @param {Function|undefined} [extractStageFromResourceName] - an optional function that accepts: a stage-qualified | ||
* @property {Function|undefined} [extractStageFromResourceName] - an optional function that accepts: a stage-qualified | ||
* resource name; and a context, and extracts a stage from the resource name | ||
* | ||
* @param {string|undefined} [resourceNameStageSeparator] - an optional non-blank separator to use to extract a stage | ||
* @property {string|undefined} [resourceNameStageSeparator] - an optional non-blank separator to use to extract a stage | ||
* from a stage-qualified resource name or inject a stage into an unqualified resource name | ||
* | ||
* @param {string|undefined} [injectInCase] - optionally specifies whether to convert an injected stage to uppercase (if | ||
* @property {string|undefined} [injectInCase] - optionally specifies whether to convert an injected stage to uppercase (if | ||
* 'upper' or 'uppercase') or to lowercase (if 'lowercase' or 'lower') or keep it as given (if 'as_is' or anything else) | ||
* | ||
* @param {string|undefined} [extractInCase] - optionally specifies whether to convert an extracted stage to uppercase | ||
* @property {string|undefined} [extractInCase] - optionally specifies whether to convert an extracted stage to uppercase | ||
* (if 'upper' or 'uppercase') or to lowercase (if 'lowercase' or 'lower') or keep it as extracted (if 'as_is' or | ||
* anything else) | ||
* @property {string|undefined} [defaultStage] - an optional default stage to use as a last resort if all other attempts fail | ||
*/ | ||
/** | ||
* Configures the given context with the given stage handling settings, but only if stage handling is not already | ||
* configured on the given context OR if forceConfiguration is true. The stage handling settings determine how | ||
* {@linkcode resolveStage}, {@linkcode toStageQualifiedStreamName}, {@linkcode extractStageFromQualifiedStreamName}, | ||
* {@linkcode toStageQualifiedResourceName}, {@linkcode extractStageFromQualifiedStreamName} and other internal | ||
* functions will behave when invoked. | ||
* | ||
* @param {string|undefined} [defaultStage] - an optional default stage to use as a last resort if all other attempts fail | ||
* | ||
* @param {Object} context the context onto which to configure stage handling settings | ||
* @param {StageHandlingSettings} [context.stageHandling] - previously configured stage handling settings on the context (if any) | ||
* @param {StageHandlingSettings} settings - the new stage handling settings to use | ||
* @param {boolean|undefined} [forceConfiguration] - whether or not to force configuration of the given settings, which | ||
* will override any previously configured stage handling settings on the given context | ||
* | ||
* @return {Object} the context object configured with stage handling settings | ||
*/ | ||
function configureStageHandling(context, customToStage, convertAliasToStage, | ||
injectStageIntoStreamName, extractStageFromStreamName, streamNameStageSeparator, | ||
injectStageIntoResourceName, extractStageFromResourceName, resourceNameStageSeparator, | ||
injectInCase, extractInCase, defaultStage, forceConfiguration) { | ||
function configureStageHandling(context, settings, forceConfiguration) { | ||
@@ -162,19 +160,3 @@ // If forceConfiguration is false check if the given context already has stage resolution configured on it | ||
// Configure the stage handling settings | ||
context.stageHandling = { | ||
customToStage: customToStage, | ||
convertAliasToStage: convertAliasToStage, | ||
injectStageIntoStreamName: injectStageIntoStreamName, | ||
extractStageFromStreamName: extractStageFromStreamName, | ||
streamNameStageSeparator: streamNameStageSeparator, | ||
injectStageIntoResourceName: injectStageIntoResourceName, | ||
extractStageFromResourceName: extractStageFromResourceName, | ||
resourceNameStageSeparator: resourceNameStageSeparator, | ||
injectInCase: injectInCase, | ||
extractInCase: extractInCase, | ||
defaultStage: defaultStage, | ||
}; | ||
context.stageHandling = settings; | ||
return context; | ||
@@ -202,3 +184,4 @@ } | ||
* | ||
* @param {Object} context - the context onto which to configure stage handling settings | ||
* @param {Object} context - the context onto which to configure the default stage handling settings | ||
* @param {StageHandlingSettings} [context.stageHandling] - previously configured stage handling settings on the context (if any) | ||
* @param {boolean|undefined} forceConfiguration - whether or not to force configuration of the given settings, which | ||
@@ -228,6 +211,21 @@ * will override any previously configured stage handling settings on the given context | ||
return configureStageHandling(context, undefined, convertAliasToStage, | ||
toStageSuffixedStreamName, extractStageFromSuffixedStreamName, streamNameStageSeparator, | ||
toStageSuffixedResourceName, extractStageFromSuffixedResourceName, resourceNameStageSeparator, | ||
injectInCase, extractInCase, undefined, forceConfiguration); | ||
const settings = { | ||
customToStage: undefined, | ||
convertAliasToStage: convertAliasToStage, | ||
injectStageIntoStreamName: toStageSuffixedStreamName, | ||
extractStageFromStreamName: extractStageFromSuffixedStreamName, | ||
streamNameStageSeparator: streamNameStageSeparator, | ||
injectStageIntoResourceName: toStageSuffixedResourceName, | ||
extractStageFromResourceName: extractStageFromSuffixedResourceName, | ||
resourceNameStageSeparator: resourceNameStageSeparator, | ||
injectInCase: injectInCase, | ||
extractInCase: extractInCase, | ||
defaultStage: undefined, | ||
}; | ||
return configureStageHandling(context, settings, forceConfiguration); | ||
} | ||
@@ -260,2 +258,4 @@ | ||
* If no stage handling settings have been configured yet, then configure the given context with the default settings. | ||
* @param {Object} context - the context to configure with default logging and stage handling | ||
* @param {string} caller - a short description to identify the caller of this function | ||
*/ | ||
@@ -315,6 +315,7 @@ function configureDefaultStageHandlingIfNotConfigured(context, caller) { | ||
* @param {Object} awsContext - the AWS context, which was passed to your lambda | ||
* @param {Object} context - the context, which can also be used to pass additional configuration through to any custom | ||
* convertAliasToStage or extractStageFromStreamName functions that you configured | ||
* @param {Object} context - the context to use, which will contain any and all of the pre-configured settings | ||
* @param {string|undefined} [context.stage] - an optional stage on the given context, which will short-circuit | ||
* resolution to this stage (if non-blank) | ||
* @param {StageHandlingSettings|undefined} [context.stageHandling] - the configured stage handling settings (if any) on | ||
* the given context, which can be used to pass additional configuration through to any custom functions that you configured | ||
* @param {Function|undefined} [context.stageHandling.customToStage] - an optional custom function that accepts: an | ||
@@ -714,2 +715,3 @@ * AWS event; an AWS context; and a context, and somehow extracts and returns a usable stage from the AWS event and/or | ||
* @param {Object} context - a context on which to set the stage | ||
* @param {Object} [context.stage] - a context on which to set the stage | ||
* @param {Object} event - the AWS event | ||
@@ -716,0 +718,0 @@ * @param {Object} awsContext - the AWS context, which was passed to your lambda |
@@ -12,2 +12,5 @@ 'use strict'; | ||
const kinesisUtils = require('../kinesis-utils'); | ||
const setKinesis = kinesisUtils.setKinesis; | ||
const getKinesis = kinesisUtils.getKinesis; | ||
const deleteKinesis = kinesisUtils.deleteKinesis; | ||
const configureKinesis = kinesisUtils.configureKinesis; | ||
@@ -17,10 +20,11 @@ | ||
const getRegion = regions.getRegion; | ||
const getRegionRaw = regions.ONLY_FOR_TESTING.getRegionRaw; | ||
// const getRegionRaw = regions.ONLY_FOR_TESTING.getRegionRaw; | ||
// const getDefaultRegion = regions.getDefaultRegion; | ||
// const resolveRegion = regions.resolveRegion; | ||
const setRegionIfNotSet = regions.ONLY_FOR_TESTING.setRegionIfNotSet; | ||
// const setRegionIfNotSet = regions.ONLY_FOR_TESTING.setRegionIfNotSet; | ||
const logging = require('logging-utils'); | ||
// const Strings = require('core-functions/strings'); | ||
const Strings = require('core-functions/strings'); | ||
const stringify = Strings.stringify; | ||
// const isBlank = Strings.isBlank; | ||
@@ -32,2 +36,120 @@ // const isNotBlank = Strings.isNotBlank; | ||
// ===================================================================================================================== | ||
// Tests for setKinesis and getKinesis | ||
// ===================================================================================================================== | ||
test('setKinesis and getKinesis', t => { | ||
const context = {}; | ||
logging.configureLogging(context, logging.TRACE); | ||
// Set current region | ||
process.env.AWS_REGION = 'us-west-1'; | ||
const region1 = getRegion(); | ||
t.equal(region1, 'us-west-1', `current region must be us-west-1`); | ||
deleteKinesis(region1); // make sure none before we start | ||
t.notOk(getKinesis(), `getKinesis() kinesis instance must not be cached yet`); | ||
t.notOk(getKinesis(region1), `getKinesis(${region1}) kinesis instance must not be cached yet`); | ||
// Cache new kinesis for current region | ||
const options0 = {}; | ||
const kinesis0 = setKinesis(options0, context); | ||
t.ok(kinesis0, `setKinesis(${stringify(options0)}) must return an instance`); | ||
t.equal(kinesis0.config.region, region1, `kinesis 0 region must be ${region1}`); | ||
t.equal(kinesis0.config.maxRetries, undefined, `kinesis 0 maxRetries (${kinesis0.config.maxRetries}) must be undefined`); | ||
t.equal(getKinesis(), kinesis0, `getKinesis() gets cached instance for current region (${region1})`); | ||
t.equal(getKinesis(region1), kinesis0, `getKinesis(${region1}) gets cached instance`); | ||
// Re-use cached kinesis for options with explicit region same as current | ||
const options1 = { region: region1 }; //, maxRetries: 0 }; | ||
const kinesis1 = setKinesis(options1, context); | ||
t.ok(kinesis1, `setKinesis(${stringify(options1)}) must return an instance`); | ||
t.equal(kinesis1.config.region, region1, `kinesis 1 region must be ${region1}`); | ||
t.equal(kinesis1.config.maxRetries, undefined, `kinesis 1 maxRetries must be undefined`); | ||
t.equal(kinesis1, kinesis0, `setKinesis(${stringify(options1)}) must re-use cached instance 0 with same options`); | ||
t.equal(getKinesis(), kinesis0, `getKinesis() gets cached instance 0 for current region (${region1})`); | ||
t.equal(getKinesis(region1), kinesis0, `getKinesis(${region1}) gets cached instance 0`); | ||
// Force replacement of cached instance when options differ | ||
const maxRetries2 = 0; //kinesis1.config.maxRetries ? kinesis1.config.maxRetries * 2; | ||
const options2 = { region: region1, maxRetries: maxRetries2 }; | ||
const kinesis2 = setKinesis(options2, context); | ||
t.ok(kinesis2, `setKinesis(${stringify(options2)}) must return an instance`); | ||
t.equal(kinesis2.config.region, region1, `kinesis 2 region must be ${region1}`); | ||
t.equal(kinesis2.config.maxRetries, maxRetries2, `kinesis 2 maxRetries must be ${maxRetries2}`); | ||
t.notEqual(kinesis2, kinesis0, `setKinesis(${stringify(options2)}) must replace incompatible cached instance 0`); | ||
t.equal(getKinesis(), kinesis2, `getKinesis() gets cached instance 2 for current region (${region1})`); | ||
t.equal(getKinesis(region1), kinesis2, `getKinesis(${region1}) gets cached instance 2`); | ||
// Re-use newly cached instance when options same, but diff sequence | ||
const maxRetries3 = 0; | ||
const options3 = { maxRetries: maxRetries3, region: region1 }; | ||
const kinesis3 = setKinesis(options3, context); | ||
t.ok(kinesis3, `setKinesis(${stringify(options3)}) must return an instance`); | ||
t.equal(kinesis3.config.region, region1, `kinesis 3 region must be ${region1}`); | ||
t.equal(kinesis3.config.maxRetries, maxRetries3, `kinesis 3 maxRetries must be ${maxRetries3}`); | ||
t.equal(kinesis3, kinesis2, `setKinesis(${stringify(options3)}) must re-use cached instance 2 with re-ordered options`); | ||
t.equal(getKinesis(), kinesis2, `getKinesis() gets cached instance 2 for current region (${region1})`); | ||
t.equal(getKinesis(region1), kinesis2, `getKinesis(${region1}) gets cached instance 2`); | ||
// Change to using a different region, which will cache a new kinesis instance under new region | ||
const region2 = 'us-west-2'; | ||
deleteKinesis(region2); // make sure none before we start | ||
t.notOk(getKinesis(region2), `getKinesis(${region2}) kinesis instance must not be cached yet`); | ||
t.equal(getKinesis(), kinesis2, `getKinesis() still gets cached instance 2 for current region (${region1})`); | ||
// Cache a new kinesis instance for the different region | ||
const maxRetries4 = 0; | ||
const options4 = { region: region2, maxRetries: maxRetries4 }; | ||
const kinesis4 = setKinesis(options4, context); | ||
t.ok(kinesis4, `setKinesis(${stringify(options4)}) must return an instance`); | ||
t.equal(kinesis4.config.region, region2, `kinesis 4 region must be ${region2}`); | ||
t.equal(kinesis4.config.maxRetries, maxRetries4, `kinesis 4 maxRetries must be ${maxRetries4}`); | ||
t.notEqual(kinesis4, kinesis2, `setKinesis(${stringify(options4)}) must NOT be cached instance 2 for region (${region1})`); | ||
t.equal(getKinesis(region2), kinesis4, `getKinesis(${region2}) gets cached instance 4`); | ||
// Check cache for current region 1 is still intact | ||
t.equal(getKinesis(), kinesis2, `getKinesis() still gets cached instance 2 for current region (${region1})`); | ||
t.equal(getKinesis(region1), kinesis2, `getKinesis(${region1}) gets cached instance 2`); | ||
// Do NOT re-use new kinesis instance for the different region if maxRetries is undefined instead of zero | ||
const maxRetries5 = ''; | ||
const options5 = { region: region2, maxRetries: maxRetries5 }; | ||
const kinesis5 = setKinesis(options5, context); | ||
t.ok(kinesis5, `setKinesis(${stringify(options5)}) must return an instance`); | ||
t.equal(kinesis5.config.region, region2, `kinesis 5 region must be ${region2}`); | ||
t.equal(kinesis5.config.maxRetries, maxRetries5, `kinesis 5 maxRetries must be ${maxRetries5}`); | ||
t.notEqual(kinesis5, kinesis4, `setKinesis(${stringify(options5)}) must NOT be cached instance 4 for region (${region2})`); | ||
// Delete cache for region 1 | ||
t.ok(deleteKinesis(region1), `must delete cached instance for region (${region1})`); // clean up | ||
t.equal(getKinesis(region1), undefined, `getKinesis(${region1}) gets undefined after delete`); | ||
// Delete cache for region 1 | ||
t.ok(deleteKinesis(region2), `must delete cached instance for region (${region2})`); // clean up | ||
t.equal(getKinesis(region2), undefined, `getKinesis(${region2}) gets undefined after delete`); | ||
t.end(); | ||
}); | ||
// ===================================================================================================================== | ||
// Tests for configureKinesis | ||
@@ -40,55 +162,70 @@ // ===================================================================================================================== | ||
t.notOk(context.kinesis, 'context.kinesis must not be configured yet'); | ||
process.env.AWS_REGION = 'us-west-1'; | ||
const region1 = getRegion(); | ||
t.equal(region1, 'us-west-1'); | ||
t.equal(region1, 'us-west-1', `current region must be us-west-1`); | ||
// Ensure not cached before we configure | ||
deleteKinesis(region1); | ||
t.notOk(context.kinesis, 'context.kinesis must not be configured yet'); | ||
// Configure it for the first time | ||
const maxRetries1 = 0; | ||
configureKinesis(context, maxRetries1); | ||
const kinesis = context.kinesis; | ||
t.ok(context.kinesis, 'context.kinesis must be configured now'); | ||
t.equal(kinesis.config.region, region1, `context.kinesis.config.region must be ${region1}`); | ||
t.equal(kinesis.config.maxRetries, maxRetries1, `context.kinesis.config.maxRetries must be ${maxRetries1}`); | ||
configureKinesis(context, {maxRetries: maxRetries1}); | ||
const kinesis1 = context.kinesis; | ||
t.ok(kinesis1, 'context.kinesis 1 must be configured now'); | ||
t.equal(kinesis1.config.region, region1, `context.kinesis 1 region must be ${region1}`); | ||
t.equal(kinesis1.config.maxRetries, maxRetries1, `context.kinesis 1 maxRetries must be ${maxRetries1}`); | ||
// "Configure" it for the second time with same region & maxRetries (should give same kinesis instance back again) | ||
context.kinesis = undefined; // clear context.kinesis otherwise will always get it back | ||
configureKinesis(context, maxRetries1); | ||
t.equal(context.kinesis, kinesis, 'context.kinesis must be same cached kinesis'); | ||
configureKinesis(context, {region: region1, maxRetries: maxRetries1}); | ||
const kinesis1a = context.kinesis; | ||
t.ok(kinesis1a, 'context.kinesis 1a must be configured'); | ||
t.equal(kinesis1a, kinesis1, 'context.kinesis 1a must be cached instance 1'); | ||
t.equal(kinesis1a.config.region, region1, `context.kinesis 1a region must be ${region1}`); | ||
t.equal(kinesis1a.config.maxRetries, maxRetries1, `context.kinesis 1a maxRetries must be ${maxRetries1}`); | ||
// Configure a new kinesis with a different maxRetries | ||
//context.kinesis = undefined; // clear context.kinesis otherwise will always get it back | ||
const maxRetries2 = 10; | ||
configureKinesis(context, maxRetries2); | ||
context.kinesis = undefined; // clear context.kinesis otherwise will always get it back | ||
configureKinesis(context, {maxRetries: maxRetries2}); | ||
const kinesis2 = context.kinesis; | ||
t.ok(context.kinesis, 'context.kinesis must be configured'); | ||
t.equal(kinesis2.config.region, region1, `context.kinesis.config.region must be ${region1}`); | ||
t.equal(kinesis2.config.maxRetries, maxRetries2, `context.kinesis.config.maxRetries must be ${maxRetries2}`); | ||
t.notEqual(kinesis2, kinesis, 'context.kinesis must NOT be same cached kinesis'); | ||
t.ok(kinesis2, 'context.kinesis 2 must be configured'); | ||
t.equal(kinesis2.config.region, region1, `context.kinesis 2 region must be ${region1}`); | ||
t.equal(kinesis2.config.maxRetries, maxRetries2, `context.kinesis 2 maxRetries must be ${maxRetries2}`); | ||
t.notEqual(kinesis2, kinesis1, 'context.kinesis 2 must not be cached instance 1'); | ||
// Configure same again, should hit context "cache" | ||
configureKinesis(context, maxRetries2); | ||
configureKinesis(context, {maxRetries: maxRetries2, region: region1}); | ||
const kinesis2a = context.kinesis; | ||
t.ok(context.kinesis, 'context.kinesis must be configured'); | ||
t.equal(kinesis2.config.region, region1, `context.kinesis.config.region must be ${region1}`); | ||
t.equal(kinesis2.config.maxRetries, maxRetries2, `context.kinesis.config.maxRetries must be ${maxRetries2}`); | ||
t.ok(kinesis2a, 'context.kinesis 2a must be configured'); | ||
t.equal(kinesis2a.config.region, region1, `context.kinesis 2a region must be ${region1}`); | ||
t.equal(kinesis2a.config.maxRetries, maxRetries2, `context.kinesis 2a maxRetries must be ${maxRetries2}`); | ||
t.equal(context.kinesis, kinesis2, 'context.kinesis must be same "cached" context.kinesis'); | ||
t.notEqual(context.kinesis, kinesis, 'context.kinesis must NOT be original cached kinesis'); | ||
t.equal(kinesis2a, kinesis2, 'context.kinesis 2a must be cached instance 2'); | ||
t.notEqual(kinesis2a, kinesis1, 'context.kinesis 2a must not be cached instance 1'); | ||
// Reconfigure original again | ||
configureKinesis(context, maxRetries1); | ||
const kinesis1 = context.kinesis; | ||
// Reconfigure "original" again | ||
context.kinesis = undefined; // clear context.kinesis otherwise will always get it back | ||
//deleteKinesis(region1); // make sure its gone before we start | ||
t.ok(context.kinesis, 'context.kinesis must be configured'); | ||
t.equal(kinesis1.config.region, region1, `context.kinesis.config.region must be ${region1}`); | ||
t.equal(kinesis1.config.maxRetries, maxRetries1, `context.kinesis.config.maxRetries must be ${maxRetries1}`); | ||
configureKinesis(context, {maxRetries: maxRetries1}); | ||
const kinesis3 = context.kinesis; | ||
t.equal(context.kinesis, kinesis, 'context.kinesis must be original cached kinesis again'); | ||
t.ok(kinesis3, 'context.kinesis 3 must be configured'); | ||
t.equal(kinesis3.config.region, region1, `context.kinesis 3 region must be ${region1}`); | ||
t.equal(kinesis3.config.maxRetries, maxRetries1, `context.kinesis 3 maxRetries must be ${maxRetries1}`); | ||
t.notEqual(kinesis3, kinesis2, 'context.kinesis 3 must not be cached instance 2'); | ||
t.notEqual(kinesis3, kinesis1, 'context.kinesis 3 must not be cached instance 1'); | ||
// Change the region | ||
@@ -98,29 +235,35 @@ process.env.AWS_REGION = 'us-west-2'; | ||
t.equal(region2, 'us-west-2'); | ||
t.equal(region2, 'us-west-2', `current region must be us-west-2`); | ||
// Configure for new region | ||
configureKinesis(context, maxRetries1); | ||
const kinesis3 = context.kinesis; | ||
context.kinesis = undefined; // clear context.kinesis otherwise will always get it back | ||
deleteKinesis(region2); // make sure none before we start | ||
t.ok(context.kinesis, 'context.kinesis must be configured'); | ||
t.equal(kinesis3.config.region, region2, `context.kinesis.config.region must be ${region2}`); | ||
t.equal(kinesis3.config.maxRetries, maxRetries1, `context.kinesis.config.maxRetries must be ${maxRetries1}`); | ||
configureKinesis(context, {maxRetries: maxRetries1}); | ||
const kinesis4 = context.kinesis; | ||
t.notEqual(context.kinesis, kinesis, 'context.kinesis must NOT be original cached kinesis'); | ||
t.notEqual(context.kinesis, kinesis2, 'context.kinesis must NOT be kinesis2 either'); | ||
t.ok(kinesis4, 'context.kinesis 4 must be configured'); | ||
t.equal(kinesis4.config.region, region2, `context.kinesis 4 region must be ${region2}`); | ||
t.equal(kinesis4.config.maxRetries, maxRetries1, `context.kinesis 4 maxRetries must be ${maxRetries1}`); | ||
t.notEqual(kinesis4, kinesis3, 'context.kinesis 4 must NOT be cached instance 3'); | ||
t.notEqual(kinesis4, kinesis2, 'context.kinesis 4 must NOT be cached instance 2'); | ||
t.notEqual(kinesis4, kinesis1, 'context.kinesis 4 must NOT be cached instance 1'); | ||
// Switch the region back again | ||
process.env.AWS_REGION = 'us-west-1'; | ||
t.equal(getRegion(), 'us-west-1'); | ||
t.equal(getRegion(), 'us-west-1', `current region must be us-west-1`); | ||
// Reconfigure original again | ||
configureKinesis(context, maxRetries1); | ||
const kinesis4 = context.kinesis; | ||
// "Reconfigure" original again | ||
context.kinesis = undefined; // clear context.kinesis otherwise will always get it back | ||
configureKinesis(context, {maxRetries: maxRetries1}); | ||
const kinesis5 = context.kinesis; | ||
t.ok(context.kinesis, 'context.kinesis must be configured'); | ||
t.equal(kinesis4.config.region, region1, `context.kinesis.config.region must be ${region1}`); | ||
t.equal(kinesis4.config.maxRetries, maxRetries1, `context.kinesis.config.maxRetries must be ${maxRetries1}`); | ||
t.ok(kinesis5, 'context.kinesis must be configured'); | ||
t.equal(kinesis5.config.region, region1, `context.kinesis 5 region must be ${region1}`); | ||
t.equal(kinesis5.config.maxRetries, maxRetries1, `context.kinesis 5 maxRetries must be ${maxRetries1}`); | ||
t.equal(context.kinesis, kinesis, 'context.kinesis must be original cached kinesis again'); | ||
t.equal(kinesis5, kinesis3, 'context.kinesis 5 must be cached instance 3'); | ||
t.end(); | ||
}); |
{ | ||
"name": "aws-core-utils-tests", | ||
"description": "Unit tests for aws-core-utils modules", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"author": "Byron du Preez", | ||
@@ -6,0 +6,0 @@ "license": "Apache-2.0", |
@@ -121,7 +121,21 @@ 'use strict'; | ||
// Configure it | ||
configureStageHandling(context, customToStage, convertAliasToStage, | ||
injectStageIntoStreamName, extractStageFromStreamName, streamNameStageSeparator, | ||
injectStageIntoResourceName, extractStageFromResourceName, resourceNameStageSeparator, | ||
injectInCase, extractInCase, defaultStage, forceConfiguration); | ||
const settings = { | ||
customToStage: customToStage, | ||
convertAliasToStage: convertAliasToStage, | ||
injectStageIntoStreamName: injectStageIntoStreamName, | ||
extractStageFromStreamName: extractStageFromStreamName, | ||
streamNameStageSeparator: streamNameStageSeparator, | ||
injectStageIntoResourceName: injectStageIntoResourceName, | ||
extractStageFromResourceName: extractStageFromResourceName, | ||
resourceNameStageSeparator: resourceNameStageSeparator, | ||
injectInCase: injectInCase, | ||
extractInCase: extractInCase, | ||
defaultStage: defaultStage, | ||
}; | ||
configureStageHandling(context, settings, forceConfiguration); | ||
const after = context.stageHandling; | ||
@@ -128,0 +142,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
211410
3239
284
3
22
+ Addeddeep-equal@^1.0.1
+ Addedcall-bind@1.0.7(transitive)
+ Addeddeep-equal@1.1.2(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddefine-properties@1.2.1(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedfunctions-have-names@1.2.3(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhas-tostringtag@1.0.2(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedis-arguments@1.1.1(transitive)
+ Addedis-date-object@1.0.5(transitive)
+ Addedis-regex@1.1.4(transitive)
+ Addedobject-is@1.1.6(transitive)
+ Addedobject-keys@1.1.1(transitive)
+ Addedregexp.prototype.flags@1.5.3(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedset-function-name@2.0.2(transitive)