Skip to content

Instantly share code, notes, and snippets.

@medikoo
Last active October 30, 2017 13:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save medikoo/58e555c9477f859b72a7c24eaedcee56 to your computer and use it in GitHub Desktop.
Save medikoo/58e555c9477f859b72a7c24eaedcee56 to your computer and use it in GitHub Desktop.
Serverless: Nested stacks per function
'use strict';
const memoize = require('memoizee');
const namingUtils = require('serverless/lib/plugins/aws/lib/naming');
const getApiGatewayResourceMap = memoize(serverless => {
// AwsCompileApigEvents plugin provides access to data maps and methods
// that allow to easily map generated resources to lambdas
const apiGatewayPlugin = serverless.pluginManager.plugins.find(
plugin => plugin.constructor.name === 'AwsCompileApigEvents'
);
// Result map: resource id to normalized function name
const resourceMap = new Map();
// Temporary map that helps to detect how many functions depend on given AWS::ApiGateway::Resource
// resources. It can be the case that more than one function depends on one resouce, in such case
// we keep resource in the main stack
const gatewayResourceLambdaMap = new Map();
// Iterate over all configured HTTP endpoints
apiGatewayPlugin.validated.events.map(({ functionName, http }) => {
// Normalized function name makes part of resource logical id
const normalizedLambdaName = namingUtils.getNormalizedFunctionName(functionName);
// AWS::ApiGateway::Method can be deducted directly as it's always mapped to single function
resourceMap.set(
namingUtils.getMethodLogicalId(apiGatewayPlugin.getResourceName(http.path), http.method),
normalizedLambdaName
);
// Collect information about all AWS::ApiGateway::Resource resources that are needed for this endpoint
const tokens = [];
http.path.split('/').forEach(token => {
tokens.push(token);
const resourceName = namingUtils.getResourceLogicalId(tokens.join('/'));
if (!gatewayResourceLambdaMap.has(resourceName)) {
gatewayResourceLambdaMap.set(resourceName, new Set());
}
gatewayResourceLambdaMap.get(resourceName).add(normalizedLambdaName);
});
});
// Resolve all AWS::ApiGateway::Resource that map single function, only those will be moved to
// nested per lambda distributed stacks
gatewayResourceLambdaMap.forEach((normalizedFunctionNames, resourceName) => {
if (normalizedFunctionNames.size > 1) return;
resourceMap.set(resourceName, normalizedFunctionNames.values().next().value);
});
return resourceMap;
});
const getAllNormalizedLambdaNames = memoize(serverless =>
Object.keys(serverless.service.functions)
.map(lambdaName => namingUtils.getNormalizedFunctionName(lambdaName))
.sort((normalizedName1, normalizedName2) => normalizedName2.length - normalizedName1.length)
);
require('serverless-plugin-split-stacks').resolveMigration = (resource, logicalId, serverless) => {
let destination;
switch (resource.Type) {
case 'AWS::ApiGateway::Method':
case 'AWS::ApiGateway::Resource':
destination = getApiGatewayResourceMap(serverless).get(logicalId);
break;
default:
// All other resource types if their name starts with one of the lambda names
// are propagated to given lambda stackxv
// (it's the Serverless internal convention to prefix most lambda specific resources
// with normalized lambda name)
destination = getAllNormalizedLambdaNames(serverless).find(normalizedLambdaName =>
logicalId.startsWith(normalizedLambdaName)
);
}
return destination ? { destination } : null;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment