{"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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"}