Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

aws-core-utils

Package Overview
Dependencies
Maintainers
1
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

aws-core-utils - npm Package Compare versions

Comparing version 5.0.17 to 6.0.0

copy.sh

62

api-lambdas.js
'use strict';
const contexts = require('./contexts');
require("core-functions/promises");
const Promises = require('core-functions/promises');
const Objects = require('core-functions/objects');

@@ -12,2 +12,4 @@ const appErrors = require('core-functions/app-errors');

const logging = require('logging-utils');
const LogLevel = logging.LogLevel;
const log = logging.log;

@@ -63,7 +65,7 @@ /**

*
* @param {Object|StandardContext|undefined} [moduleScopeContext] - an optional module-scope context from which to copy an initial standard context
* @param {StandardSettings|undefined} [moduleScopeSettings] - optional module-scoped settings from which to copy initial settings to use to configure a standard context
* @param {StandardOptions|undefined} [moduleScopeOptions] - optional module-scoped options from which to copy initial options to use to configure a standard context
* @param {function(event: AwsEvent, context: StandardContext)} fn - your function that must accept the AWS event and a standard context and ideally return a Promise
* @param {string|undefined} [logRequestResponseAtLogLevel] - an optional log level at which to log the request (i.e.
* @param {Object|StandardContext|undefined} [initContext] - an optional module-scope context from which to copy an initial standard context
* @param {StandardSettings|undefined} [initSettings] - optional module-scoped settings from which to copy initial settings to use to configure a standard context
* @param {StandardOptions|undefined} [initOptions] - optional module-scoped options from which to copy initial options to use to configure a standard context
* @param {function(event: AWSEvent, context: StandardContext)} fn - your function that must accept the AWS event and a standard context and ideally return a Promise
* @param {LogLevel|string|undefined} [logRequestResponseAtLogLevel] - an optional log level at which to log the request (i.e.
* AWS event) and response; if log level is undefined or invalid, then logs neither

@@ -79,3 +81,3 @@ * @param {number[]|undefined} [allowedHttpStatusCodes] - an optional array of HTTP status codes that are allowed to be

*/
function generateHandlerFunction(moduleScopeContext, moduleScopeSettings, moduleScopeOptions, fn, logRequestResponseAtLogLevel, allowedHttpStatusCodes, invalidRequestMsg, failureMsg, successMsg) {
function generateHandlerFunction(initContext, initSettings, initOptions, fn, logRequestResponseAtLogLevel, allowedHttpStatusCodes, invalidRequestMsg, failureMsg, successMsg) {
/**

@@ -88,6 +90,6 @@ * An API-Gateway exposed Lambda handler function.

function handler(event, awsContext, callback) {
const context = moduleScopeContext && typeof moduleScopeContext === 'object' ? Objects.copy(moduleScopeContext, true) : {};
const context = initContext && typeof initContext === 'object' ? Objects.copy(initContext, {deep: true}) : {};
try {
const settings = moduleScopeSettings && typeof moduleScopeSettings === 'object' ? Objects.copy(moduleScopeSettings, true) : undefined;
const options = moduleScopeOptions && typeof moduleScopeOptions === 'object' ? Objects.copy(moduleScopeOptions, true) : undefined;
const settings = initSettings && typeof initSettings === 'object' ? Objects.copy(initSettings, {deep: true}) : undefined;
const options = initOptions && typeof initOptions === 'object' ? Objects.copy(initOptions, {deep: true}) : undefined;

@@ -98,9 +100,13 @@ // Configure the context as a standard context

// Optionally log the request
log('Request: ', event, logRequestResponseAtLogLevel, context);
if (logRequestResponseAtLogLevel && logging.isValidLogLevel(logRequestResponseAtLogLevel)) {
context.log(logRequestResponseAtLogLevel, 'Request:', JSON.stringify(event));
}
// Execute the given function
Promise.try(() => fn(event, context))
Promises.try(() => fn(event, context))
.then(response => {
// Optionally log the response
log('Response: ', response, logRequestResponseAtLogLevel, context);
if (logRequestResponseAtLogLevel && logging.isValidLogLevel(logRequestResponseAtLogLevel)) {
context.log(logRequestResponseAtLogLevel, 'Response:', JSON.stringify(response));
}

@@ -127,3 +133,3 @@ // Log the given success message (if any)

} catch (err) {
(context.error ? context.error : console.error)(isNotBlank(failureMsg) ? failureMsg : 'Failed to execute Lambda', err.stack);
log(context, LogLevel.ERROR, isNotBlank(failureMsg) ? failureMsg : 'Failed to execute Lambda', err.stack);
// Fail the Lambda callback

@@ -135,28 +141,2 @@ failCallback(callback, err, awsContext, undefined, undefined, allowedHttpStatusCodes);

return handler;
}
function log(prefix, object, logLevel, context) {
if (isNotBlank(logLevel)) {
const msg = `${isNotBlank(prefix) ? prefix : ''}${JSON.stringify(object)}`;
switch (logLevel.toLowerCase()) {
case logging.INFO:
context.info(msg);
break;
case logging.DEBUG:
context.debug(msg);
break;
case logging.TRACE:
context.trace(msg);
break;
case logging.WARN:
context.warn(msg);
break;
case logging.ERROR:
context.error(msg);
break;
default:
context.warn(`Unexpected log level (${logLevel})`);
break;
}
}
}
}

@@ -83,13 +83,2 @@ 'use strict';

/**
* ARN resource-related components
* @typedef {Object} ArnResources
* @property {string} resourceType - a resource type (for DynamoDB stream eventSourceARN's this contains "table")
* @property {string} resource - a resource name (for DynamoDB stream eventSourceARN's this is the table name)
* @property {string} subResourceType - a sub-resource type (for DynamoDB stream eventSourceARN's this contains "stream")
* @property {string} subResource - a sub-resource name (for DynamoDB stream eventSourceARN's this is the stream timestamp)
* @property {string} aliasOrVersion - a Lambda alias or version number
* @property {string[]} others - any other components after a Lambda alias or version number
*/
/**
* Attempts to extract any and all resource-related components from the given ARN (if defined) and returns them as

@@ -96,0 +85,0 @@ * an object containing resourceType, resource, aliasOrVersion and others (just in case there were even more components

@@ -13,3 +13,5 @@ "use strict";

module.exports = {
// General error checks
isUnavailable: isUnavailable,
// Specific error checks
isConditionalCheckFailed: isConditionalCheckFailed,

@@ -19,6 +21,11 @@ isProvisionedThroughputExceeded: isProvisionedThroughputExceeded,

isLimitExceededException: isLimitExceededException,
isItemCollectionSizeLimitExceededException: isItemCollectionSizeLimitExceededException,
// Summarized multiple error checks
isThrottled: isThrottled,
isLimitExceeded: isLimitExceeded,
isRetryable: isRetryable,
// Other specific error checks
isExpiredCredentialsError: isExpiredCredentialsError,
isNetworkingError: isNetworkingError,
// S3 not found
wasS3ObjectNotFound: wasS3ObjectNotFound

@@ -47,2 +54,19 @@ };

function isItemCollectionSizeLimitExceededException(err) {
return err.code === 'ItemCollectionSizeLimitExceededException';
}
function isLimitExceeded(err) {
switch (err.code) {
// DynamoDB-specific?
case 'ItemCollectionSizeLimitExceededException':
case 'LimitExceededException':
// not DynamoDB-specific?
case 'RequestLimitExceeded':
return true;
default:
return false;
}
}
function isThrottled(err) {

@@ -53,12 +77,9 @@ switch (err.code) {

case 'ThrottlingException':
case 'ItemCollectionSizeLimitExceededException':
case 'LimitExceededException':
// S3-specific?
//case 'ServiceUnavailable': // actually 503 error
//case 'SlowDown': // actually 503 error
case 'SlowDown': // actually 503 error
// not DynamoDB-specific?
case 'Throttling':
case 'RequestLimitExceeded':
case 'RequestThrottled':

@@ -72,6 +93,4 @@ return true;

function isRetryable(err) {
if (isNetworkingError(err)) return true;
if (isExpiredCredentialsError(err)) return true;
if (isThrottled(err)) return true;
return err.statusCode >= 500;
return err.statusCode >= 500 || isNetworkingError(err) || isExpiredCredentialsError(err) || isThrottled(err) ||
isLimitExceeded(err) || err.retryable;
//return isThrottled(err) || err.code === 'ItemCollectionSizeLimitExceededException' ||

@@ -78,0 +97,0 @@ // err.code === 'UnrecognizedClientException' || err.retryable;

@@ -107,12 +107,12 @@ 'use strict';

const customOptions = optionsAvailable ? Objects.copy(options, true) : {};
const customOptions = optionsAvailable ? Objects.copy(options, {deep: true}) : {};
const customSettings = settingsAvailable ?
optionsAvailable ? Objects.merge(customOptions, settings, false, false) : settings :
optionsAvailable ? Objects.merge(customOptions, settings) : settings :
customOptions;
context.custom = context.custom && typeof context.custom === 'object' ?
Objects.merge(customSettings, context.custom, false, false) : customSettings;
Objects.merge(customSettings, context.custom) : customSettings;
return context;
}

@@ -56,3 +56,3 @@ 'use strict';

// If no options were specified, then use an empty object
const options = dynamoDBDocClientOptions ? Objects.copy(dynamoDBDocClientOptions, true) : {};
const options = dynamoDBDocClientOptions ? Objects.copy(dynamoDBDocClientOptions, {deep: true}) : {};

@@ -59,0 +59,0 @@ // If no region was specified in the given dynamoDBDocClient options, then set it to the current region

'use strict';
/**
* Defaults used by this module, which can be overridden to alter the default behaviour.
* @namespace {DynamoDBUtilsDefaults} defaults
*/
const defaults = {
emptyStringReplacement: ' '
};
/**
* Utilities for working with AWS DynamoDB.

@@ -12,5 +20,6 @@ * @module aws-core-utils/dynamodb-utils

toValueFromAttributeTypeAndValue: toValueFromAttributeTypeAndValue,
toNumber: toNumber,
toKeyValueStrings: toKeyValueStrings,
toKeyValuePairs: toKeyValuePairs
toKeyValuePairs: toKeyValuePairs,
toStorableObject: toStorableObject,
defaults: defaults
};

@@ -21,2 +30,5 @@

const Numbers = require('core-functions/numbers');
const toNumberOrIntegerLike = Numbers.toNumberOrIntegerLike;
/**

@@ -70,3 +82,3 @@ * Attempts to convert the given DynamoDB map object containing keys and Attribute values into a JavaScript object.

case 'N':
return toNumber(value);
return toNumberOrIntegerLike(value);
case 'BOOL':

@@ -83,3 +95,3 @@ return value === true || value === 'true';

case 'NS':
return value.map(v => toNumber(v));
return value.map(v => toNumberOrIntegerLike(v));
case 'B':

@@ -94,24 +106,2 @@ case 'BS':

/**
* Attempts to convert the given value into a number, but keeps any integer string, which cannot be converted to a
* number without losing precision.
* @param {string} value - the value to convert
* @returns {number|string} the number parsed from the value
*/
function toNumber(value) {
if (value) {
const typeOfValue = typeof value;
if (typeOfValue === 'string' && value.indexOf('.') === -1) {
// No decimal point, so try for an integer first
const n = Number.parseInt(value);
// Check if have enough precision to hold the given integer value ... otherwise rather keep the original string value
return `${n}` === value ? n : Number.isNaN(n) ? NaN : value;
} else if (typeOfValue === 'number') {
return value;
}
return Number.parseFloat(value);
}
return NaN;
}
/**
* Extracts an array of colon-separated key name and value strings from the given DynamoDB map object.

@@ -122,9 +112,4 @@ * @param {Object} dynamoDBMap - a DynamoDB map object

function toKeyValueStrings(dynamoDBMap) {
if (!dynamoDBMap || typeof dynamoDBMap !== 'object') {
return [];
}
return Object.getOwnPropertyNames(dynamoDBMap).map(key => {
const value = toValueFromAttributeValue(dynamoDBMap[key]);
return `${key}:${stringify(value)}`;
});
return dynamoDBMap && typeof dynamoDBMap === 'object' ?
Object.getOwnPropertyNames(dynamoDBMap).map(key => `${key}:${stringify(toValueFromAttributeValue(dynamoDBMap[key]))}`) : [];
}

@@ -136,9 +121,30 @@

* @param {Object} dynamoDBMap - a DynamoDB map object
* @returns {string[]} an array of key name and value pairs
* @returns {KeyValuePair[]} an array of key value pairs
*/
function toKeyValuePairs(dynamoDBMap) {
if (!dynamoDBMap || typeof dynamoDBMap !== 'object') {
return [];
}
return Object.getOwnPropertyNames(dynamoDBMap).map(key => [key, toValueFromAttributeValue(dynamoDBMap[key])]);
return dynamoDBMap && typeof dynamoDBMap === 'object' ?
Object.getOwnPropertyNames(dynamoDBMap).map(key => [key, toValueFromAttributeValue(dynamoDBMap[key])]) : [];
}
/**
* Transforms the given object into an object that can be safely stored to DynamoDB with all of its empty strings
* replaced with Defaults.emptyStringReplacement and with no undefined properties.
* @param {Object} object - an object to be stored in DynamoDB
* @returns {Object} an object that can be safely stored in DynamoDB
*/
function toStorableObject(object) {
// Round-trip to JSON and back to eliminate all undefined properties and replace all empty strings
return JSON.parse(JSON.stringify(object, emptyStringReplacer));
}
//noinspection JSUnusedLocalSymbols
/**
* A replacer function to be used with JSON.stringify, which replaces all empty string values with Defaults.emptyStringReplacement.
* @param {string} key - the key of the property being stringified (initially an empty key representing the object being stringified)
* @param {*} value - the value being stringified
* @returns {string} the non-empty string replacement value
*/
function emptyStringReplacer(key, value) {
// DynamoDB does NOT accept any empty strings including ones inside arrays, so no special case for arrays is necessary
return value === '' ? defaults.emptyStringReplacement : value;
}

@@ -53,3 +53,3 @@ 'use strict';

// If no options were specified, then use an empty object
const options = kinesisOptions ? Objects.copy(kinesisOptions, true) : {};
const options = kinesisOptions ? Objects.copy(kinesisOptions, {deep: true}) : {};

@@ -56,0 +56,0 @@ // If no region was specified in the given kinesis options, then set it to the current region

{
"name": "aws-core-utils",
"version": "5.0.17",
"version": "6.0.0",
"description": "Core utilities for working with Amazon Web Services (AWS), including ARNs, regions, stages, Lambdas, AWS errors, stream events, Kinesis, DynamoDB.DocumentClients, etc.",

@@ -14,4 +14,4 @@ "author": "Byron du Preez",

"dependencies": {
"core-functions": "^2.0.14",
"logging-utils": "^3.0.12",
"core-functions": "^3.0.0",
"logging-utils": "^4.0.0",
"deep-equal": "^1.0.1"

@@ -18,0 +18,0 @@ },

@@ -1,2 +0,2 @@

# aws-core-utils v5.0.17
# aws-core-utils v6.0.0

@@ -134,6 +134,6 @@ Core utilities for working with Amazon Web Services (AWS), including ARNs, regions, stages, Lambdas, AWS errors, stream events, Kinesis, DynamoDB.DocumentClients, etc.

// If you need the logic of the configureCustomSettings function, which is used by configureStandardContext, for other purposes
const myCustomSettings = {myCustomSetting1: 1, myCustomSetting2: 2, myCustomFunction: () => {}}; //
const myCustomSettings = {myCustomSetting1: 1, myCustomSetting2: 2, myCustomFunction: () => {}};
const myCustomOptions = require('my-custom-options.json');
contexts.configureCustomSettings(context, myCustomSettings, myCustomOptions);
console.log(`context.custom = ${JSON.stringify(context.custom)}`);
console.log(`context.custom = ${JSON.stringify(context.custom)}; myCustomFunction = ${JSON.stringify(context.custom.myCustomFunction)} `);
```

@@ -148,3 +148,3 @@

const logging = require('logging-utils');
logging.configureDefaultLogging(context); // or your own custom logging configuration (see logging-utils README.md)
logging.configureLogging(context); // or your own custom logging configuration (see logging-utils README.md)

@@ -188,3 +188,3 @@ // Define the DynamoDB.DocumentClient's constructor options that you want to use, e.g.

const logging = require('logging-utils');
logging.configureDefaultLogging(context); // or your own custom logging configuration (see logging-utils README.md)
logging.configureLogging(context); // or your own custom logging configuration (see logging-utils README.md)

@@ -394,2 +394,37 @@ // Define the Kinesis constructor options that you want to use, e.g.

### 6.0.0
- Updated `core-functions` dependency to version 3.0.0
- Updated `logging-utils` dependency to version 4.0.0
- Changes to `api-lambdas.js` module:
- Removed `log` function (replaced use with new `log` function & `log` method in `logging-utils`)
- Changes to `arns.js` module:
- Moved `ArnResources` typedef to `type-defs.js`
- Changes to `aws-errors.js` module:
- Added `isItemCollectionSizeLimitExceededException` function
- Added `isLimitExceeded` function
- Removed limit exceeded cases from `isThrottled` function (not backward-compatible)
- Added S3 `SlowDown` case to `isThrottled` function
- Added `isLimitExceeded` & `err.retryable` checks to `isRetryable` function
- Changes to `dynamodb-utils.js` module:
- Removed `toNumber` function (replaced use with new `toNumberOrIntegerLike` function in `core-functions/numbers.js`)
- Added new `toStorableObject` function
- Added new `defaults` static property with `emptyStringReplacement` property
- Changes to `stages.js` module:
- Replaced all setting names constants (e.g. CUSTOM_TO_STAGE_SETTING) with use of a new module-scope `settingNames`
object property that holds all the standard stage handling settings names (e.g. settingNames.customToStage)
- Added new `extractNameAndStageFromStreamName` & `extractNameAndStageFromResourceName` settings
- Added `extractNameAndStageFromQualifiedStreamName` & `extractNameAndStageFromQualifiedResourceName` functions
- Added `extractNameAndStageFromSuffixedStreamName` & `extractNameAndStageFromSuffixedResourceName` functions
- Added `_extractNameAndStageFromQualifiedName` & `_extractNameAndStageFromSuffixedName` functions
- Changed `extractStageFromQualifiedStreamName` to attempt fallback with `_extractNameAndStageFromQualifiedName`
- Changed `extractStageFromQualifiedResourceName` to attempt fallback with `_extractNameAndStageFromQualifiedName`
- Changes to `stream-events.js` module:
- Added `MAX_PARTITION_KEY_SIZE` constant
- Added `DynamoDBEventName` enum
- Added `getEventID`, `getEventName`, `getEventSource` & `getEventSourceARN` functions
- Added `getKinesisShardId`, `getKinesisShardIdFromEventID`, `getKinesisShardIdAndEventNoFromEventID` & `getKinesisSequenceNumber` functions
- Added `getDynamoDBSequenceNumber` function
- Added additional validation checks to `validateStreamEventRecord`, `validateKinesisStreamEventRecord` & `validateDynamoDBStreamEventRecord` functions
- Added many new typedefs to `type-defs.js`
### 5.0.17

@@ -396,0 +431,0 @@ - Fixed critical module-scope defects in `generateHandlerFunction` function in `api-lambdas` module

'use strict';
// Stage handling setting names
const ENV_STAGE_NAME_SETTING = 'envStageName';
/**
* The names of all of the standard stage handling settings.
* @namespace
*/
const settingNames = {
envStageName: 'envStageName',
customToStage: 'customToStage',
convertAliasToStage: 'convertAliasToStage',
streamNameStageSeparator: 'streamNameStageSeparator',
injectStageIntoStreamName: 'injectStageIntoStreamName',
extractStageFromStreamName: 'extractStageFromStreamName',
extractNameAndStageFromStreamName: 'extractNameAndStageFromStreamName',
resourceNameStageSeparator: 'resourceNameStageSeparator',
injectStageIntoResourceName: 'injectStageIntoResourceName',
extractStageFromResourceName: 'extractStageFromResourceName',
extractNameAndStageFromResourceName: 'extractNameAndStageFromResourceName',
injectInCase: 'injectInCase',
extractInCase: 'extractInCase'
};
const CUSTOM_TO_STAGE_SETTING = 'customToStage';
const CONVERT_ALIAS_TO_STAGE_SETTING = 'convertAliasToStage';
const STREAM_NAME_STAGE_SEPARATOR_SETTING = 'streamNameStageSeparator';
const INJECT_STAGE_INTO_STREAM_NAME_SETTING = 'injectStageIntoStreamName';
const EXTRACT_STAGE_FROM_STREAM_NAME_SETTING = 'extractStageFromStreamName';
const RESOURCE_NAME_STAGE_SEPARATOR_SETTING = 'resourceNameStageSeparator';
const INJECT_STAGE_INTO_RESOURCE_NAME_SETTING = 'injectStageIntoResourceName';
const EXTRACT_STAGE_FROM_RESOURCE_NAME_SETTING = 'extractStageFromResourceName';
const INJECT_IN_CASE_SETTING = 'injectInCase';
const EXTRACT_IN_CASE_SETTING = 'extractInCase';
/**

@@ -31,2 +34,3 @@ * Stage handling utilities (primarily for AWS Lambda usage), which include the following:

module.exports = {
settingNames: settingNames,
// Stage handling configuration

@@ -52,2 +56,3 @@ isStageHandlingConfigured: isStageHandlingConfigured,

extractStageFromQualifiedStreamName: extractStageFromQualifiedStreamName,
extractNameAndStageFromQualifiedStreamName: extractNameAndStageFromQualifiedStreamName,

@@ -57,2 +62,3 @@ // Resource name qualification

extractStageFromQualifiedResourceName: extractStageFromQualifiedResourceName,
extractNameAndStageFromQualifiedResourceName: extractNameAndStageFromQualifiedResourceName,

@@ -70,5 +76,7 @@ /**

extractStageFromSuffixedStreamName: extractStageFromSuffixedStreamName,
extractNameAndStageFromSuffixedStreamName: extractNameAndStageFromSuffixedStreamName,
// Stage-suffixed resource name qualification
toStageSuffixedResourceName: toStageSuffixedResourceName,
extractStageFromSuffixedResourceName: extractStageFromSuffixedResourceName,
extractNameAndStageFromSuffixedResourceName: extractNameAndStageFromSuffixedResourceName,
// Generic utils

@@ -117,3 +125,3 @@ toStageSuffixedName: toStageSuffixedName,

*
* @param {Object|StandardContext|StageHandling|Logging} context - the context onto which to configure stage handling settings
* @param {Object|StandardContext|StageHandling|Logger} context - the context onto which to configure stage handling settings
* @param {StageHandlingSettings} [context.stageHandling] - previously configured stage handling settings on the context (if any)

@@ -163,3 +171,3 @@ * @param {StageHandlingSettings} settings - the new stage handling settings to use

*
* @param {Object|StandardContext|StageHandling|Logging} context - the context onto which to configure the default stage handling settings
* @param {Object|StandardContext|StageHandling|Logger} 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)

@@ -192,6 +200,6 @@ * @param {StageHandlingOptions|undefined} [options] - optional stage handling options to use to override the default options

function getDefaultStageHandlingSettings(options) {
const settings = options && typeof options === 'object' ? Objects.copy(options, true) : {};
const settings = options && typeof options === 'object' ? Objects.copy(options, {deep: true}) : {};
const defaultOptions = loadDefaultStageHandlingOptions();
Objects.merge(defaultOptions, settings, false, false);
Objects.merge(defaultOptions, settings);

@@ -204,7 +212,9 @@ const defaultSettings = {

extractStageFromStreamName: extractStageFromSuffixedStreamName,
extractNameAndStageFromStreamName: extractNameAndStageFromSuffixedStreamName,
injectStageIntoResourceName: toStageSuffixedResourceName,
extractStageFromResourceName: extractStageFromSuffixedResourceName,
extractNameAndStageFromResourceName: extractNameAndStageFromSuffixedResourceName
};
return Objects.merge(defaultSettings, settings, false, false);
return Objects.merge(defaultSettings, settings);
}

@@ -229,3 +239,3 @@

};
return Objects.merge(defaults, defaultOptions, false, false);
return Objects.merge(defaults, defaultOptions);
}

@@ -265,3 +275,3 @@

*
* @param {Object|StandardContext|StageHandling|Logging} context - the context to configure
* @param {Object|StandardContext|StageHandling|Logger} context - the context to configure
* @param {StageHandlingSettings|undefined} [settings] - optional stage handling settings to use to configure stage handling

@@ -288,3 +298,3 @@ * @param {StageHandlingOptions|undefined} [options] - optional stage handling options to use to override default options

const stageHandlingSettings = settingsAvailable ?
Objects.merge(defaultSettings, settings, false, false) : defaultSettings;
Objects.merge(defaultSettings, settings) : defaultSettings;

@@ -296,3 +306,3 @@ // Configure stage handling with the given or derived stage handling settings

if (!settingsAvailable && !optionsAvailable && (forceConfiguration || !stageHandlingWasConfigured)) {
context.warn(`Stage handling was configured without settings or options - used default stage handling configuration (${stringify(stageHandlingSettings)})`);
context.warn(`Stage handling was configured without settings or options - used default configuration`);
}

@@ -306,3 +316,3 @@ return context;

*
* @param {Object|StandardContext|Logging} context - the context onto which to configure the given stage handling dependencies
* @param {Object|StandardContext|Logger} context - the context onto which to configure the given stage handling dependencies
* @param {Object|StandardSettings|undefined} [otherSettings] - optional other configuration settings to use

@@ -314,3 +324,3 @@ * @param {LoggingSettings|undefined} [otherSettings.loggingSettings] - optional logging settings to use to configure logging

* will override any previously configured dependencies' settings on the given context
* @returns {Logging|StandardContext} the context object configured with stage handling dependencies (i.e. logging functionality)
* @returns {Logger|StandardContext} the context object configured with stage handling dependencies (i.e. logging functionality)
*/

@@ -320,3 +330,3 @@ function configureDependencies(context, otherSettings, otherOptions, forceConfiguration) {

logging.configureLogging(context, otherSettings ? otherSettings.loggingSettings : undefined,
otherOptions ? otherOptions.loggingOptions : undefined, undefined, forceConfiguration);
otherOptions ? otherOptions.loggingOptions : undefined, forceConfiguration);
}

@@ -378,3 +388,3 @@

// Resolve extractInCase
const extractInCase = getStageHandlingSetting(context, EXTRACT_IN_CASE_SETTING);
const extractInCase = getStageHandlingSetting(context, settingNames.extractInCase);

@@ -388,3 +398,3 @@ // Attempt 1

// Attempt 2
const envStageName = getStageHandlingSetting(context, ENV_STAGE_NAME_SETTING);
const envStageName = getStageHandlingSetting(context, settingNames.envStageName);

@@ -402,3 +412,3 @@ if (isNotBlank(envStageName)) {

// Attempt 3
const customToStage = getStageHandlingFunction(context, CUSTOM_TO_STAGE_SETTING);
const customToStage = getStageHandlingFunction(context, settingNames.customToStage);

@@ -422,3 +432,3 @@ if (customToStage) {

// Check have all the pieces needed to extract an alias and apply the given convertAliasToStage function to it
const convertAliasToStage = getStageHandlingFunction(context, CONVERT_ALIAS_TO_STAGE_SETTING);
const convertAliasToStage = getStageHandlingFunction(context, settingNames.convertAliasToStage);

@@ -459,3 +469,3 @@ if (convertAliasToStage && awsContext && isNotBlank(awsContext.functionVersion) && isNotBlank(awsContext.invokedFunctionArn)) {

// Check have all the pieces needed to extract a stream name and apply the given extractStageFromStreamName function to it
const extractStageFromStreamName = getStageHandlingFunction(context, EXTRACT_STAGE_FROM_STREAM_NAME_SETTING);
const extractStageFromStreamName = getStageHandlingFunction(context, settingNames.extractStageFromStreamName);
if (extractStageFromStreamName && event && event.Records) {

@@ -468,3 +478,3 @@ stages = streamEvents.getKinesisEventSourceStreamNames(event)

// Check have all the pieces needed to extract a table name and apply the given extractStageFromResourceName function to it
const extractStageFromTableName = getStageHandlingFunction(context, EXTRACT_STAGE_FROM_RESOURCE_NAME_SETTING);
const extractStageFromTableName = getStageHandlingFunction(context, settingNames.extractStageFromResourceName);
if (extractStageFromTableName && event && event.Records) {

@@ -481,3 +491,3 @@ stages = streamEvents.getDynamoDBEventSourceTableNames(event)

if (distinctStages > 1) {
context.warn(`WARNING - Ignoring arbitrary first stage (${stage}), since found MULTIPLE distinct stages ${stringify(distinctStages)} on event (${stringify(event)})!`);
context.warn(`Ignoring arbitrary first stage (${stage}), since found MULTIPLE distinct stages ${stringify(distinctStages)} on event (${stringify(event)})!`);
stage = ''; // too many choices, so choose none

@@ -545,3 +555,3 @@ }

function toStageQualifiedStreamName(unqualifiedStreamName, stage, context) {
return _toStageQualifiedName(unqualifiedStreamName, stage, INJECT_STAGE_INTO_STREAM_NAME_SETTING, context);
return _toStageQualifiedName(unqualifiedStreamName, stage, settingNames.injectStageIntoStreamName, context);
}

@@ -561,5 +571,24 @@

function extractStageFromQualifiedStreamName(qualifiedStreamName, context) {
return _extractStageFromQualifiedName(qualifiedStreamName, EXTRACT_STAGE_FROM_STREAM_NAME_SETTING, context);
const stage = _extractStageFromQualifiedName(qualifiedStreamName, settingNames.extractStageFromStreamName, context);
return isNotBlank(stage) ? stage :
_extractNameAndStageFromQualifiedName(qualifiedStreamName, settingNames.extractNameAndStageFromStreamName, context)[1];
}
/**
* Extracts the unqualified stream name and the stage from the given stage-qualified stream name (if non-blank and an
* extractNameAndStageFromStreamName function is configured); otherwise returns the given stream name and an empty stage
* string.
*
* This function uses the configured extractNameAndStageFromStreamName function (if any) on the given context to
* determine its actual behaviour.
*
* @param {string} qualifiedStreamName - the stage-qualified stream name
* @param {StageHandling|Object} context - the context to use with stage handling settings and logging functionality
* @returns {[string,string]} an array containing: the unqualified name and the stage (if extracted); otherwise the
* given name and an empty stage string
*/
function extractNameAndStageFromQualifiedStreamName(qualifiedStreamName, context) {
return _extractNameAndStageFromQualifiedName(qualifiedStreamName, settingNames.extractNameAndStageFromStreamName, context);
}
// =====================================================================================================================

@@ -580,3 +609,3 @@ // Stream name qualification (default)

function toStageSuffixedStreamName(unsuffixedStreamName, stage, context) {
return _toStageSuffixedName(unsuffixedStreamName, stage, STREAM_NAME_STAGE_SEPARATOR_SETTING, context);
return _toStageSuffixedName(unsuffixedStreamName, stage, settingNames.streamNameStageSeparator, context);
}

@@ -595,5 +624,24 @@

function extractStageFromSuffixedStreamName(stageSuffixedStreamName, context) {
return _extractStageFromSuffixedName(stageSuffixedStreamName, STREAM_NAME_STAGE_SEPARATOR_SETTING, context);
return _extractStageFromSuffixedName(stageSuffixedStreamName, settingNames.streamNameStageSeparator, context);
}
/**
* A default extractNameAndStageFromStreamName function that extracts the unqualified name and stage from the given
* stage-suffixed stream name.
*
* The unqualified name prefix is extracted from the given stream name by taking everything before the last occurrence
* of the configured streamNameStageSeparator (if any).
*
* The stage suffix is extracted from the given stream name by taking everything after the last occurrence of the configured
* streamNameStageSeparator (if any).
*
* @param {string} stageSuffixedStreamName - the stage-suffixed name of the stream
* @param {StageHandling|Object} context - the context to use with stage handling settings and logging functionality
* @returns {[string,string]} an array containing: the unqualified name and the stage (if extracted); otherwise the
* given name and an empty stage string
*/
function extractNameAndStageFromSuffixedStreamName(stageSuffixedStreamName, context) {
return _extractNameAndStageFromSuffixedName(stageSuffixedStreamName, settingNames.streamNameStageSeparator, context);
}
// =====================================================================================================================

@@ -617,3 +665,3 @@ // Resource name qualification

function toStageQualifiedResourceName(unqualifiedResourceName, stage, context) {
return _toStageQualifiedName(unqualifiedResourceName, stage, INJECT_STAGE_INTO_RESOURCE_NAME_SETTING, context);
return _toStageQualifiedName(unqualifiedResourceName, stage, settingNames.injectStageIntoResourceName, context);
}

@@ -633,5 +681,25 @@

function extractStageFromQualifiedResourceName(qualifiedResourceName, context) {
return _extractStageFromQualifiedName(qualifiedResourceName, EXTRACT_STAGE_FROM_RESOURCE_NAME_SETTING, context);
const stage = _extractStageFromQualifiedName(qualifiedResourceName, settingNames.extractStageFromResourceName, context);
return isNotBlank(stage) ? stage :
_extractNameAndStageFromQualifiedName(qualifiedResourceName, settingNames.extractNameAndStageFromResourceName, context)[1];
}
/**
* Extracts the unqualified resource name and the stage from the given stage-qualified resource name (if non-blank and
* an extractNameAndStageFromResourceName function is configured); otherwise returns the given resource name and an
* empty stage string.
*
* This function uses the configured extractNameAndStageFromResourceName function (if any) on the given context to
* determine its actual behaviour.
*
* @param {string} qualifiedResourceName - the stage-qualified resource name
* @param {StageHandling|Object} context - the context to use with stage handling settings and logging functionality
* @returns {[string,string]} an array containing: the unqualified name and the stage (if extracted); otherwise the
* given name and an empty stage string
*/
function extractNameAndStageFromQualifiedResourceName(qualifiedResourceName, context) {
return _extractNameAndStageFromQualifiedName(qualifiedResourceName, settingNames.extractNameAndStageFromResourceName, context);
}
// =====================================================================================================================

@@ -651,3 +719,3 @@ // Resource name qualification (default)

function toStageSuffixedResourceName(unsuffixedResourceName, stage, context) {
return _toStageSuffixedName(unsuffixedResourceName, stage, RESOURCE_NAME_STAGE_SEPARATOR_SETTING, context);
return _toStageSuffixedName(unsuffixedResourceName, stage, settingNames.resourceNameStageSeparator, context);
}

@@ -665,5 +733,24 @@

function extractStageFromSuffixedResourceName(stageSuffixedResourceName, context) {
return _extractStageFromSuffixedName(stageSuffixedResourceName, RESOURCE_NAME_STAGE_SEPARATOR_SETTING, context)
return _extractStageFromSuffixedName(stageSuffixedResourceName, settingNames.resourceNameStageSeparator, context)
}
/**
* A default extractNameAndStageFromResourceName function that extracts the unqualified name and stage from the given
* stage-suffixed resource name.
*
* The unqualified name prefix is extracted from the given resource name by taking everything before the last occurrence
* of the configured resourceNameStageSeparator (if any).
*
* The stage suffix is extracted from the given resource name by taking everything after the last occurrence of the
* configured resourceNameStageSeparator (if any).
*
* @param {string} stageSuffixedResourceName - the stage-suffixed name of the resource
* @param {StageHandling|Object} context - the context to use with stage handling settings and logging functionality
* @returns {[string,string]} an array containing: the unqualified name and the stage (if extracted); otherwise the
* given name and an empty stage string
*/
function extractNameAndStageFromSuffixedResourceName(stageSuffixedResourceName, context) {
return _extractNameAndStageFromSuffixedName(stageSuffixedResourceName, settingNames.resourceNameStageSeparator, context);
}
// =====================================================================================================================

@@ -687,2 +774,3 @@ // Generic name qualification

if (isNotBlank(qualifiedName)) {
qualifiedName = trim(qualifiedName);
configureDefaultStageHandling(context);

@@ -692,4 +780,3 @@

const extractStageFromName = getStageHandlingFunction(context, extractStageFromNameSettingName);
return extractStageFromName ? extractStageFromName(trim(qualifiedName), context) : '';
return extractStageFromName ? trimOrEmpty(extractStageFromName(qualifiedName, context)) : '';
}

@@ -699,2 +786,27 @@ return '';

function _extractNameAndStageFromQualifiedName(qualifiedName, extractNameAndStageFromNameSettingName, context) {
qualifiedName = trimOrEmpty(qualifiedName);
if (isNotBlank(qualifiedName)) {
configureDefaultStageHandling(context);
// Resolve extractNameAndStageFromName function to use
const extractNameAndStageFromName = getStageHandlingFunction(context, extractNameAndStageFromNameSettingName);
if (extractNameAndStageFromName) {
const nameAndStage = extractNameAndStageFromName(qualifiedName, context);
if (Array.isArray(nameAndStage)) {
if (nameAndStage.length !== 2) {
const extractFnName = extractNameAndStageFromName.name ? extractNameAndStageFromName.name : 'extractNameAndStageFromName';
context.warn(`Extracted name and stage ${stringify(nameAndStage)} contains ${nameAndStage.length} elements, but ${extractFnName} function should return an array with a resolved name & stage`);
}
return [trimOrEmpty(nameAndStage[0]), trimOrEmpty(nameAndStage[1])];
}
if (nameAndStage) {
const extractFnName = extractNameAndStageFromName.name ? extractNameAndStageFromName.name : 'extractNameAndStageFromName';
context.warn(`Ignoring extracted name and stage (${stringify(nameAndStage)}), since ${extractFnName} function MUST return an array with a resolved name & stage`);
}
}
}
return [qualifiedName, ''];
}
// =====================================================================================================================

@@ -712,3 +824,3 @@ // Generic name qualification (default)

// Resolve injectInCase
const injectInCase = getStageHandlingSetting(context, INJECT_IN_CASE_SETTING);
const injectInCase = getStageHandlingSetting(context, settingNames.injectInCase);

@@ -728,3 +840,3 @@ return toStageSuffixedName(unsuffixedName, separator, stage, injectInCase);

// Resolve extractInCase
const extractInCase = getStageHandlingSetting(context, EXTRACT_IN_CASE_SETTING);
const extractInCase = getStageHandlingSetting(context, settingNames.extractInCase);

@@ -738,2 +850,21 @@ // Extract stage using separator and convert to case specified by extractInCase

function _extractNameAndStageFromSuffixedName(stageSuffixedName, separatorSettingName, context) {
if (isNotBlank(stageSuffixedName)) {
configureDefaultStageHandling(context);
// Resolve separator
const separator = getStageHandlingSetting(context, separatorSettingName);
// Resolve extractInCase
const extractInCase = getStageHandlingSetting(context, settingNames.extractInCase);
// Extract stage using separator and convert to case specified by extractInCase
const suffixStartPos = stageSuffixedName.lastIndexOf(separator);
return suffixStartPos !== -1 ?
[trimOrEmpty(stageSuffixedName.substring(0, suffixStartPos)), trimOrEmpty(toCase(stageSuffixedName.substring(suffixStartPos + 1), extractInCase))] :
[trimOrEmpty(stageSuffixedName), ''];
}
return [trimOrEmpty(stageSuffixedName), ''];
}
/**

@@ -740,0 +871,0 @@ * Returns a stage-suffixed version of the given unsuffixed name with an appended stage suffix, which will contain the

'use strict';
// Constants
const MAX_PARTITION_KEY_SIZE = 256;
const arns = require('./arns');
const Strings = require('core-functions/strings');
const isNotBlank = Strings.isNotBlank;
const stringify = Strings.stringify;
/**
* Utilities for extracting information from AWS Kinesis and AWS DynamoDB stream events.
* Valid event names for a DynamoDB stream event.
* @enum {string}
* @readonly
*/
const DynamoDBEventName = {
INSERT: 'INSERT',
MODIFY: 'MODIFY',
REMOVE: 'REMOVE'
};
/**
* Utilities for validating and extracting information from AWS Kinesis and AWS DynamoDB stream events.
* @module aws-core-utils/stream-events

@@ -14,2 +27,16 @@ * @author Byron du Preez

module.exports = {
MAX_PARTITION_KEY_SIZE: MAX_PARTITION_KEY_SIZE,
/** Valid event names for a DynamoDB stream event */
DynamoDBEventName: DynamoDBEventName,
/** Returns the event id from the given stream event record */
getEventID: getEventID,
/** Returns the event name from the given stream event record */
getEventName: getEventName,
/** Returns the event source from the given stream event record */
getEventSource: getEventSource,
/** Returns the event source ARN from the given stream event record */
getEventSourceARN: getEventSourceARN,
/** Returns the event source ARNs of the given stream event's records */

@@ -24,3 +51,12 @@ getEventSourceARNs: getEventSourceARNs,

getKinesisEventSourceStreamName: getKinesisEventSourceStreamName,
/** Extracts and returns the shard id from the given Kinesis stream event record */
getKinesisShardId: getKinesisShardId,
/** Extracts and returns the shard id from the given Kinesis eventID */
getKinesisShardIdFromEventID: getKinesisShardIdFromEventID,
/** Extracts and returns the shard id and event number from the given Kinesis eventID */
getKinesisShardIdAndEventNoFromEventID: getKinesisShardIdAndEventNoFromEventID,
/** Returns the sequence number from the given Kinesis stream event record */
getKinesisSequenceNumber: getKinesisSequenceNumber,
/** Extracts and returns the table names from the given DynamoDB stream event records' eventSourceARNs */

@@ -33,2 +69,5 @@ getDynamoDBEventSourceTableNames: getDynamoDBEventSourceTableNames,

/** Returns the sequence number from the given DynamoDB stream event record */
getDynamoDBSequenceNumber: getDynamoDBSequenceNumber,
/** Validates the given stream event record and raises an error if the record is invalid or not a Kinesis or DynamoDB stream event record */

@@ -46,8 +85,44 @@ validateStreamEventRecord: validateStreamEventRecord,

/**
* Returns the event id from the given stream event record.
* @param {AnyStreamEventRecord|*} record - a stream event record
* @returns {string} the event id (if any) or an empty string
*/
function getEventID(record) {
return record && record.eventID ? record.eventID : '';
}
/**
* Returns the event name from the given stream event record.
* @param {AnyStreamEventRecord|*} record - a stream event record
* @returns {string} the event name (if any) or an empty string
*/
function getEventName(record) {
return record && record.eventName ? record.eventName : '';
}
/**
* Returns the event source from the given stream event record.
* @param {AnyStreamEventRecord|*} record - a stream event record
* @returns {string} the event source (if any) or an empty string
*/
function getEventSource(record) {
return record && record.eventSource ? record.eventSource : '';
}
/**
* Returns the event source ARN from the given stream event record.
* @param {AnyStreamEventRecord|*} record - a stream event record
* @returns {string} the event source ARN (if any) or an empty string
*/
function getEventSourceARN(record) {
return record && record.eventSourceARN ? record.eventSourceARN : '';
}
/**
* Returns the event source ARNs of the given stream event's records (if any); otherwise returns an empty array.
* @param event - a Kinesis or DynamoDB stream event
* @param {AnyStreamEvent|*} event - a Kinesis or DynamoDB stream event
* @returns {string[]} an array of event source ARNs (one for each stream event record)
*/
function getEventSourceARNs(event) {
return event && event.Records ? event.Records.map(r => r.eventSourceARN) : [];
return event && Array.isArray(event.Records) ? event.Records.map(getEventSourceARN) : [];
}

@@ -57,7 +132,7 @@

* Returns the event sources of the given stream event's records (if any); otherwise returns an empty array.
* @param event - a Kinesis or DynamoDB stream event
* @param {AnyStreamEvent|*} event - a Kinesis or DynamoDB stream event
* @returns {string[]} an array of event sources (one for each stream event record)
*/
function getEventSources(event) {
return event && event.Records ? event.Records.map(r => r.eventSource) : [];
return event && Array.isArray(event.Records) ? event.Records.map(getEventSource) : [];
}

@@ -68,7 +143,7 @@

* returns an empty array.
* @param event - a Kinesis stream event
* @param {KinesisEvent|*} event - a Kinesis stream event
* @returns {string[]} an array of event source stream names (one for each stream event record)
*/
function getKinesisEventSourceStreamNames(event) {
return event && event.Records ? event.Records.map(getKinesisEventSourceStreamName) : [];
return event && Array.isArray(event.Records) ? event.Records.map(getKinesisEventSourceStreamName) : [];
}

@@ -79,10 +154,56 @@

* an empty string.
* @param record - a Kinesis stream event record
* @param {KinesisEventRecord|*} record - a Kinesis stream event record
* @returns {string} the stream name (if any) or an empty string
*/
function getKinesisEventSourceStreamName(record) {
return record && isNotBlank(record.eventSourceARN) ? arns.getArnResources(record.eventSourceARN).resource : '';
return record && record.eventSourceARN ? arns.getArnResources(record.eventSourceARN).resource : '';
}
/**
* Extracts the shard id from the given Kinesis record's eventID.
* @param {KinesisEventRecord|*} record - a Kinesis stream event record
* @returns {string} the shard id (if any) or an empty string
*/
function getKinesisShardId(record) {
return record && record.eventID ? getKinesisShardIdFromEventID(record.eventID) : '';
}
/**
* Extracts the shard id from the given Kinesis eventID.
* @param {string} eventID - an eventID from an AWS Kinesis stream event record.
* @return {string|undefined} the shard id (if any) or an empty string
*/
function getKinesisShardIdFromEventID(eventID) {
if (eventID) {
const sepPos = eventID.indexOf(':');
return sepPos !== -1 ? eventID.substring(0, sepPos) : '';
}
return '';
}
/**
* Extracts the shard id and event number from the given Kinesis eventID.
* @param {string} eventID - an eventID from an AWS Kinesis stream event record.
* @return {[string,string]} an array containing: the shard id (if any) or an empty string; and the event number (if any)
* or an empty string
*/
function getKinesisShardIdAndEventNoFromEventID(eventID) {
if (eventID) {
const sepPos = eventID.indexOf(':');
return sepPos !== -1 ?
[eventID.substring(0, sepPos), eventID.substring(sepPos + 1), ] : ['', ''];
}
return ['', ''];
}
/**
* Gets the sequence number from the given Kinesis stream event record.
* @param {KinesisEventRecord|*} record - a Kinesis stream event record
* @returns {string} the sequence number (if any) or an empty string
*/
function getKinesisSequenceNumber(record) {
return record && record.kinesis && record.kinesis.sequenceNumber ? record.kinesis.sequenceNumber : '';
}
/**
* Extracts and returns an arrays containing the table name followed by the stream timestamp/suffix from the given

@@ -95,3 +216,3 @@ * DynamoDB stream event record's eventSourceARN (if any); otherwise returns an array of 2 empty strings.

*
* @param record - a DynamoDB stream event record
* @param {DynamoDBEventRecord|*} record - a DynamoDB stream event record
* @returns {[string, string]} an array containing the table name (if any or empty) followed by an empty string followed

@@ -101,3 +222,3 @@ * by the stream timestamp/suffix (if any or empty)

function getDynamoDBEventSourceTableNameAndStreamTimestamp(record) {
if (record && isNotBlank(record.eventSourceARN)) {
if (record && record.eventSourceARN) {
const resources = arns.getArnResources(record.eventSourceARN);

@@ -112,7 +233,7 @@ return [resources.resource, resources.subResource];

* otherwise returns an empty array.
* @param event - a DynamoDB stream event
* @param {DynamoDBEvent|*} event - a DynamoDB stream event
* @returns {string[]} an array of event source table names (one for each stream event record)
*/
function getDynamoDBEventSourceTableNames(event) {
return event && event.Records ? event.Records.map(getDynamoDBEventSourceTableName) : [];
return event && Array.isArray(event.Records) ? event.Records.map(getDynamoDBEventSourceTableName) : [];
}

@@ -123,26 +244,30 @@

* returns an empty string.
* @param record - a DynamoDB stream event record
* @param {DynamoDBEventRecord|*} record - a DynamoDB stream event record
* @returns {string} the table name (if any) or an empty string (if none)
*/
function getDynamoDBEventSourceTableName(record) {
return record && isNotBlank(record.eventSourceARN) ? arns.getArnResources(record.eventSourceARN).resource : '';
return record && record.eventSourceARN ? arns.getArnResources(record.eventSourceARN).resource : '';
}
/**
* Returns the sequence number from the given DynamoDB stream event record.
* @param {DynamoDBEventRecord|*} record - a DynamoDB stream event record
* @returns {string} the sequence number (if any) or an empty string
*/
function getDynamoDBSequenceNumber(record) {
return record && record.dynamodb && record.dynamodb.SequenceNumber ? record.dynamodb.SequenceNumber : '';
}
/**
* Validates the given stream event record and raises an error if the record fails to meet any of the following criteria:
* 1. It must be defined;
* 2. It must contain a defined eventSource;
* 3. It must be either a Kinesis or DynamoDB stream event record; and
* 4. It must contain the required properties expected of its type (based on its eventSource).
* 1. It must be a valid stream event record according to {@linkcode _validateStreamEventRecord};
* 2. It must be either a Kinesis or DynamoDB stream event record; and
* 3. It must contain the required properties expected of its type (based on its eventSource).
*
* @param {Object} record - a Kinesis or DynamoDB stream event record
* @param {AnyStreamEventRecord|*} record - a Kinesis or DynamoDB stream event record
* @throws {Error} if the record is invalid
*/
function validateStreamEventRecord(record) {
if (!record) {
throw new Error(`Missing entire stream event record (${record})`);
}
if (!record.eventSource) {
throw new Error(`Missing eventSource property for stream event record (${stringify(record)})`);
}
_validateStreamEventRecord(record);
switch (record.eventSource) {

@@ -160,3 +285,3 @@ case "aws:kinesis":

default:
// Only support Kinesis and DynamoDB stream event records for now
// Only support Kinesis and DynamoDB stream event records
throw new Error(`Unexpected eventSource (${record.eventSource}) on stream event record (${stringify(record)})`);

@@ -167,20 +292,41 @@ }

/**
* Validates the given stream event record and raises an error if the record fails to meet any of the following criteria:
* 1. It must be a non-null object
* 2. It must contain a defined eventID;
* 3. It must contain a defined eventSourceARN;
* 4. It must contain a defined eventSource;
*
* @param {AnyStreamEventRecord|*} record - a Kinesis or DynamoDB stream event record
* @throws {Error} if the record is invalid
*/
function _validateStreamEventRecord(record) {
if (!record || typeof record !== 'object') {
throw new Error(`Invalid stream event record (${record}) - record must be a non-null object`);
}
if (!record.eventID) {
throw new Error(`Missing eventID property for stream event record (${stringify(record)})`);
}
if (!record.eventSourceARN) {
throw new Error(`Missing eventSourceARN property for stream event record (${stringify(record)})`);
}
if (!record.eventSource) {
throw new Error(`Missing eventSource property for stream event record (${stringify(record)})`);
}
if (!record.eventName) {
throw new Error(`Missing eventName property for stream event record (${stringify(record)})`);
}
}
/**
* Validates the given Kinesis stream event record and raises an error if the record fails to meet any of the following criteria:
* 1. It must be defined;
* 1. It must be a valid stream event record according to {@linkcode _validateStreamEventRecord};
* 2. It must be a Kinesis stream event record (i.e. must contain an eventSource of "aws:kinesis"); and
* 3. It must contain kinesis and kinesis.data properties.
*
* @param {Object} record - a Kinesis stream event record
* @param {string} [record.eventSource] - a stream event record's eventSource
* @param {Object} [record.kinesis] - a Kinesis stream event record's kinesis object
* @param {string} [record.kinesis.data] - a Kinesis stream event record's kinesis data
* @param {KinesisEventRecord|*} record - a Kinesis stream event record
* @throws {Error} if the record is invalid
*/
function validateKinesisStreamEventRecord(record) {
if (!record) {
throw new Error(`Missing entire Kinesis stream event record (${record})`);
}
if (!record.eventSource) {
throw new Error(`Missing eventSource property for Kinesis stream event record (${stringify(record)})`);
}
_validateStreamEventRecord(record);
if (record.eventSource !== "aws:kinesis") {

@@ -192,2 +338,7 @@ throw new Error(`Unexpected eventSource (${record.eventSource}) on Kinesis stream event record (${stringify(record)})`)

/**
* Validates the given Kinesis stream event record.
* @param {KinesisEventRecord|*} record - a Kinesis stream event record
* @private
*/
function _validateKinesisStreamEventRecord(record) {

@@ -200,2 +351,8 @@ if (!record.kinesis) {

}
if (!record.kinesis.partitionKey) {
throw new Error(`Missing partitionKey property for Kinesis stream event record (${stringify(record)})`);
}
if (!record.kinesis.sequenceNumber) {
throw new Error(`Missing sequenceNumber property for Kinesis stream event record (${stringify(record)})`);
}
}

@@ -205,3 +362,3 @@

* Validates the given DynamoDB stream event record and raises an error if the record fails to meet any of the following criteria:
* 1. It must be defined;
* 1. It must be a valid stream event record according to {@linkcode _validateStreamEventRecord};
* 2. It must be a DynamoDB stream event record (i.e. must contain an eventSource of "aws:dynamodb");

@@ -211,18 +368,8 @@ * 3. It must contain dynamodb, dynamodb.Keys and dynamodb.StreamViewType properties; and

*
* @param {Object} record - a DynamoDB stream event record
* @param {string} [record.eventSource] - a stream event record's eventSource
* @param {Object} [record.dynamodb] - a DynamoDB stream event record's dynamodb object
* @param {Object} [record.dynamodb.Keys] - a DynamoDB stream event record's Keys object
* @param {string} [record.dynamodb.StreamViewType] - a DynamoDB stream event record's stream view type
* @param {Object} [record.dynamodb.NewImage] - a DynamoDB stream event record's new image object
* @param {Object} [record.dynamodb.OldImage] - a DynamoDB stream event record's old image object
* @param {DynamoDBEventRecord|*} record - a DynamoDB stream event record
* @throws {Error} if the record is invalid
*/
function validateDynamoDBStreamEventRecord(record) {
if (!record) {
throw new Error(`Missing entire DynamoDB stream event record (${record})`);
}
if (!record.eventSource) {
throw new Error(`Missing eventSource property for DynamoDB stream event record (${stringify(record)})`);
}
_validateStreamEventRecord(record);
if (record.eventSource !== "aws:dynamodb") {

@@ -234,3 +381,21 @@ throw new Error(`Unexpected eventSource (${record.eventSource}) on DynamoDB stream event record (${stringify(record)})`)

/**
* Returns true if the given DynamoDB event name OR given DynamoDB stream event record's eventName is valid; false otherwise
* @param {string|DynamoDBEventRecord} recordOrEventName - a DynamoDB event name OR DynamoDB stream event record
* @returns {boolean} true if valid; false otherwise
*/
function isDynamoDBEventNameValid(recordOrEventName) {
const eventName = recordOrEventName.eventName ? recordOrEventName.eventName : recordOrEventName;
return eventName === DynamoDBEventName.INSERT || eventName === DynamoDBEventName.MODIFY || eventName === DynamoDBEventName.REMOVE;
}
/**
* Validates the given DynamoDB stream event record.
* @param {DynamoDBEventRecord|*} record - a DynamoDB stream event record
* @private
*/
function _validateDynamoDBStreamEventRecord(record) {
if (!isDynamoDBEventNameValid(record)) {
throw new Error(`Invalid eventName property (${record.eventName}) for DynamoDB stream event record (${stringify(record)})`);
}
if (!record.dynamodb) {

@@ -242,2 +407,5 @@ throw new Error(`Missing dynamodb property for DynamoDB stream event record (${stringify(record)})`);

}
if (!record.dynamodb.SequenceNumber) {
throw new Error(`Missing SequenceNumber property for DynamoDB stream event record (${stringify(record)})`);
}
if (!record.dynamodb.StreamViewType) {

@@ -251,3 +419,3 @@ throw new Error(`Missing StreamViewType property for DynamoDB stream event record (${stringify(record)})`);

case 'NEW_IMAGE':
if (!record.dynamodb.NewImage) {
if (!record.dynamodb.NewImage && record.eventName !== 'REMOVE') {
throw new Error(`Missing NewImage property for DynamoDB stream event record (${stringify(record)})`);

@@ -258,3 +426,3 @@ }

case 'OLD_IMAGE':
if (!record.dynamodb.OldImage) {
if (!record.dynamodb.OldImage && record.eventName !== 'INSERT') {
throw new Error(`Missing OldImage property for DynamoDB stream event record (${stringify(record)})`);

@@ -265,3 +433,3 @@ }

case 'NEW_AND_OLD_IMAGES':
if (!record.dynamodb.NewImage && !record.dynamodb.OldImage) {
if ((!record.dynamodb.NewImage && record.eventName !== 'REMOVE') || (!record.dynamodb.OldImage && record.eventName !== 'INSERT')) {
throw new Error(`Missing both NewImage and OldImage properties for DynamoDB stream event record (${stringify(record)})`);

@@ -274,20 +442,2 @@ }

}
}
// /**
// * Returns short descriptive key information for the given Kinesis or DynamoDB stream record, which includes the
// * record's partition key and the first & last 5 characters of its sequence number, for logging purposes.
// * @param {Object} record - a Kinesis or DynamoDB stream record
// * @return {string} short descriptive key information for the given record
// */
// function toStreamEventRecordTruncatedKeyInfo(record) {
// if (!record) return stringify(record);
// if (record.kinesis) {
// let seqNo = record.kinesis.sequenceNumber;
// let seqNoFragment = seqNo ? seqNo.substring(0, 5) + "..." + seqNo.substring(seqNo.length - 5) : '';
// return `${record.kinesis.partitionKey} ${seqNoFragment}`;
// } else if (record.dynamodb) {
// // TO DO
// }
// }
}

@@ -18,3 +18,3 @@ 'use strict';

require('core-functions/promises');
const Promises = require('core-functions/promises');

@@ -25,2 +25,3 @@ const Strings = require('core-functions/strings');

const logging = require('logging-utils');
const LogLevel = logging.LogLevel;

@@ -41,3 +42,3 @@ const stages = require("../stages");

context.info(`Simulating doing something useful with event ${JSON.stringify(event)}`);
return Promise.delay(ms)
return Promises.delay(ms)
.then(() => {

@@ -89,6 +90,6 @@ return rejectedError ? Promise.reject(rejectedError) : Promise.resolve(resolvedResponse);

const handler = apiLambdas.generateHandlerFunction(context, undefined, require('./sample-standard-options.json'),
fn, logging.INFO); //, undefined, 'Invalid do something request', 'Failed to do something useful', 'Did something useful');
fn, LogLevel.INFO); //, undefined, 'Invalid do something request', 'Failed to do something useful', 'Did something useful');
// Wrap the callback-based AWS Lambda handler function as a Promise returning function purely for testing purposes
const handlerWithPromise = Promise.wrap(handler);
const handlerWithPromise = Promises.wrap(handler);

@@ -139,6 +140,6 @@ // Invoke the handler function

const handler = apiLambdas.generateHandlerFunction(context, undefined, require('./sample-standard-options.json'),
fn, logging.DEBUG, undefined, 'Invalid do something request', 'Failed to do something useful', 'Did something useful');
fn, LogLevel.DEBUG, undefined, 'Invalid do something request', 'Failed to do something useful', 'Did something useful');
// Wrap the callback-based AWS Lambda handler function as a Promise returning function purely for testing purposes
const handlerWithPromise = Promise.wrap(handler);
const handlerWithPromise = Promises.wrap(handler);

@@ -198,6 +199,6 @@ // Invoke the handler function

const handler = apiLambdas.generateHandlerFunction(context, undefined, require('./sample-standard-options.json'),
fn, logging.TRACE, undefined, 'Invalid do something request', 'Failed to do something useful', 'Did something useful');
fn, LogLevel.TRACE, undefined, 'Invalid do something request', 'Failed to do something useful', 'Did something useful');
// Wrap the callback-based AWS Lambda handler function as a Promise returning function purely for testing purposes
const handlerWithPromise = Promise.wrap(handler);
const handlerWithPromise = Promises.wrap(handler);

@@ -204,0 +205,0 @@ // Invoke the handler function

@@ -8,2 +8,5 @@ 'use strict';

const logging = require('logging-utils');
const LogLevel = logging.LogLevel;
const strings = require('core-functions/strings');

@@ -247,3 +250,3 @@ const stringify = strings.stringify;

t.ok(context.stageHandling, 'context.stageHandling must be defined');
t.equal(context.logLevel, 'info', 'context.logLevel must be "info"');
t.equal(context.logLevel, LogLevel.INFO, `context.logLevel must be "${LogLevel.INFO}"`);
t.ok(typeof context.error === 'function', 'context.error must be defined');

@@ -274,3 +277,3 @@ t.ok(typeof context.warn === 'function', 'context.warn must be defined');

t.ok(context.stageHandling, 'context.stageHandling must be defined');
t.equal(context.logLevel, 'trace', 'context.logLevel must be "trace"');
t.equal(context.logLevel, LogLevel.TRACE, `context.logLevel must be "${LogLevel.TRACE}"`);
t.ok(typeof context.error === 'function', 'context.error must be defined');

@@ -305,3 +308,3 @@ t.ok(typeof context.warn === 'function', 'context.warn must be defined');

t.equal(context.stageHandling.streamNameStageSeparator, '-', 'context.stageHandling must be "-"');
t.equal(context.logLevel, 'error', 'context.logLevel must be "error"');
t.equal(context.logLevel, LogLevel.ERROR, `context.logLevel must be "${LogLevel.ERROR}"`);
t.ok(typeof context.error === 'function', 'context.error must be defined');

@@ -338,3 +341,3 @@ t.ok(typeof context.warn === 'function', 'context.warn must be defined');

t.equal(context.stageHandling.streamNameStageSeparator, '-', 'context.stageHandling must be "-"');
t.equal(context.logLevel, 'error', 'context.logLevel must be "error"');
t.equal(context.logLevel, LogLevel.ERROR, `context.logLevel must be "${LogLevel.ERROR}"`);
t.ok(typeof context.error === 'function', 'context.error must be defined');

@@ -381,3 +384,3 @@ t.ok(typeof context.warn === 'function', 'context.warn must be defined');

t.equal(context.stageHandling.streamNameStageSeparator, '-', 'context.stageHandling must be "-"');
t.equal(context.logLevel, 'error', 'context.logLevel must be "error"');
t.equal(context.logLevel, LogLevel.ERROR, `context.logLevel must be "${LogLevel.ERROR}"`);
t.ok(typeof context.error === 'function', 'context.error must be defined');

@@ -384,0 +387,0 @@ t.ok(typeof context.warn === 'function', 'context.warn must be defined');

@@ -25,2 +25,3 @@ 'use strict';

const logging = require('logging-utils');
const LogLevel = logging.LogLevel;

@@ -40,3 +41,3 @@ const Strings = require('core-functions/strings');

const context = {};
logging.configureLoggingWithSettings(context, logging.TRACE);
logging.configureLogging(context, {logLevel: LogLevel.TRACE});

@@ -159,3 +160,3 @@ // Set current region

const context = {};
logging.configureLoggingWithSettings(context, logging.DEBUG);
logging.configureLogging(context, {logLevel: LogLevel.DEBUG});

@@ -162,0 +163,0 @@ process.env.AWS_REGION = 'us-west-1';

@@ -15,5 +15,6 @@ 'use strict';

const toValueFromAttributeTypeAndValue = dynamoDBUtils.toValueFromAttributeTypeAndValue;
const toNumber = dynamoDBUtils.toNumber;
const toKeyValueStrings = dynamoDBUtils.toKeyValueStrings;
const toKeyValuePairs = dynamoDBUtils.toKeyValuePairs;
const toStorableObject = dynamoDBUtils.toStorableObject;
const defaults = dynamoDBUtils.defaults;

@@ -23,42 +24,27 @@ const strings = require('core-functions/strings');

test('toNumber', t => {
// A simple integer
t.equal(toNumber('103'), 103, `toNumber('103') must be ${103}`);
// A simple float
t.equal(toNumber('3.1415679'), 3.1415679, `toNumber('3.1415679') must be ${3.1415679}`);
const putRequest = {
TableName : 'Table',
Item: {
hashKey: 'hashkey',
numAttribute: 1,
boolAttribute: true,
list1: [1, 'two', false, '', null],
empty1: '',
undefined1: undefined,
map1: {
foo: 'bar',
empty2: '',
undefined2: undefined,
map2: {
abc: 'abc',
empty3: '',
undefined3: undefined,
spaces: ' ',
null2: null
}
},
null1: null
}
};
// +/- 2 to the power of 53 should still give numbers, since have about 54 bits of precisions for integers
t.equal(toNumber('9007199254740992'), 9007199254740992, `toNumber('9007199254740992') must be ${9007199254740992}`);
t.equal(toNumber('-9007199254740992'), -9007199254740992, `toNumber('-9007199254740992') must be ${-9007199254740992}`);
// +/- 2 to the power of 54 is still ok
t.equal(toNumber('18014398509481984'), 18014398509481984, `toNumber('18014398509481984') must be ${18014398509481984}`);
t.equal(toNumber('-18014398509481984'), -18014398509481984, `toNumber('-18014398509481984') must be ${-18014398509481984}`);
// +/- 2 to the power of 55 is too big
t.notEqual(toNumber('36028797018963968'), 36028797018963968, `toNumber('36028797018963968') must NOT be ${36028797018963968}`);
t.notEqual(toNumber('-36028797018963968'), -36028797018963968, `toNumber('-36028797018963968') must NOT be ${-36028797018963968}`);
t.equal(toNumber('36028797018963968'), '36028797018963968', `toNumber('36028797018963968') must be '36028797018963968'`);
t.equal(toNumber('-36028797018963968'), '-36028797018963968', `toNumber('-36028797018963968') must be '-36028797018963968'`);
// +/- 2 to the power of 56 is too big
t.notEqual(toNumber('72057594037927936'), 72057594037927936, `toNumber('72057594037927936') must NOT be ${72057594037927936}`);
t.notEqual(toNumber('-72057594037927936'), -72057594037927936, `toNumber('-72057594037927936') must NOT be ${-72057594037927936}`);
t.equal(toNumber('72057594037927936'), '72057594037927936', `toNumber('72057594037927936') must be '72057594037927936'`);
t.equal(toNumber('-72057594037927936'), '-72057594037927936', `toNumber('-72057594037927936') must be '-72057594037927936'`);
// Too big to hold in an integer
t.equal(toNumber('9223372036854775807'), '9223372036854775807', `toNumber('9223372036854775807') must be '9223372036854775807'`);
t.equal(toNumber('-9223372036854775808'), '-9223372036854775808', `toNumber('-9223372036854775808') must be '-9223372036854775808'`);
t.ok(Number.isNaN(toNumber('')), `toNumber('') must be NaN`);
t.ok(Number.isNaN(toNumber('abc')), `toNumber('abc') must be NaN`);
t.ok(Number.isNaN(toNumber(undefined)), `toNumber(undefined) must be NaN`);
t.ok(Number.isNaN(toNumber(null)), `toNumber(null) must be NaN`);
t.ok(Number.isNaN(toNumber({})), `toNumber({}) must be NaN`);
t.ok(Number.isNaN(toNumber([])), `toNumber([]) must be NaN`);
t.end();
});
test('toValueFromAttributeTypeAndValue', t => {

@@ -173,1 +159,82 @@ t.equal(toValueFromAttributeTypeAndValue('NULL', true), null, `toValueFromAttributeTypeAndValue('NULL', true) must be null`);

});
test('toStorableObject with default emptyStringReplacement', t => {
let emptyStringReplacement = defaults.emptyStringReplacement; // 'EMPTY_STRING';
//Defaults.emptyStringReplacement = emptyStringReplacement;
const item = toStorableObject(putRequest.Item);
console.log(`toStorableObject(putRequest.Item) = ${stringify(item)}`);
const oldItem = putRequest.Item;
// Check empty strings were replaced
t.equal(item.empty1, emptyStringReplacement, `item.empty1 must be ${emptyStringReplacement}`);
t.equal(item.map1.empty2, emptyStringReplacement, `item.map1.empty2 must be ${emptyStringReplacement}`);
t.equal(item.map1.map2.empty3, emptyStringReplacement, `item.map1.map2.empty3 must be ${emptyStringReplacement}`);
t.equal(item.list1[3], emptyStringReplacement, `item.list1[3] must be ${emptyStringReplacement}`);
// Check non-empty strings are still intact
t.equal(item.hashKey, oldItem.hashKey, `item.hashKey must be ${oldItem.hashKey}`);
t.equal(item.map1.foo, oldItem.map1.foo, `item.map1.foo must be ${oldItem.map1.foo}`);
t.equal(item.map1.map2.abc, oldItem.map1.map2.abc, `item.map1.map2.abc must be ${oldItem.map1.map2.abc}`);
t.equal(item.map1.map2.spaces, oldItem.map1.map2.spaces, `item.map1.map2.spaces must be ${oldItem.map1.map2.spaces}`);
t.equal(item.list1[1], oldItem.list1[1], `item.list1[1] must be ${oldItem.list1[1]}`);
const itemKeys = Object.getOwnPropertyNames(item);
const map1Keys = Object.getOwnPropertyNames(item.map1);
const map2Keys = Object.getOwnPropertyNames(item.map1.map2);
// Ensure that no undefined properties exist
t.equal(itemKeys.indexOf('undefined1'), -1, `item.undefined1 must NOT exist`);
t.equal(map1Keys.indexOf('undefined2'), -1, `item.map1.undefined2 must NOT exist`);
t.equal(map2Keys.indexOf('undefined3'), -1, `item.map1.map2.undefined3 must NOT exist`);
// Ensure that null properties are intact
t.notEqual(itemKeys.indexOf('null1'), -1, `item.null1 must exist`);
t.equal(item.null1, null, `item.null1 must be null`);
t.equal(item.map1.map2.null2, null, `item.map1.map2.null2 must be null`);
t.equal(item.list1[4], null, `item.list1[4] must be null`);
t.end();
});
test('toStorableObject with overridden Defaults.emptyStringReplacement', t => {
let emptyStringReplacement = 'EMPTY_STRING';
// Override the default empty string replacement
defaults.emptyStringReplacement = emptyStringReplacement;
const item = toStorableObject(putRequest.Item);
console.log(`toStorableObject(putRequest.Item) = ${stringify(item)}`);
const oldItem = putRequest.Item;
// Check empty strings were replaced
t.equal(item.empty1, emptyStringReplacement, `item.empty1 must be ${emptyStringReplacement}`);
t.equal(item.map1.empty2, emptyStringReplacement, `item.map1.empty2 must be ${emptyStringReplacement}`);
t.equal(item.map1.map2.empty3, emptyStringReplacement, `item.map1.map2.empty3 must be ${emptyStringReplacement}`);
t.equal(item.list1[3], emptyStringReplacement, `item.list1[3] must be ${emptyStringReplacement}`);
// Check non-empty strings are still intact
t.equal(item.hashKey, oldItem.hashKey, `item.hashKey must be ${oldItem.hashKey}`);
t.equal(item.map1.foo, oldItem.map1.foo, `item.map1.foo must be ${oldItem.map1.foo}`);
t.equal(item.map1.map2.abc, oldItem.map1.map2.abc, `item.map1.map2.abc must be ${oldItem.map1.map2.abc}`);
t.equal(item.map1.map2.spaces, oldItem.map1.map2.spaces, `item.map1.map2.spaces must be ${oldItem.map1.map2.spaces}`);
t.equal(item.list1[1], oldItem.list1[1], `item.list1[1] must be ${oldItem.list1[1]}`);
const itemKeys = Object.getOwnPropertyNames(item);
const map1Keys = Object.getOwnPropertyNames(item.map1);
const map2Keys = Object.getOwnPropertyNames(item.map1.map2);
// Ensure that no undefined properties exist
t.equal(itemKeys.indexOf('undefined1'), -1, `item.undefined1 must NOT exist`);
t.equal(map1Keys.indexOf('undefined2'), -1, `item.map1.undefined2 must NOT exist`);
t.equal(map2Keys.indexOf('undefined3'), -1, `item.map1.map2.undefined3 must NOT exist`);
// Ensure that null properties are intact
t.notEqual(itemKeys.indexOf('null1'), -1, `item.null1 must exist`);
t.equal(item.null1, null, `item.null1 must be null`);
t.equal(item.map1.map2.null2, null, `item.map1.map2.null2 must be null`);
t.equal(item.list1[4], null, `item.list1[4] must be null`);
t.end();
});

@@ -25,2 +25,3 @@ 'use strict';

const logging = require('logging-utils');
const LogLevel = logging.LogLevel;

@@ -40,3 +41,3 @@ const Strings = require('core-functions/strings');

const context = {};
logging.configureLoggingWithSettings(context, logging.TRACE);
logging.configureLogging(context, {logLevel: LogLevel.TRACE});

@@ -159,3 +160,3 @@ // Set current region

const context = {};
logging.configureLoggingWithSettings(context, logging.DEBUG);
logging.configureLogging(context, {logLevel: LogLevel.DEBUG});

@@ -162,0 +163,0 @@ process.env.AWS_REGION = 'us-west-1';

{
"name": "aws-core-utils-tests",
"description": "Unit tests for aws-core-utils modules",
"version": "5.0.17",
"version": "6.0.0",
"author": "Byron du Preez",
"license": "Apache-2.0",
"private": true,
"engines": {

@@ -8,0 +9,0 @@ "node": ">=4.3.2"

{
"loggingOptions": {
"logLevel": "trace",
"logLevel": "TRACE",
"useLevelPrefixes": true,
"envLogLevelName": "LOG_LEVEL",
"useConsoleTrace": false

@@ -6,0 +7,0 @@ },

@@ -12,2 +12,6 @@ 'use strict';

const streamEvents = require('../stream-events');
const getEventID = streamEvents.getEventID;
const getEventSource = streamEvents.getEventSource;
const getEventSourceARN = streamEvents.getEventSourceARN;
//const getEventSources = streamEvents.getEventSources;
const getEventSourceARNs = streamEvents.getEventSourceARNs;

@@ -18,5 +22,13 @@

//const getDynamoDBEventSourceTableName = streamEvents.getDynamoDBEventSourceTableName;
//const getDynamoDBEventSourceTableNameAndStreamTimestamp = streamEvents.getDynamoDBEventSourceTableNameAndStreamTimestamp;
const getKinesisShardId = streamEvents.getKinesisShardId;
const getKinesisShardIdFromEventID = streamEvents.getKinesisShardIdFromEventID;
const getKinesisShardIdAndEventNoFromEventID = streamEvents.getKinesisShardIdAndEventNoFromEventID;
const getKinesisSequenceNumber = streamEvents.getKinesisSequenceNumber;
// const getDynamoDBEventSourceTableName = streamEvents.getDynamoDBEventSourceTableName;
// const getDynamoDBEventSourceTableNameAndStreamTimestamp = streamEvents.getDynamoDBEventSourceTableNameAndStreamTimestamp;
const getDynamoDBSequenceNumber = streamEvents.getDynamoDBSequenceNumber;
const validateStreamEventRecord = streamEvents.validateStreamEventRecord;

@@ -35,2 +47,55 @@ const validateKinesisStreamEventRecord = streamEvents.validateKinesisStreamEventRecord;

// =====================================================================================================================
// getEventID, getEventSource, getEventSourceARN, getKinesisSequenceNumber & getDynamoDBSequenceNumber
// =====================================================================================================================
test('getEventID, getEventSource, getEventSourceARN, getKinesisSequenceNumber & getDynamoDBSequenceNumber', t => {
// With garbage
t.equal(getEventID(undefined), '', `getEventID(undefined) must be ''`);
t.equal(getEventSource(undefined), '', `getEventSource(undefined) must be ''`);
t.equal(getEventSourceARN(undefined), '', `getEventSourceARN(undefined) must be ''`);
t.equal(getKinesisSequenceNumber(undefined), '', `getKinesisSequenceNumber(undefined) must be ''`);
t.equal(getDynamoDBSequenceNumber(undefined), '', `getDynamoDBSequenceNumber(undefined) must be ''`);
t.equal(getEventID(null), '', `getEventID(null) must be ''`);
t.equal(getEventSource(null), '', `getEventSource(null) must be ''`);
t.equal(getEventSourceARN(null), '', `getEventSourceARN(null) must be ''`);
t.equal(getKinesisSequenceNumber(null), '', `getKinesisSequenceNumber(null) must be ''`);
t.equal(getDynamoDBSequenceNumber(null), '', `getDynamoDBSequenceNumber(null) must be ''`);
t.equal(getEventID({}), '', `getEventID({}) must be ''`);
t.equal(getEventSource({}), '', `getEventSource({}) must be ''`);
t.equal(getEventSourceARN({}), '', `getEventSourceARN({}) must be ''`);
t.equal(getKinesisSequenceNumber({}), '', `getKinesisSequenceNumber({}) must be ''`);
t.equal(getDynamoDBSequenceNumber({}), '', `getDynamoDBSequenceNumber({}) must be ''`);
t.equal(getEventID('junk'), '', `getEventID('junk') must be ''`);
t.equal(getEventSource('junk'), '', `getEventSource('junk') must be ''`);
t.equal(getEventSourceARN('junk'), '', `getEventSourceARN('junk') must be ''`);
t.equal(getKinesisSequenceNumber('junk'), '', `getKinesisSequenceNumber('junk') must be ''`);
t.equal(getDynamoDBSequenceNumber('junk'), '', `getDynamoDBSequenceNumber('junk') must be ''`);
// With a Kinesis stream event record
const kinesisEventSourceArn = samples.sampleKinesisEventSourceArn('eventSourceArnRegion', 'TestStream_QA');
const kinesisRecord = samples.sampleKinesisRecord(undefined, undefined, kinesisEventSourceArn, 'eventAwsRegion');
t.equal(getEventID(kinesisRecord), kinesisRecord.eventID, `getEventID(kinesisRecord) must be '${kinesisRecord.eventID}'`);
t.equal(getEventSource(kinesisRecord), kinesisRecord.eventSource, `getEventSource(kinesisRecord) must be '${kinesisRecord.eventSource}'`);
t.equal(getEventSourceARN(kinesisRecord), kinesisRecord.eventSourceARN, `getEventSourceARN(kinesisRecord) must be '${kinesisRecord.eventSourceARN}'`);
t.equal(getKinesisSequenceNumber(kinesisRecord), kinesisRecord.kinesis.sequenceNumber, `getKinesisSequenceNumber(kinesisRecord) must be '${kinesisRecord.kinesis.sequenceNumber}'`);
t.equal(getDynamoDBSequenceNumber(kinesisRecord), '', `getDynamoDBSequenceNumber(kinesisRecord) must be ''`);
// With a DynamoDB stream event record
const dynamoDBEventSourceArn = samples.sampleDynamoDBEventSourceArn('eventSourceArnRegion', 'TestStream_UAT');
const dynamoDBRecord = samples.awsDynamoDBUpdateSampleEvent(dynamoDBEventSourceArn).Records[0];
t.equal(getEventID(dynamoDBRecord), dynamoDBRecord.eventID, `getEventID(dynamoDBRecord) must be '${dynamoDBRecord.eventID}'`);
t.equal(getEventSource(dynamoDBRecord), dynamoDBRecord.eventSource, `getEventSource(dynamoDBRecord) must be '${dynamoDBRecord.eventSource}'`);
t.equal(getEventSourceARN(dynamoDBRecord), dynamoDBRecord.eventSourceARN, `getEventSourceARN(dynamoDBRecord) must be '${dynamoDBRecord.eventSourceARN}'`);
t.equal(getDynamoDBSequenceNumber(dynamoDBRecord), dynamoDBRecord.dynamodb.SequenceNumber, `getDynamoDBSequenceNumber(dynamoDBRecord) must be '${stringify(dynamoDBRecord.dynamodb.SequenceNumber)}'`);
t.equal(getKinesisSequenceNumber(dynamoDBRecord), '', `getKinesisSequenceNumber(dynamoDBRecord) must be ''`);
t.end();
});
// =====================================================================================================================
// getEventSourceARNs

@@ -42,3 +107,3 @@ // =====================================================================================================================

const eventSourceArns = streamNames.map(streamName =>
isNotBlank(streamName) ? samples.sampleKinesisEventSourceArn('eventSourceArnRegion', trim(streamName)) : trim(streamName));
isNotBlank(streamName) ? samples.sampleKinesisEventSourceArn('eventSourceArnRegion', trimOrEmpty(streamName)) : trimOrEmpty(streamName));
const records = eventSourceArns.map(eventSourceArn =>

@@ -155,2 +220,97 @@ samples.sampleKinesisRecord(undefined, undefined, eventSourceArn, 'eventAwsRegion'));

// =====================================================================================================================
// getKinesisShardIdFromEventID
// =====================================================================================================================
test('getKinesisShardIdFromEventID', t => {
// Other cases
t.equal(getKinesisShardIdFromEventID(undefined), '', `eventID (undefined) shardId must be ''`);
t.equal(getKinesisShardIdFromEventID(null), '', `eventID (null) shardId must be ''`);
t.equal(getKinesisShardIdFromEventID(''), '', `eventID '' shardId must be ''`);
t.equal(getKinesisShardIdFromEventID(':'), '', `eventID ':' shardId must be ''`);
t.equal(getKinesisShardIdFromEventID('shardId-'), '', `eventID 'shardId-' shardId must be ''`);
t.equal(getKinesisShardIdFromEventID('shardId-:'), 'shardId-', `eventID 'shardId-:' shardId must be 'shardId-'`);
t.equal(getKinesisShardIdFromEventID('shardId-0:'), 'shardId-0', `eventID 'shardId-0:' shardId must be 'shardId-0'`);
t.equal(getKinesisShardIdFromEventID('shardId-012:'), 'shardId-012', `eventID 'shardId-012:' shardId must be 'shardId-012'`);
// More real eventIDs
let eventID = 'shardId-000000000000:49545115243490985018280067714973144582180062593244200961';
let expected = 'shardId-000000000000';
t.equal(getKinesisShardIdFromEventID(eventID), expected, `eventID '${eventID}' shardId must be '${expected}'`);
eventID = 'shardId-123456789012:49545115243490985018280067714973144582180062593244200961';
expected = 'shardId-123456789012';
t.equal(getKinesisShardIdFromEventID(eventID), expected, `eventID '${eventID}' shardId must be '${expected}'`);
t.end();
});
// =====================================================================================================================
// getKinesisShardId
// =====================================================================================================================
test('getKinesisShardId', t => {
function toRecord(eventID) {
return {eventID: eventID};
}
// Other cases
t.equal(getKinesisShardId(undefined), '', `record (undefined) shardId must be ''`);
t.equal(getKinesisShardId({}), '', `record ({}) shardId must be ''`);
t.equal(getKinesisShardId(toRecord(undefined)), '', `record ${JSON.stringify(toRecord(undefined))} shardId must be ''`);
t.equal(getKinesisShardId(toRecord(null)), '', `record ${JSON.stringify(toRecord(null))} shardId must be ''`);
t.equal(getKinesisShardId(toRecord('')), '', `record ${JSON.stringify(toRecord(''))} shardId must be ''`);
t.equal(getKinesisShardId(toRecord(':')), '', `record ${JSON.stringify(toRecord(':'))} shardId must be ''`);
t.equal(getKinesisShardId(toRecord('shardId-')), '', `record ${JSON.stringify(toRecord('shardId-'))} shardId must be ''`);
t.equal(getKinesisShardId(toRecord('shardId-:')), 'shardId-', `record ${JSON.stringify(toRecord('shardId-:'))} shardId must be 'shardId-'`);
t.equal(getKinesisShardId(toRecord('shardId-0:')), 'shardId-0', `record ${JSON.stringify(toRecord('shardId-0:'))} shardId must be 'shardId-0'`);
t.equal(getKinesisShardId(toRecord('shardId-012:')), 'shardId-012', `record ${JSON.stringify(toRecord('shardId-012:'))} shardId must be 'shardId-012'`);
// More real eventIDs
let eventID = 'shardId-000000000000:49545115243490985018280067714973144582180062593244200961';
let expected = 'shardId-000000000000';
t.equal(getKinesisShardId(toRecord(eventID)), expected, `record ${JSON.stringify(toRecord(eventID))} shardId must be '${expected}'`);
eventID = 'shardId-123456789012:49545115243490985018280067714973144582180062593244200961';
expected = 'shardId-123456789012';
t.equal(getKinesisShardId(toRecord(eventID)), expected, `record ${JSON.stringify(toRecord(eventID))} shardId must be '${expected}'`);
t.end();
});
// =====================================================================================================================
// getKinesisShardIdAndEventNoFromEventID
// =====================================================================================================================
test('getKinesisShardIdAndEventNoFromEventID', t => {
// Other cases
t.deepEqual(getKinesisShardIdAndEventNoFromEventID(undefined), ['', ''], `eventID (undefined) shardId & eventNo must be ['','']`);
t.deepEqual(getKinesisShardIdAndEventNoFromEventID(null), ['', ''], `eventID (null) shardId & eventNo must be ['','']`);
t.deepEqual(getKinesisShardIdAndEventNoFromEventID(''), ['', ''], `eventID '' shardId & eventNo must be ['','']`);
t.deepEqual(getKinesisShardIdAndEventNoFromEventID(':'), ['', ''], `eventID ':' shardId & eventNo must be ['','']`);
t.deepEqual(getKinesisShardIdAndEventNoFromEventID('shardId-'), ['', ''], `eventID 'shardId-' shardId & eventNo must be ['','']`);
t.deepEqual(getKinesisShardIdAndEventNoFromEventID('shardId-:'), ['shardId-', ''], `eventID 'shardId-:' shardId & eventNo must be ['shardId-','']`);
t.deepEqual(getKinesisShardIdAndEventNoFromEventID('shardId-0:'), ['shardId-0', ''], `eventID 'shardId-0:' shardId & eventNo must be ['shardId-0','']`);
t.deepEqual(getKinesisShardIdAndEventNoFromEventID('shardId-012:456'), ['shardId-012', '456'], `eventID 'shardId-012:456' shardId & eventNo must be ['shardId-012','456']`);
t.equal(getKinesisShardIdFromEventID(undefined), '', `eventID (undefined) shardId must be ''`);
t.equal(getKinesisShardIdFromEventID(null), '', `eventID (null) shardId must be ''`);
t.equal(getKinesisShardIdFromEventID(''), '', `eventID '' shardId must be ''`);
t.equal(getKinesisShardIdFromEventID(':'), '', `eventID ':' shardId must be ''`);
t.equal(getKinesisShardIdFromEventID('shardId-'), '', `eventID 'shardId-' shardId must be ''`);
t.equal(getKinesisShardIdFromEventID('shardId-:'), 'shardId-', `eventID 'shardId-:' shardId must be 'shardId-'`);
t.equal(getKinesisShardIdFromEventID('shardId-0:'), 'shardId-0', `eventID 'shardId-0:' shardId must be 'shardId-0'`);
t.equal(getKinesisShardIdFromEventID('shardId-012:'), 'shardId-012', `eventID 'shardId-012:' shardId must be 'shardId-012'`);
// More real eventIDs
let eventID = 'shardId-000000000000:49545115243490985018280067714973144582180062593244200961';
let expected = ['shardId-000000000000', '49545115243490985018280067714973144582180062593244200961'];
t.deepEqual(getKinesisShardIdAndEventNoFromEventID(eventID), expected, `eventID '${expected}' shardId & eventNo must be ${stringify(expected)}`);
eventID = 'shardId-123456789012:49545115243490985018280067714973144582180062593244200962';
expected = ['shardId-123456789012', '49545115243490985018280067714973144582180062593244200962'];
t.deepEqual(getKinesisShardIdAndEventNoFromEventID(eventID), expected, `eventID '${expected}' shardId & eventNo must be ${stringify(expected)}`);
t.end();
});
// =====================================================================================================================
// validateStreamEventRecord

@@ -195,3 +355,3 @@ // =====================================================================================================================

// "valid" Kinesis records
check({eventSource: 'aws:kinesis', kinesis: {data: "dummy_data"}}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:kinesis', kinesis: {partitionKey: 'shardId-000:123', data: "dummy_data", sequenceNumber: '123'}, eventName: 'aws:kinesis:record'}, true);

@@ -209,18 +369,18 @@ // valid Kinesis records

// invalid DynamoDB stream event records
check({eventSource: 'aws:dynamodb'}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OTHER"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {StreamViewType: "KEYS_ONLY"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", NewImage: {}}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", OldImage: {}}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES"}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OTHER"}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {StreamViewType: "KEYS_ONLY"}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE"}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", NewImage: {}}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE"}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", OldImage: {}}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES"}}, false);
// "valid" DynamoDB stream event records
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "KEYS_ONLY"}}, true);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", OldImage: {}}}, true);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", NewImage: {}}}, true);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}}}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "KEYS_ONLY", SequenceNumber: '123'}, eventName: 'MODIFY'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", OldImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", NewImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, true);

@@ -271,5 +431,6 @@ // valid DynamoDB stream event records

check({eventSource: 'aws:kinesis', kinesis: {}}, false);
check({eventSource: 'aws:kinesis', kinesis: {data: "dummy_data"}}, false);
// "valid" Kinesis records
check({eventSource: 'aws:kinesis', kinesis: {data: "dummy_data"}}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:kinesis', kinesis: {partitionKey: 'shardId-000-123', data: "dummy_data", sequenceNumber: '123'}, eventName: 'aws:kinesis:record'}, true);

@@ -286,6 +447,6 @@ // valid Kinesis records

// invalid - since DynamoDB stream event records
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "KEYS_ONLY"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", OldImage: {}}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", NewImage: {}}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "KEYS_ONLY", SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", OldImage: {}, SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", NewImage: {}, SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}, SequenceNumber: '123'}}, false);

@@ -332,21 +493,74 @@ // invalid - since DynamoDB stream event records

check({eventSource: 'aws:other'}, false);
check({eventID: 'eventID'}, false);
check({eventSourceARN: 'eventSourceARN'}, false);
check({eventID: 'eventID', eventSource: 'aws:other'}, false);
check({eventSourceARN: 'eventSourceARN', eventSource: 'aws:other'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:other'}, false);
// invalid Kinesis stream event records
check({eventSource: 'aws:kinesis'}, false);
check({eventID: 'eventID', eventSource: 'aws:kinesis'}, false);
check({eventSourceARN: 'eventSourceARN', eventSource: 'aws:kinesis'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:kinesis'}, false);
// invalid DynamoDB stream event records
check({eventSource: 'aws:dynamodb'}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OTHER"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {StreamViewType: "KEYS_ONLY"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", NewImage: {}}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE"}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", OldImage: {}}}, false);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES"}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OTHER", SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {StreamViewType: "KEYS_ONLY", SequenceNumber: '123'}, eventName: 'INSERT'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {StreamViewType: "KEYS_ONLY", SequenceNumber: '123'}, eventName: 'MODIFY'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {StreamViewType: "KEYS_ONLY", SequenceNumber: '123'}, eventName: 'REMOVE'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", SequenceNumber: '123'}, eventName: 'MODIFY'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", SequenceNumber: '123'}, eventName: 'REMOVE'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", NewImage: {}, SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", NewImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", NewImage: {}, SequenceNumber: '123'}, eventName: 'REMOVE'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", SequenceNumber: '123'}, eventName: 'INSERT'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", SequenceNumber: '123'}, eventName: 'MODIFY'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", OldImage: {}, SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", OldImage: {}, SequenceNumber: '123'}, eventName: 'INSERT'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", OldImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", SequenceNumber: '123'}, eventName: 'INSERT'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", SequenceNumber: '123'}, eventName: 'MODIFY'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", SequenceNumber: '123'}, eventName: 'REMOVE'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, SequenceNumber: '123'}, eventName: 'INSERT'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", NewImage: {}, SequenceNumber: '123'}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", NewImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", NewImage: {}, SequenceNumber: '123'}, eventName: 'REMOVE'}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}, SequenceNumber: '123'}}, false);
// "valid" DynamoDB stream event records
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "KEYS_ONLY"}}, true);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", OldImage: {}}}, true);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", NewImage: {}}}, true);
check({eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}}}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "KEYS_ONLY", SequenceNumber: '123'}, eventName: 'INSERT'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "KEYS_ONLY", SequenceNumber: '123'}, eventName: 'MODIFY'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "KEYS_ONLY", SequenceNumber: '123'}, eventName: 'REMOVE'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", SequenceNumber: '123'}, eventName: 'INSERT'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", NewImage: {}, SequenceNumber: '123'}, eventName: 'INSERT'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", OldImage: {}, SequenceNumber: '123'}, eventName: 'INSERT'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", OldImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "OLD_IMAGE", OldImage: {}, SequenceNumber: '123'}, eventName: 'REMOVE'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", SequenceNumber: '123'}, eventName: 'REMOVE'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", NewImage: {}, SequenceNumber: '123'}, eventName: 'INSERT'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", NewImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_IMAGE", NewImage: {}, SequenceNumber: '123'}, eventName: 'REMOVE'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, SequenceNumber: '123'}, eventName: 'REMOVE'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", NewImage: {}, SequenceNumber: '123'}, eventName: 'INSERT'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}, SequenceNumber: '123'}, eventName: 'INSERT'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}, SequenceNumber: '123'}, eventName: 'MODIFY'}, true);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:dynamodb', dynamodb: {Keys: {}, StreamViewType: "NEW_AND_OLD_IMAGES", OldImage: {}, NewImage: {}, SequenceNumber: '123'}, eventName: 'REMOVE'}, true);
// valid DynamoDB stream event records

@@ -359,3 +573,3 @@ const awsDynamoDBUpdateSampleEvent = samples.awsDynamoDBUpdateSampleEvent("identityArn", "eventSourceArn");

// invalid - since Kinesis records
check({eventSource: 'aws:kinesis', kinesis: {data: "dummy_data"}}, false);
check({eventID: 'eventID', eventSourceARN: 'eventSourceARN', eventSource: 'aws:kinesis', kinesis: {data: "dummy_data"}}, false);

@@ -362,0 +576,0 @@ // invalid - since Kinesis records

@@ -8,7 +8,3 @@ 'use strict';

/**
* @typedef {Object} AwsEvent - an AWS event passed to your Lambda handler function
*/
/**
* @typedef {Object} AwsContext - an AWS context passed to your Lambda handler function
* @typedef {Object} AWSContext - an AWS context passed to your Lambda handler function
* @property {uuid} awsRequestId - a unique identifier assigned to the current invocation of your handler function by AWS Lambda

@@ -19,3 +15,3 @@ * @property {function(): number} getRemainingTimeInMillis - gets the remaining time to execute in milliseconds

/**
* @typedef {function(event: AwsEvent, awsContext: AwsContext, callback: Callback)} AwsLambdaHandlerFunction - a handler
* @typedef {function(event: AWSEvent, awsContext: AWSContext, callback: Callback)} AwsLambdaHandlerFunction - a handler
* function for your AWS Lambda

@@ -33,3 +29,3 @@ */

* @property {string|undefined} [stage] - the configured stage to use
* @property {AwsContext|undefined} [awsContext] - the AWS context passed to your Lambda function on invocation
* @property {AWSContext|undefined} [awsContext] - the AWS context passed to your Lambda function on invocation
*/

@@ -84,3 +80,3 @@

* @property {string} region - the name of the AWS region to use
* @property {AwsContext} awsContext - the AWS context passed to your Lambda function on invocation
* @property {AWSContext} awsContext - the AWS context passed to your Lambda function on invocation
*/

@@ -106,3 +102,3 @@

/**
* @typedef {Logging} StageHandling - an object configured with stage handling and logging functionality
* @typedef {Logger} StageHandling - an object configured with stage handling and logging functionality
* @property {StageHandlingSettings} stageHandling - an object configured with stage handling settings and functionality to use

@@ -145,4 +141,5 @@ */

* settings determine how {@linkcode stages.js#resolveStage}, {@linkcode stages.js#toStageQualifiedStreamName},
* {@linkcode stages.js#extractStageFromQualifiedStreamName}, {@linkcode stages.js#toStageQualifiedResourceName},
* {@linkcode stages.js#extractStageFromQualifiedStreamName} and other internal functions will behave when invoked.
* {@linkcode stages.js#extractStageFromQualifiedStreamName}, {@linkcode stages.js#extractNameAndStageFromQualifiedStreamName},
* {@linkcode stages.js#toStageQualifiedResourceName}, {@linkcode stages.js#extractStageFromQualifiedResourceName},
* {@linkcode stages.js#extractNameAndStageFromQualifiedResourceName} and other internal functions will behave when invoked.
*

@@ -157,8 +154,10 @@ * They can also be used to pass any additional custom configuration options and settings that you need through to any

* @property {ExtractStageFromStreamName|undefined} [extractStageFromStreamName] - an optional function that extracts a stage from a stage-qualified stream name
* @property {ExtractNameAndStageFromStreamName|undefined} [extractNameAndStageFromStreamName] - an optional function that extracts the unqualified name and stage from a stage-qualified stream name
* @property {InjectStageIntoResourceName|undefined} [injectStageIntoResourceName] - an optional function that returns a stage-qualified resource name
* @property {ExtractStageFromResourceName|undefined} [extractStageFromResourceName] - an optional function that extracts a stage from a stage-qualified resource name
* @property {ExtractNameAndStageFromResourceName|undefined} [extractNameAndStageFromResourceName] - an optional function that extracts the unqualified name and stage from a stage-qualified resource name
*/
/**
* @typedef {function(event: AwsEvent, awsContext: AwsContext, context: StageHandling): (string|undefined)} CustomToStage -
* @typedef {function(event: AWSEvent, awsContext: AWSContext, context: StageHandling): (string|undefined)} CustomToStage -
* a custom function that accepts: an AWS event; an AWS context; and a context, and somehow extracts a usable stage from

@@ -169,3 +168,3 @@ * the AWS event and/or AWS context.

/**
* @typedef {function(alias: string, event: AwsEvent, awsContext: AwsContext, context: StageHandling): (string|undefined)} ConvertAliasToStage -
* @typedef {function(alias: string, event: AWSEvent, awsContext: AWSContext, context: StageHandling): (string|undefined)} ConvertAliasToStage -
* a function that accepts: an extracted AWS Lambda alias (if any); an AWS event; an AWS context; and a context, and

@@ -187,2 +186,8 @@ * converts the alias into a stage

/**
* @typedef {function(qualifiedStreamName: string, context: StageHandling):[string,string]} ExtractNameAndStageFromStreamName -
* a function that accepts: a stage-qualified stream name; and a context, and extracts the unqualified name and stage
* from the stream name
*/
/**
* @typedef {function(unqualifiedResourceName: string, stage: string, context: StageHandling):(string|undefined)} InjectStageIntoResourceName -

@@ -198,1 +203,152 @@ * a function that accepts: an unqualified resource name; a stage; and a context, and returns a stage-qualified resource

/**
* @typedef {function(qualifiedResourceName: string, context: StageHandling):[string,string]} ExtractNameAndStageFromResourceName -
* a function that accepts: a stage-qualified resource name; and a context, and extracts the unqualified name and stage
* from the resource name
*/
/**
* ARN resource-related components
* @typedef {Object} ArnResources
* @property {string} resourceType - a resource type (for DynamoDB stream eventSourceARN's this contains "table")
* @property {string} resource - a resource name (for DynamoDB stream eventSourceARN's this is the table name)
* @property {string} subResourceType - a sub-resource type (for DynamoDB stream eventSourceARN's this contains "stream")
* @property {string} subResource - a sub-resource name (for DynamoDB stream eventSourceARN's this is the stream timestamp)
* @property {string} aliasOrVersion - a Lambda alias or version number
* @property {string[]} others - any other components after a Lambda alias or version number
*/
/**
* @typedef {Object} AWSEvent - represents an AWS event typically passed to your Lambda handler function
* @see KinesisEvent
* @see DynamoDBEvent
* @see S3Event
* @see SESEvent
* @see SNSEvent
*/
/**
* @typedef {KinesisEvent|DynamoDBEvent|S3Event|SESEvent|SNSEvent} AnyAWSEvent - represents any AWS event (currently supported)
*/
/**
* @typedef {AWSEvent} StreamEvent - represents an AWS stream event
* @property {StreamEventRecord[]} Records - the records of the AWS stream event
* @see KinesisEvent
* @see DynamoDBEvent
*/
/**
* @typedef {KinesisEvent|DynamoDBEvent} AnyStreamEvent - represents an AWS Kinesis or DynamoDB stream event
/**
* @typedef {StreamEvent} KinesisEvent - represents an AWS Kinesis stream event
* @property {KinesisEventRecord[]} Records - the records of the AWS Kinesis stream event
*/
/**
* @typedef {StreamEvent} DynamoDBEvent - represents an AWS DynamoDB stream event
* @property {DynamoDBEventRecord[]} Records - the records of the AWS DynamoDB stream event
*/
/**
* @typedef {AWSEvent} S3Event - represents an AWS S3 (Simple Storage Service) event
* @property {S3EventRecord[]} Records - the records of the AWS S3 event
*/
/**
* @typedef {AWSEvent} SESEvent - represents an AWS SES (Simple Email Service) event
* @property {SESEventRecord[]} Records - the records of the AWS SES event
*/
/**
* @typedef {AWSEvent} SNSEvent - represents an AWS SNS (Simple Notification Service) event
* @property {string} EventSource - the event source of the AWS event record
* @property {SNSEventRecord[]} Records - the records of the AWS SNS event
*/
/**
* @typedef {Object} AWSEventRecord - represents an AWS event record
* @see KinesisEventRecord
* @see DynamoDBEventRecord
* @see S3EventRecord
* @see SESEventRecord
* @see SNSEventRecord
*/
/**
* @typedef {KinesisEventRecord|DynamoDBEventRecord|S3EventRecord|SESEventRecord|SNSEventRecord} AnyAWSEventRecord - represents any AWS event record (currently supported)
*/
/**
* @typedef {AWSEventRecord} StreamEventRecord - represents an AWS stream event record
* @property {string} eventID - the event ID, which should uniquely identify the record
* @property {string} eventSource - the event source, which should be either 'aws:kinesis' or 'aws:dynamodb'
* @property {string} eventSourceARN - the event source ARN (Amazon Resource Number), which identifies the event source stream or table
* @property {string} eventVersion - the version of the event
* @property {string} awsRegion - the AWS region in which the event took place
* @property {string} eventName - the "name" of the event - for Kinesis this will be 'aws:kinesis:record'; for DynamoDB this will be 'INSERT', 'MODIFY' or 'REMOVE'
* @see KinesisEventRecord
* @see DynamoDBEventRecord
*/
/**
* @typedef {KinesisEventRecord|DynamoDBEventRecord} AnyStreamEventRecord - represents any AWS stream event record (currently supported)
*/
/**
* @typedef {StreamEventRecord} KinesisEventRecord - represents an AWS Kinesis stream event record
* @property {KinesisProperty} kinesis - the kinesis property contains the data and details of the Kinesis event record
* @property {string} invokeIdentityArn - the invoke identity ARN (Amazon Resource Number)
* @see KinesisEvent
*/
/**
* @typedef {Object} KinesisProperty - represents the kinesis property of an AWS Kinesis stream event record
* @property {string} partitionKey - the partition key of the Kinesis event record
* @property {string} data - the actual data of a Kinesis event record in base 64 format
* @property {string} sequenceNumber - the sequence number of the Kinesis event record
* @property {string} kinesisSchemaVersion - the schema version of the Kinesis event record
*/
/**
* @typedef {StreamEventRecord} DynamoDBEventRecord - represents an AWS DynamoDB stream event record
* @property {DynamodbProperty} dynamodb - the dynamodb property contains the details of the DynamoDB record that was inserted, modified or removed
* @see DynamoDBEvent
*/
/**
* @typedef {Object} DynamodbProperty - represents the dynamodb property of an AWS DynamoDB stream event record
* @property {Object} Keys - the keys of the DynamoDB record (in DynamoDB attribute value format)
* @property {Object} [NewImage] - the new image of the DynamoDB record (in DynamoDB attribute value format)
* @property {Object} [OldImage] - the old image of the DynamoDB record (in DynamoDB attribute value format)
* @property {string} SequenceNumber - the sequence number of the event
* @property {string} SizeBytes - the size of the event in bytes
* @property {string} StreamViewType - the type of stream view, which defines whether NewImage and OldImage should be
* present or not, and which should be 'KEYS_ONLY', 'NEW_IMAGE', 'OLD_IMAGE' or 'NEW_AND_OLD_IMAGES'
*/
/**
* @typedef {AWSEventRecord} S3EventRecord - represents an AWS S3 event record
* @property {string} eventSource - the event source of the AWS S3 event record
* @see S3Event
*/
/**
* @typedef {AWSEventRecord} SESEventRecord - represents an AWS SES event record
* @property {string} eventSource - the event source of the AWS SES event record
* @see SESEvent
*/
/**
* @typedef {AWSEventRecord} SNSEventRecord - represents an AWS SNS event record
* @property {string} EventSource - the event source of the AWS SNS event record
* @see SNSEvent
*/
/**
* @typedef {Object} DynamoDBUtilsDefaults - Defaults used by the dynamodb-utils module, which can be overridden to
* alter the default behaviour
* @property {string} emptyStringReplacement - a non-empty string to use as a replacement for empty strings, which
* cannot be stored to DynamoDB (defaults to ' ', i.e. a single space)
*/

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc