aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
781 lines • 89.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable import/order */
jest.mock('../../lib/api/deploy-stack');
jest.mock('../../lib/util/asset-publishing');
const fake_cloudformation_stack_1 = require("./fake-cloudformation-stack");
const deployments_1 = require("../../lib/api/deployments");
const deploy_stack_1 = require("../../lib/api/deploy-stack");
const common_1 = require("../../lib/api/hotswap/common");
const toolkit_info_1 = require("../../lib/api/toolkit-info");
const cloudformation_1 = require("../../lib/api/util/cloudformation");
const util_1 = require("../util");
const mock_sdk_1 = require("../util/mock-sdk");
let sdkProvider;
let deployments;
let mockToolkitInfoLookup;
let currentCfnStackResources;
let numberOfTimesListStackResourcesWasCalled;
beforeEach(() => {
jest.resetAllMocks();
sdkProvider = new mock_sdk_1.MockSdkProvider();
deployments = new deployments_1.Deployments({ sdkProvider });
numberOfTimesListStackResourcesWasCalled = 0;
currentCfnStackResources = {};
sdkProvider.stubCloudFormation({
listStackResources: ({ StackName: stackName }) => {
numberOfTimesListStackResourcesWasCalled++;
const stackResources = currentCfnStackResources[stackName];
if (!stackResources) {
throw new Error(`Stack with id ${stackName} does not exist`);
}
return {
StackResourceSummaries: stackResources,
};
},
});
toolkit_info_1.ToolkitInfo.lookup = mockToolkitInfoLookup = jest.fn().mockResolvedValue(toolkit_info_1.ToolkitInfo.bootstrapStackNotFoundInfo('TestBootstrapStack'));
});
function mockSuccessfulBootstrapStackLookup(props) {
const outputs = {
BucketName: 'BUCKET_NAME',
BucketDomainName: 'BUCKET_ENDPOINT',
BootstrapVersion: '1',
...props,
};
const fakeStack = (0, mock_sdk_1.mockBootstrapStack)(sdkProvider.sdk, {
Outputs: Object.entries(outputs).map(([k, v]) => ({
OutputKey: k,
OutputValue: `${v}`,
})),
});
mockToolkitInfoLookup.mockResolvedValue(toolkit_info_1.ToolkitInfo.fromStack(fakeStack));
}
test('passes through hotswap=true to deployStack()', async () => {
// WHEN
await deployments.deployStack({
stack: (0, util_1.testStack)({
stackName: 'boop',
}),
hotswap: common_1.HotswapMode.FALL_BACK,
});
// THEN
expect(deploy_stack_1.deployStack).toHaveBeenCalledWith(expect.objectContaining({
hotswap: common_1.HotswapMode.FALL_BACK,
}));
});
test('placeholders are substituted in CloudFormation execution role', async () => {
await deployments.deployStack({
stack: (0, util_1.testStack)({
stackName: 'boop',
properties: {
cloudFormationExecutionRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}',
},
}),
});
expect(deploy_stack_1.deployStack).toHaveBeenCalledWith(expect.objectContaining({
roleArn: 'bloop:here:123456789012',
}));
});
test('role with placeholders is assumed if assumerole is given', async () => {
const mockForEnvironment = jest.fn().mockImplementation(() => { return { sdk: sdkProvider.sdk }; });
sdkProvider.forEnvironment = mockForEnvironment;
await deployments.deployStack({
stack: (0, util_1.testStack)({
stackName: 'boop',
properties: {
assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}',
},
}),
});
expect(mockForEnvironment).toHaveBeenCalledWith(expect.anything(), expect.anything(), expect.objectContaining({
assumeRoleArn: 'bloop:here:123456789012',
}));
});
test('deployment fails if bootstrap stack is missing', async () => {
await expect(deployments.deployStack({
stack: (0, util_1.testStack)({
stackName: 'boop',
properties: {
assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}',
requiresBootstrapStackVersion: 99,
},
}),
})).rejects.toThrow(/requires a bootstrap stack/);
});
test('deployment fails if bootstrap stack is too old', async () => {
mockSuccessfulBootstrapStackLookup({
BootstrapVersion: 5,
});
await expect(deployments.deployStack({
stack: (0, util_1.testStack)({
stackName: 'boop',
properties: {
assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}',
requiresBootstrapStackVersion: 99,
},
}),
})).rejects.toThrow(/requires bootstrap stack version '99', found '5'/);
});
test.each([false, true])('if toolkit stack be found: %p but SSM parameter name is present deployment succeeds', async (canLookup) => {
if (canLookup) {
mockSuccessfulBootstrapStackLookup({
BootstrapVersion: 2,
});
}
let requestedParameterName;
sdkProvider.stubSSM({
getParameter(request) {
requestedParameterName = request.Name;
return {
Parameter: {
Value: '99',
},
};
},
});
await deployments.deployStack({
stack: (0, util_1.testStack)({
stackName: 'boop',
properties: {
assumeRoleArn: 'bloop:${AWS::Region}:${AWS::AccountId}',
requiresBootstrapStackVersion: 99,
bootstrapStackVersionSsmParameter: '/some/parameter',
},
}),
});
expect(requestedParameterName).toEqual('/some/parameter');
});
test('readCurrentTemplateWithNestedStacks() can handle non-Resources in the template', async () => {
const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({
stackName: 'ParentOfStackWithOutputAndParameter',
stackId: 'StackId',
});
cloudformation_1.CloudFormationStack.lookup = (async (_, stackName) => {
switch (stackName) {
case 'ParentOfStackWithOutputAndParameter':
cfnStack.template = async () => ({
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-output-one-param-stack.nested.template.json',
},
},
},
});
break;
case 'NestedStack':
cfnStack.template = async () => ({
Resources: {
NestedResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
Parameters: {
NestedParam: {
Type: 'String',
},
},
Outputs: {
NestedOutput: {
Value: {
Ref: 'NestedResource',
},
},
},
});
break;
default:
throw new Error('unknown stack name ' + stackName + ' found');
}
return cfnStack;
});
const rootStack = (0, util_1.testStack)({
stackName: 'ParentOfStackWithOutputAndParameter',
template: {
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-output-one-param-stack.nested.template.json',
},
},
},
},
});
pushStackResourceSummaries('ParentOfStackWithOutputAndParameter', stackSummaryOf('NestedStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/NestedStack/abcd'));
// WHEN
const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount;
const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate;
// THEN
expect(nestedStackCount).toEqual(1);
expect(deployedTemplate).toEqual({
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
NestedResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
Outputs: {
NestedOutput: {
Value: {
Ref: 'NestedResource',
},
},
},
Parameters: {
NestedParam: {
Type: 'String',
},
},
},
},
Metadata: {
'aws:asset:path': 'one-output-one-param-stack.nested.template.json',
},
},
},
});
expect(rootStack.template).toEqual({
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
NestedResource: {
Type: 'AWS::Something',
Properties: {
Property: 'new-value',
},
},
},
Outputs: {
NestedOutput: {
Value: {
Ref: 'NestedResource',
},
},
},
Parameters: {
NestedParam: {
Type: 'Number',
},
},
},
},
Metadata: {
'aws:asset:path': 'one-output-one-param-stack.nested.template.json',
},
},
},
});
});
test('readCurrentTemplateWithNestedStacks() with a 3-level nested + sibling structure works', async () => {
const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({
stackName: 'MultiLevelRoot',
stackId: 'StackId',
});
cloudformation_1.CloudFormationStack.lookup = (async (_, stackName) => {
switch (stackName) {
case 'MultiLevelRoot':
cfnStack.template = async () => ({
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json',
},
},
},
});
break;
case 'NestedStack':
cfnStack.template = async () => ({
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
GrandChildStackA: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
GrandChildStackB: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
},
});
break;
case 'GrandChildStackA':
cfnStack.template = async () => ({
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
});
break;
case 'GrandChildStackB':
cfnStack.template = async () => ({
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
});
break;
default:
throw new Error('unknown stack name ' + stackName + ' found in deployments.test.ts');
}
return cfnStack;
});
const rootStack = (0, util_1.testStack)({
stackName: 'MultiLevelRoot',
template: {
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json',
},
},
},
},
});
pushStackResourceSummaries('MultiLevelRoot', stackSummaryOf('NestedStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/NestedStack/abcd'));
pushStackResourceSummaries('NestedStack', stackSummaryOf('GrandChildStackA', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildStackA/abcd'), stackSummaryOf('GrandChildStackB', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildStackB/abcd'));
pushStackResourceSummaries('GrandChildStackA', stackSummaryOf('NestedStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildA/abcd'));
pushStackResourceSummaries('GrandChildStackB', stackSummaryOf('NestedStack', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/GrandChildB/abcd'));
// WHEN
const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount;
const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate;
// THEN
expect(nestedStackCount).toEqual(3);
expect(deployedTemplate).toEqual({
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
GrandChildStackA: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
},
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
GrandChildStackB: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
},
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
},
},
Metadata: {
'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json',
},
},
},
});
expect(rootStack.template).toEqual({
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
GrandChildStackA: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'new-value',
},
},
},
},
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
GrandChildStackB: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'new-value',
},
},
},
},
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'new-value',
},
},
},
},
},
Metadata: {
'aws:asset:path': 'one-resource-two-stacks-stack.nested.template.json',
},
},
},
});
});
test('readCurrentTemplateWithNestedStacks() on an undeployed parent stack with an (also undeployed) nested stack works', async () => {
// GIVEN
const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({
stackName: 'UndeployedParent',
stackId: 'StackId',
});
cloudformation_1.CloudFormationStack.lookup = (async (_cfn, _stackName) => {
cfnStack.template = async () => ({});
return cfnStack;
});
const rootStack = (0, util_1.testStack)({
stackName: 'UndeployedParent',
template: {
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-one-stack-stack.nested.template.json',
},
},
},
},
});
// WHEN
const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount;
const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate;
// THEN
expect(nestedStackCount).toEqual(2);
expect(deployedTemplate).toEqual({
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
NestedTemplate: {
Resources: {
NestedStack: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
NestedTemplate: {},
},
},
},
},
},
},
},
});
});
test('readCurrentTemplateWithNestedStacks() caches calls to listStackResources()', async () => {
// GIVEN
const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({
stackName: 'CachingRoot',
stackId: 'StackId',
});
cloudformation_1.CloudFormationStack.lookup = (async (_cfn, _stackName) => {
cfnStack.template = async () => ({
Resources: {
NestedStackA: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
NestedStackB: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
},
});
return cfnStack;
});
const rootStack = (0, util_1.testStack)({
stackName: 'CachingRoot',
template: {
Resources: {
NestedStackA: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
NestedStackB: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
},
},
});
pushStackResourceSummaries('CachingRoot', stackSummaryOf('NestedStackA', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/one-resource-stack/abcd'), stackSummaryOf('NestedStackB', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/one-resource-stack/abcd'));
// WHEN
await deployments.readCurrentTemplateWithNestedStacks(rootStack);
// THEN
expect(numberOfTimesListStackResourcesWasCalled).toEqual(1);
});
test('readCurrentTemplateWithNestedStacks() succesfully ignores stacks without metadata', async () => {
// GIVEN
const cfnStack = new fake_cloudformation_stack_1.FakeCloudformationStack({
stackName: 'MetadataRoot',
stackId: 'StackId',
});
cloudformation_1.CloudFormationStack.lookup = (async (_, stackName) => {
if (stackName === 'MetadataRoot') {
cfnStack.template = async () => ({
Resources: {
WithMetadata: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
},
});
}
else {
cfnStack.template = async () => ({
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
});
}
return cfnStack;
});
const rootStack = (0, util_1.testStack)({
stackName: 'MetadataRoot',
template: {
Resources: {
WithoutMetadata: {
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Type: 'AWS::CloudFormation::Stack',
},
WithEmptyMetadata: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {},
},
WithMetadata: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
},
},
});
pushStackResourceSummaries('MetadataRoot', stackSummaryOf('WithMetadata', 'AWS::CloudFormation::Stack', 'arn:aws:cloudformation:bermuda-triangle-1337:123456789012:stack/one-resource-stack/abcd'));
// WHEN
const nestedStackCount = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).nestedStackCount;
const deployedTemplate = (await deployments.readCurrentTemplateWithNestedStacks(rootStack)).deployedTemplate;
// THEN
expect(nestedStackCount).toEqual(1);
expect(deployedTemplate).toEqual({
Resources: {
WithMetadata: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'old-value',
},
},
},
},
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
},
});
expect(rootStack.template).toEqual({
Resources: {
WithoutMetadata: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
},
WithEmptyMetadata: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
},
Metadata: {},
},
WithMetadata: {
Type: 'AWS::CloudFormation::Stack',
Properties: {
TemplateURL: 'https://www.magic-url.com',
NestedTemplate: {
Resources: {
SomeResource: {
Type: 'AWS::Something',
Properties: {
Property: 'new-value',
},
},
},
},
},
Metadata: {
'aws:asset:path': 'one-resource-stack.nested.template.json',
},
},
},
});
});
function pushStackResourceSummaries(stackName, ...items) {
if (!currentCfnStackResources[stackName]) {
currentCfnStackResources[stackName] = [];
}
currentCfnStackResources[stackName].push(...items);
}
function stackSummaryOf(logicalId, resourceType, physicalResourceId) {
return {
LogicalResourceId: logicalId,
PhysicalResourceId: physicalResourceId,
ResourceType: resourceType,
ResourceStatus: 'CREATE_COMPLETE',
LastUpdatedTimestamp: new Date(),
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvdWRmb3JtYXRpb24tZGVwbG95bWVudHMudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImNsb3VkZm9ybWF0aW9uLWRlcGxveW1lbnRzLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxpQ0FBaUM7QUFDakMsSUFBSSxDQUFDLElBQUksQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO0FBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsaUNBQWlDLENBQUMsQ0FBQztBQUc3QywyRUFBc0U7QUFDdEUsMkRBQXdEO0FBQ3hELDZEQUF5RDtBQUN6RCx5REFBMkQ7QUFDM0QsNkRBQXlEO0FBQ3pELHNFQUF3RTtBQUN4RSxrQ0FBb0M7QUFDcEMsK0NBQXVFO0FBRXZFLElBQUksV0FBNEIsQ0FBQztBQUNqQyxJQUFJLFdBQXdCLENBQUM7QUFDN0IsSUFBSSxxQkFBZ0MsQ0FBQztBQUNyQyxJQUFJLHdCQUFrRixDQUFDO0FBQ3ZGLElBQUksd0NBQWdELENBQUM7QUFDckQsVUFBVSxDQUFDLEdBQUcsRUFBRTtJQUNkLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztJQUNyQixXQUFXLEdBQUcsSUFBSSwwQkFBZSxFQUFFLENBQUM7SUFDcEMsV0FBVyxHQUFHLElBQUkseUJBQVcsQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLENBQUM7SUFFL0Msd0NBQXdDLEdBQUcsQ0FBQyxDQUFDO0lBQzdDLHdCQUF3QixHQUFHLEVBQUUsQ0FBQztJQUM5QixXQUFXLENBQUMsa0JBQWtCLENBQUM7UUFDN0Isa0JBQWtCLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO1lBQy9DLHdDQUF3QyxFQUFFLENBQUM7WUFDM0MsTUFBTSxjQUFjLEdBQUcsd0JBQXdCLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDM0QsSUFBSSxDQUFDLGNBQWMsRUFBRTtnQkFDbkIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsU0FBUyxpQkFBaUIsQ0FB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