aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
711 lines • 89.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const client_sfn_1 = require("@aws-sdk/client-sfn");
const setup = require("./hotswap-test-setup");
const common_1 = require("../../../lib/api/hotswap/common");
const mock_sdk_1 = require("../../util/mock-sdk");
const silent_1 = require("../../util/silent");
let hotswapMockSdkProvider;
beforeEach(() => {
hotswapMockSdkProvider = setup.setupHotswapTests();
});
describe.each([common_1.HotswapMode.FALL_BACK, common_1.HotswapMode.HOTSWAP_ONLY])('%p mode', (hotswapMode) => {
(0, silent_1.silentTest)('returns undefined when a new StateMachine is added to the Stack', async () => {
// GIVEN
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
},
},
},
});
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).not.toHaveReceivedCommand(client_sfn_1.UpdateStateMachineCommand);
}
else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(deployStackResult?.noOp).toEqual(true);
expect(mock_sdk_1.mockStepFunctionsClient).not.toHaveReceivedCommand(client_sfn_1.UpdateStateMachineCommand);
}
});
(0, silent_1.silentTest)('calls the updateStateMachine() API when it receives only a definitionString change without Fn::Join in a state machine', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ Prop: "old-value" }',
StateMachineName: 'my-machine',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ Prop: "new-value" }',
StateMachineName: 'my-machine',
},
},
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
definition: '{ Prop: "new-value" }',
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:my-machine',
});
});
(0, silent_1.silentTest)('calls the updateStateMachine() API when it receives only a definitionString change with Fn::Join in a state machine', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'\n',
[
'{',
' "StartAt" : "SuccessState"',
' "States" : {',
' "SuccessState": {',
' "Type": "Pass"',
' "Result": "Success"',
' "End": true',
' }',
' }',
'}',
],
],
},
StateMachineName: 'my-machine',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'\n',
[
'{',
' "StartAt": "SuccessState",',
' "States": {',
' "SuccessState": {',
' "Type": "Succeed"',
' }',
' }',
'}',
],
],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
definition: JSON.stringify({
StartAt: 'SuccessState',
States: {
SuccessState: {
Type: 'Succeed',
},
},
}, null, 2),
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:my-machine',
});
});
(0, silent_1.silentTest)('calls the updateStateMachine() API when it receives a change to the definitionString in a state machine that has no name', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ "Prop" : "old-value" }',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ "Prop" : "new-value" }',
},
},
},
},
});
// WHEN
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:swa:states:here:123456789012:stateMachine:my-machine'));
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
definition: '{ "Prop" : "new-value" }',
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:my-machine',
});
});
(0, silent_1.silentTest)(`does not call the updateStateMachine() API when it receives a change to a property that is not the definitionString in a state machine
alongside a hotswappable change in CLASSIC mode but does in HOTSWAP_ONLY mode`, async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ "Prop" : "old-value" }',
LoggingConfiguration: {
// non-definitionString property
IncludeExecutionData: true,
},
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ "Prop" : "new-value" }',
LoggingConfiguration: {
IncludeExecutionData: false,
},
},
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:swa:states:here:123456789012:stateMachine:my-machine'));
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).not.toHaveReceivedCommand(client_sfn_1.UpdateStateMachineCommand);
}
else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
definition: '{ "Prop" : "new-value" }',
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:my-machine',
});
}
});
(0, silent_1.silentTest)('does not call the updateStateMachine() API when a resource has a DefinitionString property but is not an AWS::StepFunctions::StateMachine is changed', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::NotStepFunctions::NotStateMachine',
Properties: {
DefinitionString: '{ Prop: "old-value" }',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::NotStepFunctions::NotStateMachine',
Properties: {
DefinitionString: '{ Prop: "new-value" }',
},
},
},
},
});
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).not.toHaveReceivedCommand(client_sfn_1.UpdateStateMachineCommand);
}
else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(deployStackResult?.noOp).toEqual(true);
expect(mock_sdk_1.mockStepFunctionsClient).not.toHaveReceivedCommand(client_sfn_1.UpdateStateMachineCommand);
}
});
(0, silent_1.silentTest)('can correctly hotswap old style synth changes', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Parameters: { AssetParam1: { Type: 'String' } },
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: { Ref: 'AssetParam1' },
StateMachineName: 'machine-name',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Parameters: { AssetParam2: { Type: String } },
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: { Ref: 'AssetParam2' },
StateMachineName: 'machine-name',
},
},
},
},
});
// WHEN
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:swa:states:here:123456789012:stateMachine:my-machine'));
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact, {
AssetParam2: 'asset-param-2',
});
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
definition: 'asset-param-2',
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:machine-name',
});
});
(0, silent_1.silentTest)('calls the updateStateMachine() API when it receives a change to the definitionString that uses Attributes in a state machine', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Func: {
Type: 'AWS::Lambda::Function',
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'\n',
[
'{',
' "StartAt" : "SuccessState"',
' "States" : {',
' "SuccessState": {',
' "Type": "Succeed"',
' }',
' }',
'}',
],
],
},
StateMachineName: 'my-machine',
},
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Func: {
Type: 'AWS::Lambda::Function',
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': ['', ['"Resource": ', { 'Fn::GetAtt': ['Func', 'Arn'] }]],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// WHEN
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:swa:states:here:123456789012:stateMachine:my-machine'), setup.stackSummaryOf('Func', 'AWS::Lambda::Function', 'my-func'));
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
definition: '"Resource": arn:swa:lambda:here:123456789012:function:my-func',
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:my-machine',
});
});
(0, silent_1.silentTest)("will not perform a hotswap deployment if it cannot find a Ref target (outside the state machine's name)", async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Parameters: {
Param1: { Type: 'String' },
},
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': ['', ['{ Prop: "old-value" }, ', '{ "Param" : ', { 'Fn::Sub': '${Param1}' }, ' }']],
},
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'my-machine'));
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Parameters: {
Param1: { Type: 'String' },
},
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': ['', ['{ Prop: "new-value" }, ', '{ "Param" : ', { 'Fn::Sub': '${Param1}' }, ' }']],
},
},
},
},
},
});
// THEN
await expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow(/Parameter or resource 'Param1' could not be found for evaluation/);
});
(0, silent_1.silentTest)("will not perform a hotswap deployment if it doesn't know how to handle a specific attribute (outside the state machines's name)", async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Bucket: {
Type: 'AWS::S3::Bucket',
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'{ Prop: "old-value" }, ',
'{ "S3Bucket" : ',
{ 'Fn::GetAtt': ['Bucket', 'UnknownAttribute'] },
' }',
],
],
},
StateMachineName: 'my-machine',
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Machine', 'AWS::StepFunctions::StateMachine', 'arn:swa:states:here:123456789012:stateMachine:my-machine'), setup.stackSummaryOf('Bucket', 'AWS::S3::Bucket', 'my-bucket'));
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Bucket: {
Type: 'AWS::S3::Bucket',
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'{ Prop: "new-value" }, ',
'{ "S3Bucket" : ',
{ 'Fn::GetAtt': ['Bucket', 'UnknownAttribute'] },
' }',
],
],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// THEN
await expect(() => hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact)).rejects.toThrow("We don't support the 'UnknownAttribute' attribute of the 'AWS::S3::Bucket' resource. This is a CDK limitation. Please report it at https://github.com/aws/aws-cdk/issues/new/choose");
});
(0, silent_1.silentTest)('knows how to handle attributes of the AWS::Events::EventBus resource', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
EventBus: {
Type: 'AWS::Events::EventBus',
Properties: {
Name: 'my-event-bus',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'{"EventBus1Arn":"',
{ 'Fn::GetAtt': ['EventBus', 'Arn'] },
'","EventBus1Name":"',
{ 'Fn::GetAtt': ['EventBus', 'Name'] },
'","EventBus1Ref":"',
{ Ref: 'EventBus' },
'"}',
],
],
},
StateMachineName: 'my-machine',
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('EventBus', 'AWS::Events::EventBus', 'my-event-bus'));
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
EventBus: {
Type: 'AWS::Events::EventBus',
Properties: {
Name: 'my-event-bus',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
[
'{"EventBus2Arn":"',
{ 'Fn::GetAtt': ['EventBus', 'Arn'] },
'","EventBus2Name":"',
{ 'Fn::GetAtt': ['EventBus', 'Name'] },
'","EventBus2Ref":"',
{ Ref: 'EventBus' },
'"}',
],
],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// THEN
const result = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
expect(result).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:my-machine',
definition: JSON.stringify({
EventBus2Arn: 'arn:swa:events:here:123456789012:event-bus/my-event-bus',
EventBus2Name: 'my-event-bus',
EventBus2Ref: 'my-event-bus',
}),
});
});
(0, silent_1.silentTest)('knows how to handle attributes of the AWS::DynamoDB::Table resource', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Table: {
Type: 'AWS::DynamoDB::Table',
Properties: {
KeySchema: [
{
AttributeName: 'name',
KeyType: 'HASH',
},
],
AttributeDefinitions: [
{
AttributeName: 'name',
AttributeType: 'S',
},
],
BillingMode: 'PAY_PER_REQUEST',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{}',
StateMachineName: 'my-machine',
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Table', 'AWS::DynamoDB::Table', 'my-dynamodb-table'));
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Table: {
Type: 'AWS::DynamoDB::Table',
Properties: {
KeySchema: [
{
AttributeName: 'name',
KeyType: 'HASH',
},
],
AttributeDefinitions: [
{
AttributeName: 'name',
AttributeType: 'S',
},
],
BillingMode: 'PAY_PER_REQUEST',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
['{"TableName":"', { Ref: 'Table' }, '","TableArn":"', { 'Fn::GetAtt': ['Table', 'Arn'] }, '"}'],
],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// THEN
const result = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
expect(result).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:my-machine',
definition: JSON.stringify({
TableName: 'my-dynamodb-table',
TableArn: 'arn:swa:dynamodb:here:123456789012:table/my-dynamodb-table',
}),
});
});
(0, silent_1.silentTest)('knows how to handle attributes of the AWS::KMS::Key resource', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Key: {
Type: 'AWS::KMS::Key',
Properties: {
Description: 'magic-key',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{}',
StateMachineName: 'my-machine',
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Key', 'AWS::KMS::Key', 'a-key'));
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Key: {
Type: 'AWS::KMS::Key',
Properties: {
Description: 'magic-key',
},
},
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: {
'Fn::Join': [
'',
['{"KeyId":"', { Ref: 'Key' }, '","KeyArn":"', { 'Fn::GetAtt': ['Key', 'Arn'] }, '"}'],
],
},
StateMachineName: 'my-machine',
},
},
},
},
});
// THEN
const result = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
expect(result).not.toBeUndefined();
expect(mock_sdk_1.mockStepFunctionsClient).toHaveReceivedCommandWith(client_sfn_1.UpdateStateMachineCommand, {
stateMachineArn: 'arn:swa:states:here:123456789012:stateMachine:my-machine',
definition: JSON.stringify({
KeyId: 'a-key',
KeyArn: 'arn:swa:kms:here:123456789012:key/a-key',
}),
});
});
(0, silent_1.silentTest)('does not explode if the DependsOn changes', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ Prop: "old-value" }',
StateMachineName: 'my-machine',
},
DependsOn: ['abc'],
},
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
Machine: {
Type: 'AWS::StepFunctions::StateMachine',
Properties: {
DefinitionString: '{ Prop: "old-value" }',
StateMachineName: 'my-machine',
},
},
DependsOn: ['xyz'],
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(deployStackResult?.noOp).toEqual(true);
expect(mock_sdk_1.mockStepFunctionsClient).not.toHaveReceivedCommand(client_sfn_1.UpdateStateMachineCommand);
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGUtbWFjaGluZS1ob3Rzd2FwLWRlcGxveW1lbnRzLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzdGF0ZS1tYWNoaW5lLWhvdHN3YXAtZGVwbG95bWVudHMudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLG9EQUFnRTtBQUNoRSw4Q0FBOEM7QUFDOUMsNERBQThEO0FBQzlELGtEQUE4RDtBQUM5RCw4Q0FBK0M7QUFFL0MsSUFBSSxzQkFBb0QsQ0FBQztBQUV6RCxVQUFVLENBQUMsR0FBRyxFQUFFO0lBQ2Qsc0JBQXNCLEdBQUcsS0FBSyxDQUFDLGlCQUFpQixFQUFFLENBQUM7QUFDckQsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsb0JBQVcsQ0FBQyxTQUFTLEVBQUUsb0JBQVcsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDLFdBQVcsRUFBRSxFQUFFO0lBQzFGLElBQUEsbUJBQVUsRUFBQyxpRUFBaUUsRUFBRSxLQUFLLElBQUksRUFBRTtRQUN2RixRQUFRO1FBQ1IsTUFBTSxnQkFBZ0IsR0FBRyxLQUFLLENBQUMsa0JBQWtCLENBQUM7WUFDaEQsUUFBUSxFQUFFO2dCQUNSLFNBQVMsRUFBRTtvQkFDVCxPQUFPLEVBQUU7d0JBQ1AsSUFBSSxFQUFFLGtDQUFrQztxQkFDekM7aUJBQ0Y7YUFDRjtTQUNGLENBQUMsQ0FBQztRQUVILElBQUksV0FBVyxLQUFLLG9CQUFXLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDMUMsT0FBTztZQUNQLE1BQU0saUJBQWlCLEdBQUcsTUFBTSxzQkFBc0IsQ0FBQyxvQkFBb0IsQ0FBQyxXQUFXLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztZQUUzRyxPQUFPO1lBQ1AsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDMUMsTUFBTSxDQUFDLGtDQUF1QixDQUFDLENBQUMsR0FBRyxDQUFDLHFCQUFx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