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