Socket
Socket
Sign inDemoInstall

middy

Package Overview
Dependencies
Maintainers
8
Versions
147
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

middy - npm Package Compare versions

Comparing version 0.11.2 to 0.12.0

4

middlewares.d.ts

@@ -50,5 +50,7 @@ import { SSM } from 'aws-sdk'

cache?: boolean;
params: { [key: string]: string; };
paths?: { [key: string]: string; };
names?: { [key: string]: string; };
awsSdkOptions?: Partial<SSM.Types.ClientConfiguration>;
setToContext?: boolean;
getParamNameFromPath?: (path: string, name: string, prefix: string) => string;
}

@@ -55,0 +57,0 @@

{
"name": "middy",
"version": "0.11.2",
"version": "0.12.0",
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda",

@@ -5,0 +5,0 @@ "main": "./index.js",

@@ -505,3 +505,3 @@ <div align="center">

- [`httpPartialResponse`](/docs/middlewares.md#httppartialresponse): Filter response objects attributes based on query string parameters.
- [`jsonBodyParser`](/docs/middlewares.md#jsonbodyparser): automatically parses HTTP requests with JSON body and converts the body into an object. Also handles gracefully broken JSON if used in combination of
- [`jsonBodyParser`](/docs/middlewares.md#jsonbodyparser): Automatically parses HTTP requests with JSON body and converts the body into an object. Also handles gracefully broken JSON if used in combination of
`httpErrorHanler`.

@@ -597,5 +597,3 @@ - [`s3KeyNormalizer`](/docs/middlewares.md#s3keynormalizer): Normalizes key names in s3 events.

**Kind**: global typedef
**Returns**: <code>void</code> \| <code>Promise</code> - - A middleware can return a Promise instead of using the `next` function as a callback.
In this case middy will wait for the promise to resolve (or reject) and it will automatically
propagate the result to the next middleware.
**Returns**: <code>void</code> \| <code>Promise</code> - - A middleware can return a Promise instead of using the `next` function as a callback. In this case middy will wait for the promise to resolve (or reject) and it will automatically propagate the result to the next middleware.

@@ -602,0 +600,0 @@ | Param | Type | Description |

@@ -10,2 +10,4 @@ jest.mock('aws-sdk')

SSM.prototype.getParameters = getParametersMock
const getParametersByPathMock = jest.fn()
SSM.prototype.getParametersByPath = getParametersByPathMock

@@ -15,3 +17,7 @@ beforeEach(() => {

getParametersMock.mockClear()
getParametersByPathMock.mockReset()
getParametersByPathMock.mockClear()
delete process.env.MONGO_URL
delete process.env.OTHER_MONGO_URL
delete process.env.SERVICE_NAME_MONGO_URL
})

@@ -24,2 +30,6 @@

getParametersByPathMock.mockReturnValue({
promise: () => Promise.resolve(ssmMockResponse)
})
const handler = middy((event, context, cb) => {

@@ -42,3 +52,3 @@ cb()

middlewareOptions: {
params: {
names: {
MONGO_URL: '/dev/service_name/mongo_url'

@@ -63,3 +73,3 @@ }

middlewareOptions: {
params: {
names: {
MONGO_URL: '/dev/service_name/mongo_url'

@@ -86,3 +96,3 @@ },

middlewareOptions: {
params: {
names: {
secureValue: '/dev/service_name/secure_param'

@@ -106,7 +116,8 @@ },

middlewareOptions: {
params: {
names: {
secureValue: '/dev/service_name/secure_param'
},
cache: true,
setToContext: true
setToContext: true,
paramsLoaded: false
},

@@ -126,3 +137,3 @@ cb () {

middlewareOptions: {
params: {
names: {
secureValue: '/dev/service_name/secure_param'

@@ -145,3 +156,3 @@ },

middlewareOptions: {
params: {
names: {
invalidParam: 'invalid-smm-param-name',

@@ -168,2 +179,33 @@ anotherInvalidParam: 'another-invalid-ssm-param'

})
test('It should set properties on target with names equal to full parameter name sans specified path', (done) => {
testScenario({
ssmMockResponse: {
Parameters: [{Name: '/dev/service_name/mongo_url', Value: 'my-mongo-url'}]
},
middlewareOptions: {
paths: {'': '/dev/service_name'}
},
cb () {
expect(process.env.MONGO_URL).toEqual('my-mongo-url')
done()
}
})
})
test('It should retrieve params from multiple paths', (done) => {
testScenario({
ssmMockResponse: {
Parameters: [{Name: '/dev/service_name/mongo_url', Value: 'my-mongo-url'}]
},
middlewareOptions: {
paths: {'': ['/dev/service_name'], 'prefix': '/dev'}
},
cb () {
expect(process.env.MONGO_URL).toEqual('my-mongo-url')
expect(process.env.PREFIX_SERVICE_NAME_MONGO_URL).toEqual('my-mongo-url')
done()
}
})
})
})

@@ -0,12 +1,16 @@

let paramsLoaded = false
let ssmInstance
module.exports = (opts) => {
module.exports = opts => {
const defaults = {
awsSdkOptions: {
maxRetries: 6, // lowers a chance to hit service rate limits, default is 3
retryDelayOptions: {base: 200}
retryDelayOptions: { base: 200 }
},
params: {},
paths: {},
names: {},
getParamNameFromPath: getParamNameFromPathDefault,
setToContext: false,
cache: false
cache: false,
paramsLoaded: paramsLoaded
}

@@ -16,50 +20,66 @@

return ({
before (handler, next) {
const targetParamsObject = getTargetObjectToAssign(handler, options)
const stillCached = areParamsStillCached(options, targetParamsObject)
return {
before: (handler, next) => {
if (options.cache && options.paramsLoaded) return next()
if (stillCached) {
return next()
ssmInstance = ssmInstance || getSSMInstance(options.awsSdkOptions)
const ssmPromises = Object.keys(options.paths).reduce((aggregator, prefix) => {
const pathsData = options.paths[prefix]
const paths = Array.isArray(pathsData) ? pathsData : [pathsData]
return paths.reduce((subAggregator, path) => {
subAggregator.push(
ssmInstance
.getParametersByPath({ Path: path, Recursive: true, WithDecryption: true })
.promise()
.then(handleInvalidParams)
.then(ssmResponse => getParamsToAssignByPath(path, ssmResponse, prefix, options.getParamNameFromPath))
)
return subAggregator
}, aggregator)
}, [])
const ssmParamNames = getSSMParamValues(options.names)
if (ssmParamNames.length) {
ssmPromises.push(
ssmInstance
.getParameters({ Names: ssmParamNames, WithDecryption: true })
.promise()
.then(handleInvalidParams)
.then(ssmResponse => getParamsToAssignByName(options.names, ssmResponse))
)
}
const ssmParamNames = getSSMParamNames(options.params)
lazilyLoadSSMInstance(options.awsSdkOptions)
return getSSMParams(ssmParamNames)
.then(ssmResponse => {
assignSSMParamsToTarget(targetParamsObject, options.params, ssmResponse)
return Promise.all(ssmPromises).then(objectsToMap => {
const targetParamsObject = getTargetObjectToAssign(handler, options)
objectsToMap.forEach(object => {
Object.assign(targetParamsObject, object)
})
paramsLoaded = true
})
}
})
}
function getTargetObjectToAssign (handler, options) {
if (options.setToContext) {
return handler.context
}
return process.env
}
function areParamsStillCached (options, targetParamsObject) {
if (!options.cache) {
return false
}
// returns full parameter name sans the path as specified, with slashes replaced with underscores and any prefix applied
// everything gets upper cased
// e.g. if path is '/dev/myApi/', the parameter '/dev/myApi/connString/default' will be returned with the name 'CONNSTRING_DEFAULT'
// see: https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-su-organize.html
const getParamNameFromPathDefault = (path, name, prefix) => {
const localName = name
.split(`${path}/`)
.join(``) // replace path
.split(`/`)
.join(`_`) // replace remaining slashes with underscores
for (const userParamsName in options.params) {
if (options.params.hasOwnProperty(userParamsName)) {
if (typeof targetParamsObject[userParamsName] === 'undefined') {
return false
}
}
}
const fullLocalName = prefix ? `${prefix}_${localName}` : localName
return true
return fullLocalName.toUpperCase()
}
function getSSMParamNames (userParamsMap) {
return Object.keys(userParamsMap).map(key => userParamsMap[key])
}
const getTargetObjectToAssign = (handler, options) => (options.setToContext ? handler.context : process.env)
const getSSMParamValues = userParamsMap => Object.keys(userParamsMap).map(key => userParamsMap[key])
/**

@@ -72,49 +92,39 @@ * Lazily load aws-sdk and initialize SSM constructor

*/
function lazilyLoadSSMInstance (awsSdkOptions) {
const getSSMInstance = awsSdkOptions => {
// lazy load aws-sdk and SSM constructor to avoid performance
// penalties if you don't use this middleware
if (ssmInstance) {
return ssmInstance
}
// AWS Lambda has aws-sdk included version 2.176.0
// see https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html
const {SSM} = require('aws-sdk')
ssmInstance = new SSM(awsSdkOptions)
const { SSM } = require('aws-sdk')
return new SSM(awsSdkOptions)
}
/**
* Get array of SSM params using aws-sdk
* Throw error if SSM returns an error because we asked for params that don't exist
* @throws {Error} When any invalid parameters found in response
* @param {String[]} ssmParamNames Array of param names to fetch
* @param {Function} getter Function that returns a promise which resolves with the params returned from ssm
* @return {Promise.<Object[]>} Array of SSM params from aws-sdk
*/
function getSSMParams (ssmParamNames) {
// prevents throwing error from aws-sdk when empty params passed
if (!ssmParamNames.length) {
return Promise.resolve([])
const handleInvalidParams = ({ Parameters, InvalidParameters }) => {
if (InvalidParameters && InvalidParameters.length) {
throw new Error(`InvalidParameters present: ${InvalidParameters.join(', ')}`)
}
return ssmInstance.getParameters({Names: ssmParamNames, WithDecryption: true})
.promise()
.then(({Parameters, InvalidParameters}) => {
if (InvalidParameters && InvalidParameters.length) {
throw new Error(`InvalidParameters present: ${InvalidParameters.join(', ')}`)
}
return Parameters
})
return Parameters
}
/**
* Assigns params from SSM response to target object using names from middleware options
* @param {Object} paramsTarget Target object to assign params to
* @param {Object} userParamsMap Options from middleware defining param names
* @param {Object[]} ssmResponse Array of params returned from SSM by aws-sdk
* Get object of user param names as keys and SSM param values as value
* @param {Object} userParamsMap Params object from middleware options
* @param {Object[]} ssmParams Array of parameters from SSM returned by aws-sdk
* @return {Object} Merged object for assignment to target object
*/
function assignSSMParamsToTarget (paramsTarget, userParamsMap, ssmResponse) {
const paramsToAttach = getParamsToAssign(userParamsMap, ssmResponse)
const getParamsToAssignByName = (userParamsMap, ssmParams) => {
const ssmToUserParamsMap = invertObject(userParamsMap)
Object.assign(paramsTarget, paramsToAttach)
return ssmParams.reduce((aggregator, ssmParam) => {
aggregator[ssmToUserParamsMap[ssmParam.Name]] = ssmParam.Value
return aggregator
}, {})
}

@@ -124,29 +134,18 @@

* Get object of user param names as keys and SSM param values as value
* @param {Object} userParamsMap Params object from middleware options
* @param {String} userParamsPath Path string from middleware options
* @param {Object[]} ssmParams Array of parameters from SSM returned by aws-sdk
* @param {String} prefix String to prefix to param values from a given path
* @param {Function} nameMapper function to build the local name for a param based on path, prefix, and name in SSM
* @return {Object} Merged object for assignment to target object
*/
function getParamsToAssign (userParamsMap, ssmParams) {
const ssmToUserParamsMap = invertObject(userParamsMap)
const targetObject = {}
const getParamsToAssignByPath = (userParamsPath, ssmParams, prefix, nameMapper) =>
ssmParams.reduce((aggregator, ssmParam) => {
aggregator[nameMapper(userParamsPath, ssmParam.Name, prefix)] = ssmParam.Value
return aggregator
}, {})
for (let {Name: ssmParamName, Value: ssmParamValue} of ssmParams) {
const userParamName = ssmToUserParamsMap[ssmParamName]
targetObject[userParamName] = ssmParamValue
}
return targetObject
}
function invertObject (obj) {
const invertedObject = {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
invertedObject[obj[key]] = key
}
}
return invertedObject
}
const invertObject = obj =>
Object.keys(obj).reduce((aggregator, key) => {
aggregator[obj[key]] = key
return aggregator
}, {})
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