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.
618 lines • 93.8 kB
JavaScript
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 = `sspy_extension_${runtime.toString()}_${architecture.name.toString()}`.replace(/\./g, '_');
let layer = this.layerMap[layerKey];
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:
spyWrapperPath = '/opt/python/spy-wrapper';
layer =
layer ||
new PythonLayerVersion(this, layerKey, {
compatibleRuntimes: [runtime],
compatibleArchitectures: [architecture],
entry: this.getLanguageExtensionAssetLocation('python'),
bundling: {
bundlingFileAccess: BundlingFileAccess.VOLUME_COPY,
},
});
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, layerKey, {
compatibleRuntimes: [runtime],
compatibleArchitectures: [architecture],
code: lambda.Code.fromAsset(this.getExtensionAssetLocation()),
});
break;
default:
console.log(`No extensions available for ${runtime.toString()}`);
return undefined;
}
this.layerMap[layerKey] = 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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