serverless-spy
Version:
CDK-based library for writing elegant integration tests on AWS serverless architecture and an additional web console to monitor events in real time.
646 lines • 97 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServerlessSpy = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const fs = require("fs");
const path = require("path");
const aws_lambda_python_alpha_1 = require("@aws-cdk/aws-lambda-python-alpha");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const dynamoDb = require("aws-cdk-lib/aws-dynamodb");
const events = require("aws-cdk-lib/aws-events");
const targets = require("aws-cdk-lib/aws-events-targets");
const aws_iam_1 = require("aws-cdk-lib/aws-iam");
const lambda = require("aws-cdk-lib/aws-lambda");
const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
const dynamoDbStream = require("aws-cdk-lib/aws-lambda-event-sources");
const aws_lambda_event_sources_1 = require("aws-cdk-lib/aws-lambda-event-sources");
const lambdaNode = require("aws-cdk-lib/aws-lambda-nodejs");
const aws_lambda_nodejs_1 = require("aws-cdk-lib/aws-lambda-nodejs");
const s3 = require("aws-cdk-lib/aws-s3");
const s3notif = require("aws-cdk-lib/aws-s3-notifications");
const sns = require("aws-cdk-lib/aws-sns");
const snsSubs = require("aws-cdk-lib/aws-sns-subscriptions");
const sqs = require("aws-cdk-lib/aws-sqs");
const constructs_1 = require("constructs");
const envVariableNames_1 = require("./common/envVariableNames");
const isLambdaFunction = (node) => 'functionName' in node && 'functionArn' in node && 'runtime' in node;
const serverlessSpyIotEndpointCrNamePrefix = 'ServerlessSpyIotEndpoint';
class ServerlessSpy extends constructs_1.Construct {
constructor(scope, id, props) {
super(scope, id);
this.props = props;
this.createdResourcesBySSpy = [];
this.lambdaSubscriptionPool = [];
this.lambdasSpied = [];
this.serviceKeys = [];
this.spiedNodes = [];
this.layerMap = {};
const rootStack = this.cleanName(this.findRootStack(aws_cdk_lib_1.Stack.of(this)).node.id);
const getIoTEndpoint = new aws_cdk_lib_1.custom_resources.AwsCustomResource(this, serverlessSpyIotEndpointCrNamePrefix, {
onCreate: {
service: 'Iot',
action: 'describeEndpoint',
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('endpointAddress'),
parameters: {
endpointType: 'iot:Data-ATS',
},
},
onUpdate: {
service: 'Iot',
action: 'describeEndpoint',
physicalResourceId: aws_cdk_lib_1.custom_resources.PhysicalResourceId.fromResponse('endpointAddress'),
parameters: {
endpointType: 'iot:Data-ATS',
},
},
installLatestAwsSdk: false,
policy: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
resources: aws_cdk_lib_1.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
}),
functionName: serverlessSpyIotEndpointCrNamePrefix + rootStack,
});
this.iotEndpoint = getIoTEndpoint.getResponseField('endpointAddress');
this.createdResourcesBySSpy.push(getIoTEndpoint);
new aws_cdk_lib_1.CfnOutput(this, 'ServerlessSpyIoTEndpoint', {
key: 'ServerlessSpyWsUrl',
value: `${this.iotEndpoint}/${rootStack}`,
});
this.lambdaSubscriptionMain = this.provideFunctionForSubscription();
}
getDefaultLambdaEnvironmentVariables() {
return {
NODE_OPTIONS: '--enable-source-maps',
};
}
/**
* Initalize spying on resources given as parameter.
* @param nodes Which reources and their children to spy on.
*/
spyNodes(nodes) {
for (const node of nodes) {
let ns = this.getAllNodes(node);
this.internalSpyNodes(ns);
}
this.finalizeSpy();
}
/**
* Initalize spying on resources.
* @param filter Limit which resources to spy on.
*/
spy(filter) {
let nodes = this.getAllNodes(aws_cdk_lib_1.Stack.of(this));
const filterWithDefaults = {
spyLambda: true,
spySqs: true,
spySnsTopic: true,
spySnsSubsription: true,
spyEventBridge: true,
spyEventBridgeRule: true,
spyS3: true,
spyDynamoDB: true,
...filter,
};
const CRID = 'AWS' +
aws_cdk_lib_1.custom_resources.AwsCustomResource.PROVIDER_FUNCTION_UUID.replace(/-/gi, '').substring(0, 16);
nodes = nodes.filter((node) => {
if (
// Ignore the custom resource and the Provider (as well as any other Providers using the same provider function), otherwise we cause
// circular dependencies
node.node.id.startsWith(CRID) ||
node.node.id === 'Provider' ||
// Ignore singleton functions as they can cause very odd behavior and crashes
node instanceof aws_lambda_1.SingletonFunction) {
if (this.props?.debugMode) {
console.info(`Skipping ${node.node.id}`);
}
return false;
}
else if (filterWithDefaults.spyLambda &&
(node instanceof lambda.Function ||
node instanceof aws_lambda_nodejs_1.NodejsFunction ||
isLambdaFunction(node))) {
return true;
}
else if (filterWithDefaults.spySnsTopic && node instanceof sns.Topic) {
return true;
}
else if (filterWithDefaults.spySnsSubsription &&
node instanceof sns.Subscription) {
return true;
}
else if (filterWithDefaults.spyS3 && node instanceof s3.Bucket) {
return true;
}
else if (filterWithDefaults.spyDynamoDB &&
node instanceof dynamoDb.Table) {
return true;
}
else if (filterWithDefaults.spyDynamoDB &&
node instanceof dynamoDb.TableV2) {
return true;
}
else if (filterWithDefaults.spyEventBridge &&
node instanceof events.EventBus) {
return true;
}
else if (filterWithDefaults.spyEventBridgeRule &&
node instanceof events.Rule) {
return true;
}
else if (filterWithDefaults.spySqs &&
node instanceof lambda.CfnEventSourceMapping) {
return true;
}
else if (filterWithDefaults.spySqs &&
this.props?.spySqsWithNoSubscriptionAndDropAllMessages &&
node instanceof sqs.Queue) {
return true;
}
return false;
});
this.internalSpyNodes(nodes);
this.finalizeSpy();
}
internalSpyNodes(nodes) {
for (const node of nodes) {
this.internalSpyNode(node);
}
}
finalizeSpy() {
//set mapping property for all functions we created
for (const func of this.lambdaSubscriptionPool) {
func.function.addEnvironment(envVariableNames_1.envVariableNames.SSPY_INFRA_MAPPING, JSON.stringify(func.mapping));
}
//set mapping property for all functions we spy on
for (const func of this.lambdasSpied) {
func.function.addEnvironment(envVariableNames_1.envVariableNames.SSPY_INFRA_MAPPING, JSON.stringify(func.mapping));
}
if (this.props?.generateSpyEventsFileLocation) {
this.writeSpyEventsClass(this.props?.generateSpyEventsFileLocation);
}
}
getExtensionAssetLocation() {
let extensionAssetLocation = path.join(__dirname, '../extension/dist/layer');
const extensionAssetLocationAlt = path.join(__dirname, '../lib/extension/dist/layer');
if (!fs.existsSync(extensionAssetLocation)) {
if (!fs.existsSync(extensionAssetLocationAlt)) {
throw new Error(`Folder with assets for extension does not exists at ${extensionAssetLocation} or at ${extensionAssetLocationAlt} `);
}
else {
extensionAssetLocation = extensionAssetLocationAlt;
}
}
const extensionAssetLocationWrapper = path.join(extensionAssetLocation, 'spy-wrapper');
if (!fs.existsSync(extensionAssetLocationWrapper)) {
throw new Error(`Wrapper script for extension does not exists ${extensionAssetLocation}`);
}
const extensionAssetLocationCode = path.join(extensionAssetLocation, `nodejs/node_modules/interceptor.js`);
if (!fs.existsSync(extensionAssetLocationCode)) {
throw new Error(`Code for extension does not exists ${extensionAssetLocationCode}`);
}
return extensionAssetLocation;
}
getLanguageExtensionAssetLocation(language) {
const rootDir = path.join(__dirname, '..');
let extensionAssetLocation = path.join(rootDir, `extensions/${language}`);
const extensionAssetLocationAlt = path.join(rootDir, `lib/extensions/${language}`);
if (!fs.existsSync(extensionAssetLocation)) {
if (!fs.existsSync(extensionAssetLocationAlt)) {
throw new Error(`Folder with assets for extension for ${language} does not exists at ${extensionAssetLocation} or at ${extensionAssetLocationAlt} `);
}
else {
extensionAssetLocation = extensionAssetLocationAlt;
}
}
const extensionAssetLocationWrapper = path.join(
// extensionAssetLocation.substring(
// 0,
// extensionAssetLocation.lastIndexOf(path.sep)
// ),
extensionAssetLocation, 'spy-wrapper');
if (!fs.existsSync(extensionAssetLocationWrapper)) {
throw new Error(`Wrapper script for extension does not exists at ${extensionAssetLocationWrapper}`);
}
return extensionAssetLocation;
}
/**
* Write SpyEvents class, which helps with writing the code for tests.
* @param fileLocation
*/
writeSpyEventsClass(fileLocation) {
fs.mkdirSync(path.dirname(fileLocation), { recursive: true });
const properties = this.serviceKeys
.map((sk) => ` ${sk.replace(/#/g, '')}: '${sk}' = '${sk}';\n`)
.join('');
const code = `/* eslint-disable */\nexport class ServerlessSpyEvents {\n${properties}}\n`;
fs.writeFileSync(fileLocation, code);
}
getAllNodes(parent) {
const nodes = [];
nodes.push(parent);
this.getAllNodesRecursive(parent, nodes);
return nodes;
}
getAllNodesRecursive(parent, nodes) {
for (const node of parent.node.children) {
nodes.push(node);
this.getAllNodesRecursive(node, nodes);
}
}
internalSpyNode(node) {
if (this.spiedNodes.includes(node)) {
return;
}
this.spiedNodes.push(node);
if (this.createdResourcesBySSpy.includes(node)) {
return;
}
if (this.lambdaSubscriptionPool.find((s) => s.function === node)) {
return;
}
if (this.props?.debugMode) {
console.info('Spy on node', this.getConstructName(node));
}
if (node instanceof lambda.Function ||
node instanceof aws_lambda_nodejs_1.NodejsFunction ||
isLambdaFunction(node)) {
this.internalSpyLambda(node);
}
else if (node instanceof sns.Topic) {
this.internalSpySnsTopic(node);
}
else if (node instanceof sns.Subscription) {
this.internalSpySnsSubscription(node);
}
else if (node instanceof s3.Bucket) {
this.internalSpyS3(node);
}
else if (node instanceof dynamoDb.Table) {
this.internalSpyDynamodb(node);
}
else if (node instanceof dynamoDb.TableV2) {
this.internalSpyDynamodb(node);
}
else if (node instanceof events.EventBus) {
this.internalSpyEventBus(node);
}
else if (node instanceof events.Rule) {
this.internalSpyEventBusRule(node);
}
else if (node instanceof lambda.CfnEventSourceMapping) {
this.internalSpySqs(node);
}
else if (node instanceof sqs.Queue) {
if (this.props?.spySqsWithNoSubscriptionAndDropAllMessages) {
this.internalSpySpySqsWithNoSubscription(node);
}
}
}
getExtensionForRuntime(runtime, architecture) {
const layerKey = (r, a) => `${r.toString()}_${a.name.toString()}`;
let layer = this.layerMap[layerKey(runtime, architecture)];
let spyWrapperPath = '/opt/spy-wrapper';
switch (runtime.name) {
case lambda.Runtime.PYTHON_3_8.name:
case lambda.Runtime.PYTHON_3_9.name:
case lambda.Runtime.PYTHON_3_10.name:
case lambda.Runtime.PYTHON_3_11.name:
case lambda.Runtime.PYTHON_3_12.name:
const location = this.getLanguageExtensionAssetLocation('python');
spyWrapperPath = '/opt/python/spy-wrapper';
layer =
layer ||
new aws_lambda_python_alpha_1.PythonLayerVersion(this, `PythonExtension${runtime.name
.replace('python', '')
.replace('.', '_')}`, {
compatibleRuntimes: [runtime],
compatibleArchitectures: [architecture],
entry: location,
bundling: {
bundlingFileAccess: aws_cdk_lib_1.BundlingFileAccess.VOLUME_COPY,
// command: [
// `cp ${path.join(
// location.substring(0, location.lastIndexOf(path.sep)),
// 'spy-wrapper/spy-wrapper'
// )} /asset-output/python`,
// ],
},
});
break;
case lambda.Runtime.NODEJS_12_X.name:
case lambda.Runtime.NODEJS_14_X.name:
case lambda.Runtime.NODEJS_16_X.name:
case lambda.Runtime.NODEJS_18_X.name:
case lambda.Runtime.NODEJS_20_X.name:
case lambda.Runtime.NODEJS_22_X.name:
layer =
layer ||
new lambda.LayerVersion(this, `NodeExtension${runtime.name
.replace('node', '')
.replace('.', '_')}`, {
compatibleRuntimes: [
lambda.Runtime.NODEJS_12_X,
lambda.Runtime.NODEJS_14_X,
lambda.Runtime.NODEJS_16_X,
lambda.Runtime.NODEJS_18_X,
lambda.Runtime.NODEJS_20_X,
lambda.Runtime.NODEJS_22_X,
],
compatibleArchitectures: [architecture],
code: lambda.Code.fromAsset(this.getExtensionAssetLocation()),
});
break;
default:
console.log(`No extensions available for ${runtime.toString()}`);
return undefined;
}
for (const compatibleRuntime of layer.compatibleRuntimes) {
this.layerMap[layerKey(compatibleRuntime, architecture)] = layer;
}
this.createdResourcesBySSpy.push(layer);
return { layer, spyWrapperPath };
}
internalSpySpySqsWithNoSubscription(queue) {
const subscription = this.findElement((n) => n instanceof lambda.CfnEventSourceMapping &&
n.eventSourceArn === queue.queueArn);
if (subscription) {
return; //already have subscription
}
const queueName = this.getConstructName(queue);
const func = new aws_lambda_nodejs_1.NodejsFunction(this, `${queueName}SqsSubscriptionAndDropAllMessages`, {
memorySize: 512,
timeout: aws_cdk_lib_1.Duration.seconds(5),
runtime: lambda.Runtime.NODEJS_22_X,
handler: 'handler',
entry: this.getAssetLocation('functions/sqsSubscriptionAndDropAllMessages.js'),
environment: this.getDefaultLambdaEnvironmentVariables(),
});
func.addEventSource(new aws_lambda_event_sources_1.SqsEventSource(queue));
this.setupForIoT(func);
const { layer, spyWrapperPath } = this.getExtensionForRuntime(func.runtime, func.architecture);
func.addLayers(layer);
func.addEnvironment('AWS_LAMBDA_EXEC_WRAPPER', spyWrapperPath);
if (this.props?.debugMode) {
func.addEnvironment(envVariableNames_1.envVariableNames.SSPY_DEBUG, 'true');
}
this.createdResourcesBySSpy.push(func);
const serviceKey = `Sqs#${queueName}`;
this.addMappingToFunction(func, {
key: queue.queueArn,
value: serviceKey,
});
this.serviceKeys.push(serviceKey);
func.addEnvironment(envVariableNames_1.envVariableNames.SSPY_SUBSCRIBED_TO_SQS, 'true');
}
internalSpySqs(node) {
const queue = this.findElement((n) => n instanceof sqs.Queue &&
n.queueArn === node.eventSourceArn);
const func = this.findElement((n) => n instanceof lambda.Function &&
n.functionName === node.functionName);
if (queue && func) {
const queueName = this.getConstructName(queue);
const serviceKey = `Sqs#${queueName}`;
this.addMappingToFunction(func, {
key: queue.queueArn,
value: serviceKey,
});
this.serviceKeys.push(serviceKey);
func.addEnvironment(envVariableNames_1.envVariableNames.SSPY_SUBSCRIBED_TO_SQS, 'true');
}
}
createFunctionForSubscription(index) {
const func = new lambdaNode.NodejsFunction(this, `Subscription${index}`, {
memorySize: 512,
timeout: aws_cdk_lib_1.Duration.seconds(5),
runtime: lambda.Runtime.NODEJS_22_X,
handler: 'handler',
entry: this.getAssetLocation('functions/sendMessage.js'),
environment: {
NODE_OPTIONS: '--enable-source-maps',
},
});
this.setupForIoT(func);
return func;
}
internalSpyS3(s3Bucket) {
s3Bucket.addEventNotification(s3.EventType.OBJECT_CREATED_PUT, new s3notif.LambdaDestination(this.lambdaSubscriptionMain.function));
const name = this.getConstructName(s3Bucket);
const serviceKey = `S3#${name}`;
this.lambdaSubscriptionMain.mapping[s3Bucket.bucketArn] = serviceKey;
this.serviceKeys.push(serviceKey);
}
internalSpyDynamodb(table) {
// enable DynamoDB streams with a hack
table.node.defaultChild.streamSpecification = {
streamViewType: dynamoDb.StreamViewType.NEW_AND_OLD_IMAGES,
};
table.tableStreamArn = table.node.defaultChild.attrStreamArn;
this.lambdaSubscriptionMain.function.addEventSource(new dynamoDbStream.DynamoEventSource(table, {
startingPosition: lambda.StartingPosition.LATEST,
batchSize: 1,
retryAttempts: 0,
}));
const name = this.getConstructName(table);
const serviceKey = `DynamoDB#${name}`;
this.lambdaSubscriptionMain.mapping[table.tableArn] = serviceKey;
this.serviceKeys.push(serviceKey);
}
internalSpyEventBusRule(rule) {
const { eventBusName } = rule.node.defaultChild;
let bridgeName = 'Default';
if (!!eventBusName) {
const eventBridge = this.getEventBridge(eventBusName);
if (!eventBridge) {
throw new Error(`Can not find EventBridge with name "${eventBusName}"`);
}
bridgeName = this.getConstructName(eventBridge);
}
const functionSubscription = this.provideFunctionForSubscription((s) => !s.usedForEventBridge);
functionSubscription.usedForEventBridge = true;
rule.addTarget(new targets.LambdaFunction(functionSubscription.function));
const ruleName = this.getConstructName(rule);
const serviceKey = `EventBridgeRule#${bridgeName}#${ruleName}`;
functionSubscription.mapping.eventBridge = serviceKey;
this.serviceKeys.push(serviceKey);
}
internalSpyEventBus(eventBus) {
const functionSubscription = this.provideFunctionForSubscription((s) => !s.usedForEventBridge);
functionSubscription.usedForEventBridge = true;
const bridgeName = this.getConstructName(eventBus);
const rule = new events.Rule(this, `RuleAll${bridgeName}`, {
eventBus,
eventPattern: { version: ['0'] },
targets: [new targets.LambdaFunction(functionSubscription.function)],
});
this.createdResourcesBySSpy.push(rule);
const serviceKey = `EventBridge#${bridgeName}`;
functionSubscription.mapping.eventBridge = serviceKey;
this.serviceKeys.push(serviceKey);
}
internalSpySnsTopic(topic) {
const functionSubscription = this.provideFunctionForSubscription((s) => !s.subsribedTopics.includes(topic));
const subscription = topic.addSubscription(new snsSubs.LambdaSubscription(functionSubscription.function));
this.createdResourcesBySSpy.push(subscription);
const topicName = this.getConstructName(topic);
const serviceKey = `SnsTopic#${topicName}`;
functionSubscription.mapping[topic.topicArn] = serviceKey;
this.serviceKeys.push(serviceKey);
functionSubscription.subsribedTopics.push(topic);
}
internalSpySnsSubscription(subscription) {
if (!subscription.node.scope) {
return;
}
const topic = this.getTopic(subscription.node.defaultChild.topicArn);
if (!topic) {
throw new Error('Can not find Topic');
}
const functionSubscription = this.provideFunctionForSubscription((s) => !s.subsribedTopics.includes(topic));
const { filterPolicy } = subscription.node
.defaultChild;
const subscriptionClone = topic.addSubscription(new snsSubs.LambdaSubscription(functionSubscription.function));
subscriptionClone.node.defaultChild.filterPolicy =
filterPolicy;
this.createdResourcesBySSpy.push(subscriptionClone);
const topicName = this.getConstructName(topic);
const targetName = this.getConstructName(subscription.node.scope);
functionSubscription.subsribedTopics.push(topic);
const serviceKey = `SnsSubscription#${topicName}#${targetName}`;
functionSubscription.mapping[topic.topicArn] = serviceKey;
this.serviceKeys.push(serviceKey);
}
provideFunctionForSubscription(filterFunction) {
let functionSubscription;
if (filterFunction) {
functionSubscription = this.lambdaSubscriptionPool.find(filterFunction);
}
else if (this.lambdaSubscriptionPool.length > 0) {
functionSubscription = this.lambdaSubscriptionPool[0];
}
if (!functionSubscription) {
functionSubscription = {
subsribedTopics: [],
usedForEventBridge: false,
mapping: {},
function: this.createFunctionForSubscription(this.lambdaSubscriptionPool.length),
};
this.lambdaSubscriptionPool.push(functionSubscription);
}
return functionSubscription;
}
setupForIoT(func) {
func.addEnvironment(envVariableNames_1.envVariableNames.SSPY_ROOT_STACK, this.cleanName(this.findRootStack(aws_cdk_lib_1.Stack.of(this)).node.id));
func.addEnvironment(envVariableNames_1.envVariableNames.SSPY_IOT_ENDPOINT, this.iotEndpoint);
func.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
actions: ['iot:*'],
effect: aws_iam_1.Effect.ALLOW,
resources: ['*'],
}));
}
internalSpyLambda(func) {
const { layer, spyWrapperPath } = this.getExtensionForRuntime(func.runtime, func.architecture || aws_lambda_1.Architecture.X86_64);
if (!layer) {
return;
}
func.addLayers(layer);
const functionName = this.getConstructName(func);
func.addEnvironment(envVariableNames_1.envVariableNames.SSPY_FUNCTION_NAME, functionName);
func.addEnvironment('AWS_LAMBDA_EXEC_WRAPPER', spyWrapperPath);
if (this.props?.debugMode) {
func.addEnvironment(envVariableNames_1.envVariableNames.SSPY_DEBUG, 'true');
}
this.setupForIoT(func);
this.serviceKeys.push(`Function#${functionName}#Request`);
this.serviceKeys.push(`Function#${functionName}#Error`);
this.serviceKeys.push(`Function#${functionName}#Console`);
this.serviceKeys.push(`Function#${functionName}#Response`);
this.addMappingToFunction(func);
}
getConstructName(construct) {
let constructName = construct.node.path;
const { node } = aws_cdk_lib_1.Stack.of(this);
if (constructName.startsWith(node.id)) {
constructName = constructName.substring(node.id.length + 1);
}
return this.cleanName(constructName);
}
cleanName(name) {
//snake case to camel case including dash and first letter to upper case
return name
.replace(/[-_]+/g, ' ')
.replace(/[^\w\s]/g, '')
.replace(/\s(.)/g, ($1) => $1.toUpperCase())
.replace(/\s/g, '')
.replace(/^(.)/, ($1) => $1.toUpperCase());
}
getTopic(topicArn) {
const topic = this.findElement((node) => node instanceof sns.Topic && node.topicArn === topicArn);
return topic;
}
getEventBridge(eventBusName) {
const eventBridge = this.findElement((node) => (node instanceof events.EventBus ||
node.constructor.name === 'ImportedEventBus') &&
node.eventBusName === eventBusName);
return eventBridge;
}
findRootStack(stack) {
if (stack.nested) {
const parentStack = stack.nestedStackParent;
if (parentStack)
return this.findRootStack(parentStack);
return stack;
}
else {
return stack;
}
}
findElement(filterFunc, parent) {
if (!parent) {
parent = this.findRootStack(aws_cdk_lib_1.Stack.of(this));
}
for (const node of parent.node.children) {
if (filterFunc(node)) {
return node;
}
const elementFoundInChild = this.findElement(filterFunc, node);
if (elementFoundInChild) {
return elementFoundInChild;
}
}
return undefined;
}
addMappingToFunction(func, keyValue) {
for (const fs of this.lambdasSpied) {
if (fs.function === func) {
if (keyValue) {
fs.mapping[keyValue.key] = keyValue.value;
}
return;
}
}
const fs = {
function: func,
mapping: {},
};
if (keyValue) {
fs.mapping[keyValue.key] = keyValue.value;
}
this.lambdasSpied.push(fs);
}
getAssetLocation(location) {
const loc = path.join(__dirname, '../lib/' + location);
if (fs.existsSync(loc)) {
return loc;
}
const loc2 = path.join(__dirname, '../../lib/' + location);
if (fs.existsSync(loc2)) {
return loc2;
}
throw new Error(`Location ${loc} and ${loc2} does not exists.`);
}
}
exports.ServerlessSpy = ServerlessSpy;
_a = JSII_RTTI_SYMBOL_1;
ServerlessSpy[_a] = { fqn: "serverless-spy.ServerlessSpy", version: "2.3.7" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVybGVzc1NweS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9TZXJ2ZXJsZXNzU3B5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw4RUFBc0U7QUFDdEUsNkNBUXFCO0FBQ3JCLHFEQUFxRDtBQUNyRCxpREFBaUQ7QUFDakQsMERBQTBEO0FBQzFELGlEQUE2QztBQUM3QyxpREFBaUQ7QUFDakQsdURBSWdDO0FBQ2hDLHVFQUF1RTtBQUN2RSxtRkFBc0U7QUFDdEUsNERBQTREO0FBQzVELHFFQUErRDtBQUMvRCx5Q0FBeUM7QUFDekMsNERBQTREO0FBQzVELDJDQUEyQztBQUMzQyw2REFBNkQ7QUFDN0QsMkNBQTJDO0FBQzNDLDJDQUFtRDtBQUNuRCxnRUFBNkQ7QUFtQjdELE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxJQUFnQixFQUEyQixFQUFFLENBQ3JFLGNBQWMsSUFBSSxJQUFJLElBQUksYUFBYSxJQUFJLElBQUksSUFBSSxTQUFTLElBQUksSUFBSSxDQUFDO0FBRXZFLE1BQU0sb0NBQW9DLEdBQUcsMEJBQTBCLENBQUM7QUFFeEUsTUFBYSxhQUFjLFNBQVEsc0JBQVM7SUFVMUMsWUFDRSxLQUFnQixFQUNoQixFQUFVLEVBQ0YsS0FBMEI7UUFFbEMsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUZULFVBQUssR0FBTCxLQUFLLENBQXFCO1FBWjVCLDJCQUFzQixHQUFpQixFQUFFLENBQUM7UUFDMUMsMkJBQXNCLEdBQXlCLEVBQUUsQ0FBQztRQUVsRCxpQkFBWSxHQUFrQixFQUFFLENBQUM7UUFDbEMsZ0JBQVcsR0FBYSxFQUFFLENBQUM7UUFDMUIsZUFBVSxHQUFpQixFQUFFLENBQUM7UUFDOUIsYUFBUSxHQUEyQyxFQUFFLENBQUM7UUFVNUQsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FDOUIsSUFBSSxDQUFDLGFBQWEsQ0FB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