UNPKG

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.

638 lines 97 kB
import * as fs from 'fs'; import * as path from 'path'; import { PythonLayerVersion } from '@aws-cdk/aws-lambda-python-alpha'; import { aws_iam, BundlingFileAccess, CfnOutput, custom_resources, Duration, Stack, } from 'aws-cdk-lib'; import * as dynamoDb from 'aws-cdk-lib/aws-dynamodb'; import * as events from 'aws-cdk-lib/aws-events'; import * as targets from 'aws-cdk-lib/aws-events-targets'; import { Effect } from 'aws-cdk-lib/aws-iam'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { Architecture, SingletonFunction, } from 'aws-cdk-lib/aws-lambda'; import * as dynamoDbStream from 'aws-cdk-lib/aws-lambda-event-sources'; import { SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; import * as lambdaNode from 'aws-cdk-lib/aws-lambda-nodejs'; import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as s3notif from 'aws-cdk-lib/aws-s3-notifications'; import * as sns from 'aws-cdk-lib/aws-sns'; import * as snsSubs from 'aws-cdk-lib/aws-sns-subscriptions'; import * as sqs from 'aws-cdk-lib/aws-sqs'; import { Construct } from 'constructs'; import { envVariableNames } from './common/envVariableNames'; const isLambdaFunction = (node) => 'functionName' in node && 'functionArn' in node && 'runtime' in node; const serverlessSpyIotEndpointCrNamePrefix = 'ServerlessSpyIotEndpoint'; export class ServerlessSpy extends 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(Stack.of(this)).node.id); const getIoTEndpoint = new custom_resources.AwsCustomResource(this, serverlessSpyIotEndpointCrNamePrefix, { onCreate: { service: 'Iot', action: 'describeEndpoint', physicalResourceId: custom_resources.PhysicalResourceId.fromResponse('endpointAddress'), parameters: { endpointType: 'iot:Data-ATS', }, }, onUpdate: { service: 'Iot', action: 'describeEndpoint', physicalResourceId: custom_resources.PhysicalResourceId.fromResponse('endpointAddress'), parameters: { endpointType: 'iot:Data-ATS', }, }, installLatestAwsSdk: false, policy: custom_resources.AwsCustomResourcePolicy.fromSdkCalls({ resources: custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE, }), functionName: serverlessSpyIotEndpointCrNamePrefix + rootStack, }); this.iotEndpoint = getIoTEndpoint.getResponseField('endpointAddress'); this.createdResourcesBySSpy.push(getIoTEndpoint); new 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(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' + 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 SingletonFunction) { if (this.props?.debugMode) { console.info(`Skipping ${node.node.id}`); } return false; } else if (filterWithDefaults.spyLambda && (node instanceof lambda.Function || node instanceof 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.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.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 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 PythonLayerVersion(this, `PythonExtension${runtime.name .replace('python', '') .replace('.', '_')}`, { compatibleRuntimes: [runtime], compatibleArchitectures: [architecture], entry: location, bundling: { bundlingFileAccess: 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 NodejsFunction(this, `${queueName}SqsSubscriptionAndDropAllMessages`, { memorySize: 512, timeout: Duration.seconds(5), runtime: lambda.Runtime.NODEJS_22_X, handler: 'handler', entry: this.getAssetLocation('functions/sqsSubscriptionAndDropAllMessages.js'), environment: this.getDefaultLambdaEnvironmentVariables(), }); func.addEventSource(new 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.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.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.SSPY_SUBSCRIBED_TO_SQS, 'true'); } } createFunctionForSubscription(index) { const func = new lambdaNode.NodejsFunction(this, `Subscription${index}`, { memorySize: 512, timeout: 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.SSPY_ROOT_STACK, this.cleanName(this.findRootStack(Stack.of(this)).node.id)); func.addEnvironment(envVariableNames.SSPY_IOT_ENDPOINT, this.iotEndpoint); func.addToRolePolicy(new aws_iam.PolicyStatement({ actions: ['iot:*'], effect: Effect.ALLOW, resources: ['*'], })); } internalSpyLambda(func) { const { layer, spyWrapperPath } = this.getExtensionForRuntime(func.runtime, func.architecture || Architecture.X86_64); if (!layer) { return; } func.addLayers(layer); const functionName = this.getConstructName(func); func.addEnvironment(envVariableNames.SSPY_FUNCTION_NAME, functionName); func.addEnvironment('AWS_LAMBDA_EXEC_WRAPPER', spyWrapperPath); if (this.props?.debugMode) { func.addEnvironment(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 } = 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(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.`); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVybGVzc1NweS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9TZXJ2ZXJsZXNzU3B5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ3RFLE9BQU8sRUFDTCxPQUFPLEVBQ1Asa0JBQWtCLEVBQ2xCLFNBQVMsRUFDVCxnQkFBZ0IsRUFDaEIsUUFBUSxFQUVSLEtBQUssR0FDTixNQUFNLGFBQWEsQ0FBQztBQUNyQixPQUFPLEtBQUssUUFBUSxNQUFNLDBCQUEwQixDQUFDO0FBQ3JELE9BQU8sS0FBSyxNQUFNLE1BQU0sd0JBQXdCLENBQUM7QUFDakQsT0FBTyxLQUFLLE9BQU8sTUFBTSxnQ0FBZ0MsQ0FBQztBQUMxRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDN0MsT0FBTyxLQUFLLE1BQU0sTUFBTSx3QkFBd0IsQ0FBQztBQUNqRCxPQUFPLEVBQ0wsWUFBWSxFQUVaLGlCQUFpQixHQUNsQixNQUFNLHdCQUF3QixDQUFDO0FBQ2hDLE9BQU8sS0FBSyxjQUFjLE1BQU0sc0NBQXNDLENBQUM7QUFDdkUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ3RFLE9BQU8sS0FBSyxVQUFVLE1BQU0sK0JBQStCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQy9ELE9BQU8sS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDekMsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQ0FBa0MsQ0FBQztBQUM1RCxPQUFPLEtBQUssR0FBRyxNQUFNLHFCQUFxQixDQUFDO0FBQzNDLE9BQU8sS0FBSyxPQUFPLE1BQU0sbUNBQW1DLENBQUM7QUFD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