UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

711 lines 89.4 kB
"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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