UNPKG

@aws-cdk/core

Version:

AWS Cloud Development Kit Core Library

698 lines 103 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CfnParser = exports.CfnParsingContext = exports.FromCloudFormation = exports.FromCloudFormationPropertyObject = exports.FromCloudFormationResult = void 0; const cfn_fn_1 = require("../cfn-fn"); const cfn_pseudo_1 = require("../cfn-pseudo"); const cfn_resource_policy_1 = require("../cfn-resource-policy"); const lazy_1 = require("../lazy"); const cfn_reference_1 = require("../private/cfn-reference"); const token_1 = require("../token"); const util_1 = require("../util"); /** * The class used as the intermediate result from the generated L1 methods * that convert from CloudFormation's UpperCase to CDK's lowerCase property names. * Saves any extra properties that were present in the argument object, * but that were not found in the CFN schema, * so that they're not lost from the final CDK-rendered template. */ class FromCloudFormationResult { constructor(value) { this.value = value; this.extraProperties = {}; } appendExtraProperties(prefix, properties) { for (const [key, val] of Object.entries(properties ?? {})) { this.extraProperties[`${prefix}.${key}`] = val; } } } exports.FromCloudFormationResult = FromCloudFormationResult; /** * A property object we will accumulate properties into */ class FromCloudFormationPropertyObject extends FromCloudFormationResult { constructor() { super({}); // We're still accumulating this.recognizedProperties = new Set(); } /** * Add a parse result under a given key */ addPropertyResult(cdkPropName, cfnPropName, result) { this.recognizedProperties.add(cfnPropName); if (!result) { return; } this.value[cdkPropName] = result.value; this.appendExtraProperties(cfnPropName, result.extraProperties); } addUnrecognizedPropertiesAsExtra(properties) { for (const [key, val] of Object.entries(properties)) { if (!this.recognizedProperties.has(key)) { this.extraProperties[key] = val; } } } } exports.FromCloudFormationPropertyObject = FromCloudFormationPropertyObject; /** * This class contains static methods called when going from * translated values received from {@link CfnParser.parseValue} * to the actual L1 properties - * things like changing IResolvable to the appropriate type * (string, string array, or number), etc. * * While this file not exported from the module * (to not make it part of the public API), * it is directly referenced in the generated L1 code. * */ class FromCloudFormation { // nothing to for any but return it static getAny(value) { return new FromCloudFormationResult(value); } static getBoolean(value) { if (typeof value === 'string') { // CloudFormation allows passing strings as boolean switch (value) { case 'true': return new FromCloudFormationResult(true); case 'false': return new FromCloudFormationResult(false); default: throw new Error(`Expected 'true' or 'false' for boolean value, got: '${value}'`); } } // in all other cases, just return the value, // and let a validator handle if it's not a boolean return new FromCloudFormationResult(value); } static getDate(value) { // if the date is a deploy-time value, just return it if (token_1.isResolvableObject(value)) { return new FromCloudFormationResult(value); } // if the date has been given as a string, convert it if (typeof value === 'string') { return new FromCloudFormationResult(new Date(value)); } // all other cases - just return the value, // if it's not a Date, a validator should catch it return new FromCloudFormationResult(value); } // won't always return a string; if the input can't be resolved to a string, // the input will be returned. static getString(value) { // if the string is a deploy-time value, serialize it to a Token if (token_1.isResolvableObject(value)) { return new FromCloudFormationResult(value.toString()); } // CloudFormation treats numbers and strings interchangeably; // so, if we get a number here, convert it to a string if (typeof value === 'number') { return new FromCloudFormationResult(value.toString()); } // CloudFormation treats booleans and strings interchangeably; // so, if we get a boolean here, convert it to a string if (typeof value === 'boolean') { return new FromCloudFormationResult(value.toString()); } // in all other cases, just return the input, // and let a validator handle it if it's not a string return new FromCloudFormationResult(value); } // won't always return a number; if the input can't be parsed to a number, // the input will be returned. static getNumber(value) { // if the string is a deploy-time value, serialize it to a Token if (token_1.isResolvableObject(value)) { return new FromCloudFormationResult(token_1.Token.asNumber(value)); } // return a number, if the input can be parsed as one if (typeof value === 'string') { const parsedValue = parseFloat(value); if (!isNaN(parsedValue)) { return new FromCloudFormationResult(parsedValue); } } // otherwise return the input, // and let a validator handle it if it's not a number return new FromCloudFormationResult(value); } static getStringArray(value) { // if the array is a deploy-time value, serialize it to a Token if (token_1.isResolvableObject(value)) { return new FromCloudFormationResult(token_1.Token.asList(value)); } // in all other cases, delegate to the standard mapping logic return this.getArray(this.getString)(value); } static getArray(mapper) { return (value) => { if (!Array.isArray(value)) { // break the type system, and just return the given value, // which hopefully will be reported as invalid by the validator // of the property we're transforming // (unless it's a deploy-time value, // which we can't map over at build time anyway) return new FromCloudFormationResult(value); } const values = new Array(); const ret = new FromCloudFormationResult(values); for (let i = 0; i < value.length; i++) { const result = mapper(value[i]); values.push(result.value); ret.appendExtraProperties(`${i}`, result.extraProperties); } return ret; }; } static getMap(mapper) { return (value) => { if (typeof value !== 'object') { // if the input is not a map (= object in JS land), // just return it, and let the validator of this property handle it // (unless it's a deploy-time value, // which we can't map over at build time anyway) return new FromCloudFormationResult(value); } const values = {}; const ret = new FromCloudFormationResult(values); for (const [key, val] of Object.entries(value)) { const result = mapper(val); values[key] = result.value; ret.appendExtraProperties(key, result.extraProperties); } return ret; }; } static getCfnTag(tag) { return tag == null ? new FromCloudFormationResult({}) // break the type system - this should be detected at runtime by a tag validator : new FromCloudFormationResult({ key: tag.Key, value: tag.Value, }); } /** * Return a function that, when applied to a value, will return the first validly deserialized one */ static getTypeUnion(validators, mappers) { return (value) => { for (let i = 0; i < validators.length; i++) { const candidate = mappers[i](value); if (validators[i](candidate.value).isSuccess) { return candidate; } } // if nothing matches, just return the input unchanged, and let validators catch it return new FromCloudFormationResult(value); }; } } exports.FromCloudFormation = FromCloudFormation; /** * The context in which the parsing is taking place. * * Some fragments of CloudFormation templates behave differently than others * (for example, the 'Conditions' sections treats { "Condition": "NameOfCond" } * differently than the 'Resources' section). * This enum can be used to change the created {@link CfnParser} behavior, * based on the template context. */ var CfnParsingContext; (function (CfnParsingContext) { /** We're currently parsing the 'Conditions' section. */ CfnParsingContext[CfnParsingContext["CONDITIONS"] = 0] = "CONDITIONS"; /** We're currently parsing the 'Rules' section. */ CfnParsingContext[CfnParsingContext["RULES"] = 1] = "RULES"; })(CfnParsingContext = exports.CfnParsingContext || (exports.CfnParsingContext = {})); /** * This class contains methods for translating from a pure CFN value * (like a JS object { "Ref": "Bucket" }) * to a form CDK understands * (like Fn.ref('Bucket')). * * While this file not exported from the module * (to not make it part of the public API), * it is directly referenced in the generated L1 code, * so any renames of it need to be reflected in cfn2ts/codegen.ts as well. * */ class CfnParser { constructor(options) { this.options = options; } handleAttributes(resource, resourceAttributes, logicalId) { const cfnOptions = resource.cfnOptions; cfnOptions.creationPolicy = this.parseCreationPolicy(resourceAttributes.CreationPolicy); cfnOptions.updatePolicy = this.parseUpdatePolicy(resourceAttributes.UpdatePolicy); cfnOptions.deletionPolicy = this.parseDeletionPolicy(resourceAttributes.DeletionPolicy); cfnOptions.updateReplacePolicy = this.parseDeletionPolicy(resourceAttributes.UpdateReplacePolicy); cfnOptions.version = this.parseValue(resourceAttributes.Version); cfnOptions.description = this.parseValue(resourceAttributes.Description); cfnOptions.metadata = this.parseValue(resourceAttributes.Metadata); // handle Condition if (resourceAttributes.Condition) { const condition = this.finder.findCondition(resourceAttributes.Condition); if (!condition) { throw new Error(`Resource '${logicalId}' uses Condition '${resourceAttributes.Condition}' that doesn't exist`); } cfnOptions.condition = condition; } // handle DependsOn resourceAttributes.DependsOn = resourceAttributes.DependsOn ?? []; const dependencies = Array.isArray(resourceAttributes.DependsOn) ? resourceAttributes.DependsOn : [resourceAttributes.DependsOn]; for (const dep of dependencies) { const depResource = this.finder.findResource(dep); if (!depResource) { throw new Error(`Resource '${logicalId}' depends on '${dep}' that doesn't exist`); } resource.node.addDependency(depResource); } } parseCreationPolicy(policy) { if (typeof policy !== 'object') { return undefined; } // change simple JS values to their CDK equivalents policy = this.parseValue(policy); return util_1.undefinedIfAllValuesAreEmpty({ autoScalingCreationPolicy: parseAutoScalingCreationPolicy(policy.AutoScalingCreationPolicy), resourceSignal: parseResourceSignal(policy.ResourceSignal), }); function parseAutoScalingCreationPolicy(p) { if (typeof p !== 'object') { return undefined; } return util_1.undefinedIfAllValuesAreEmpty({ minSuccessfulInstancesPercent: FromCloudFormation.getNumber(p.MinSuccessfulInstancesPercent).value, }); } function parseResourceSignal(p) { if (typeof p !== 'object') { return undefined; } return util_1.undefinedIfAllValuesAreEmpty({ count: FromCloudFormation.getNumber(p.Count).value, timeout: FromCloudFormation.getString(p.Timeout).value, }); } } parseUpdatePolicy(policy) { if (typeof policy !== 'object') { return undefined; } // change simple JS values to their CDK equivalents policy = this.parseValue(policy); return util_1.undefinedIfAllValuesAreEmpty({ autoScalingReplacingUpdate: parseAutoScalingReplacingUpdate(policy.AutoScalingReplacingUpdate), autoScalingRollingUpdate: parseAutoScalingRollingUpdate(policy.AutoScalingRollingUpdate), autoScalingScheduledAction: parseAutoScalingScheduledAction(policy.AutoScalingScheduledAction), codeDeployLambdaAliasUpdate: parseCodeDeployLambdaAliasUpdate(policy.CodeDeployLambdaAliasUpdate), enableVersionUpgrade: FromCloudFormation.getBoolean(policy.EnableVersionUpgrade).value, useOnlineResharding: FromCloudFormation.getBoolean(policy.UseOnlineResharding).value, }); function parseAutoScalingReplacingUpdate(p) { if (typeof p !== 'object') { return undefined; } return util_1.undefinedIfAllValuesAreEmpty({ willReplace: p.WillReplace, }); } function parseAutoScalingRollingUpdate(p) { if (typeof p !== 'object') { return undefined; } return util_1.undefinedIfAllValuesAreEmpty({ maxBatchSize: FromCloudFormation.getNumber(p.MaxBatchSize).value, minInstancesInService: FromCloudFormation.getNumber(p.MinInstancesInService).value, minSuccessfulInstancesPercent: FromCloudFormation.getNumber(p.MinSuccessfulInstancesPercent).value, pauseTime: FromCloudFormation.getString(p.PauseTime).value, suspendProcesses: FromCloudFormation.getStringArray(p.SuspendProcesses).value, waitOnResourceSignals: FromCloudFormation.getBoolean(p.WaitOnResourceSignals).value, }); } function parseCodeDeployLambdaAliasUpdate(p) { if (typeof p !== 'object') { return undefined; } return { beforeAllowTrafficHook: FromCloudFormation.getString(p.BeforeAllowTrafficHook).value, afterAllowTrafficHook: FromCloudFormation.getString(p.AfterAllowTrafficHook).value, applicationName: FromCloudFormation.getString(p.ApplicationName).value, deploymentGroupName: FromCloudFormation.getString(p.DeploymentGroupName).value, }; } function parseAutoScalingScheduledAction(p) { if (typeof p !== 'object') { return undefined; } return util_1.undefinedIfAllValuesAreEmpty({ ignoreUnmodifiedGroupSizeProperties: FromCloudFormation.getBoolean(p.IgnoreUnmodifiedGroupSizeProperties).value, }); } } parseDeletionPolicy(policy) { switch (policy) { case null: return undefined; case undefined: return undefined; case 'Delete': return cfn_resource_policy_1.CfnDeletionPolicy.DELETE; case 'Retain': return cfn_resource_policy_1.CfnDeletionPolicy.RETAIN; case 'Snapshot': return cfn_resource_policy_1.CfnDeletionPolicy.SNAPSHOT; default: throw new Error(`Unrecognized DeletionPolicy '${policy}'`); } } parseValue(cfnValue) { // == null captures undefined as well if (cfnValue == null) { return undefined; } // if we have any late-bound values, // just return them if (token_1.isResolvableObject(cfnValue)) { return cfnValue; } if (Array.isArray(cfnValue)) { return cfnValue.map(el => this.parseValue(el)); } if (typeof cfnValue === 'object') { // an object can be either a CFN intrinsic, or an actual object const cfnIntrinsic = this.parseIfCfnIntrinsic(cfnValue); if (cfnIntrinsic !== undefined) { return cfnIntrinsic; } const ret = {}; for (const [key, val] of Object.entries(cfnValue)) { ret[key] = this.parseValue(val); } return ret; } // in all other cases, just return the input return cfnValue; } get finder() { return this.options.finder; } parseIfCfnIntrinsic(object) { const key = this.looksLikeCfnIntrinsic(object); switch (key) { case undefined: return undefined; case 'Ref': { const refTarget = object[key]; const specialRef = this.specialCaseRefs(refTarget); if (specialRef !== undefined) { return specialRef; } else { const refElement = this.finder.findRefTarget(refTarget); if (!refElement) { throw new Error(`Element used in Ref expression with logical ID: '${refTarget}' not found`); } return cfn_reference_1.CfnReference.for(refElement, 'Ref'); } } case 'Fn::GetAtt': { const value = object[key]; let logicalId, attributeName, stringForm; // Fn::GetAtt takes as arguments either a string... if (typeof value === 'string') { // ...in which case the logical ID and the attribute name are separated with '.' const dotIndex = value.indexOf('.'); if (dotIndex === -1) { throw new Error(`Short-form Fn::GetAtt must contain a '.' in its string argument, got: '${value}'`); } logicalId = value.slice(0, dotIndex); attributeName = value.slice(dotIndex + 1); // the +1 is to skip the actual '.' stringForm = true; } else { // ...or a 2-element list logicalId = value[0]; attributeName = value[1]; stringForm = false; } const target = this.finder.findResource(logicalId); if (!target) { throw new Error(`Resource used in GetAtt expression with logical ID: '${logicalId}' not found`); } return cfn_reference_1.CfnReference.for(target, attributeName, stringForm ? cfn_reference_1.ReferenceRendering.GET_ATT_STRING : undefined); } case 'Fn::Join': { // Fn::Join takes a 2-element list as its argument, // where the first element is the delimiter, // and the second is the list of elements to join const value = this.parseValue(object[key]); // wrap the array as a Token, // as otherwise Fn.join() will try to concatenate // the non-token parts, // causing a diff with the original template return cfn_fn_1.Fn.join(value[0], lazy_1.Lazy.list({ produce: () => value[1] })); } case 'Fn::Cidr': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.cidr(value[0], value[1], value[2]); } case 'Fn::FindInMap': { const value = this.parseValue(object[key]); // the first argument to FindInMap is the mapping name let mappingName; if (token_1.Token.isUnresolved(value[0])) { // the first argument can be a dynamic expression like Ref: Param; // if it is, we can't find the mapping in advance mappingName = value[0]; } else { const mapping = this.finder.findMapping(value[0]); if (!mapping) { throw new Error(`Mapping used in FindInMap expression with name '${value[0]}' was not found in the template`); } mappingName = mapping.logicalId; } return cfn_fn_1.Fn._findInMap(mappingName, value[1], value[2]); } case 'Fn::Select': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.select(value[0], value[1]); } case 'Fn::GetAZs': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.getAzs(value); } case 'Fn::ImportValue': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.importValue(value); } case 'Fn::Split': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.split(value[0], value[1]); } case 'Fn::Transform': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.transform(value.Name, value.Parameters); } case 'Fn::Base64': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.base64(value); } case 'Fn::If': { // Fn::If takes a 3-element list as its argument, // where the first element is the name of a Condition const value = this.parseValue(object[key]); const condition = this.finder.findCondition(value[0]); if (!condition) { throw new Error(`Condition '${value[0]}' used in an Fn::If expression does not exist in the template`); } return cfn_fn_1.Fn.conditionIf(condition.logicalId, value[1], value[2]); } case 'Fn::Equals': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.conditionEquals(value[0], value[1]); } case 'Fn::And': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.conditionAnd(...value); } case 'Fn::Not': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.conditionNot(value[0]); } case 'Fn::Or': { const value = this.parseValue(object[key]); return cfn_fn_1.Fn.conditionOr(...value); } case 'Fn::Sub': { const value = this.parseValue(object[key]); let fnSubString; let map; if (typeof value === 'string') { fnSubString = value; map = undefined; } else { fnSubString = value[0]; map = value[1]; } return this.parseFnSubString(fnSubString, map); } case 'Condition': { // a reference to a Condition from another Condition const condition = this.finder.findCondition(object[key]); if (!condition) { throw new Error(`Referenced Condition with name '${object[key]}' was not found in the template`); } return { Condition: condition.logicalId }; } default: if (this.options.context === CfnParsingContext.RULES) { return this.handleRulesIntrinsic(key, object); } else { throw new Error(`Unsupported CloudFormation function '${key}'`); } } } looksLikeCfnIntrinsic(object) { const objectKeys = Object.keys(object); // a CFN intrinsic is always an object with a single key if (objectKeys.length !== 1) { return undefined; } const key = objectKeys[0]; return key === 'Ref' || key.startsWith('Fn::') || // special intrinsic only available in the 'Conditions' section (this.options.context === CfnParsingContext.CONDITIONS && key === 'Condition') ? key : undefined; } parseFnSubString(templateString, expressionMap) { const map = expressionMap ?? {}; const self = this; return cfn_fn_1.Fn.sub(go(templateString), Object.keys(map).length === 0 ? expressionMap : map); function go(value) { const leftBrace = value.indexOf('${'); if (leftBrace === -1) { return value; } // search for the closing brace to the right of the opening '${' // (in theory, there could be other braces in the string, // for example if it represents a JSON object) const rightBrace = value.indexOf('}', leftBrace); if (rightBrace === -1) { return value; } const leftHalf = value.substring(0, leftBrace); const rightHalf = value.substring(rightBrace + 1); // don't include left and right braces when searching for the target of the reference const refTarget = value.substring(leftBrace + 2, rightBrace).trim(); if (refTarget[0] === '!') { return value.substring(0, rightBrace + 1) + go(rightHalf); } // lookup in map if (refTarget in map) { return leftHalf + '${' + refTarget + '}' + go(rightHalf); } // since it's not in the map, check if it's a pseudo-parameter // (or a value to be substituted for a Parameter, provided by the customer) const specialRef = self.specialCaseSubRefs(refTarget); if (specialRef !== undefined) { if (token_1.Token.isUnresolved(specialRef)) { // specialRef can only be a Token if the value passed by the customer // for substituting a Parameter was a Token. // This is actually bad here, // because the Token can potentially be something that doesn't render // well inside an Fn::Sub template string, like a { Ref } object. // To handle this case, // instead of substituting the Parameter directly with the token in the template string, // add a new entry to the Fn::Sub map, // with key refTarget, and the token as the value. // This is safe, because this sort of shadowing is legal in CloudFormation, // and also because we're certain the Fn::Sub map doesn't contain an entry for refTarget // (as we check that condition in the code right above this). map[refTarget] = specialRef; return leftHalf + '${' + refTarget + '}' + go(rightHalf); } else { return leftHalf + specialRef + go(rightHalf); } } const dotIndex = refTarget.indexOf('.'); const isRef = dotIndex === -1; if (isRef) { const refElement = self.finder.findRefTarget(refTarget); if (!refElement) { throw new Error(`Element referenced in Fn::Sub expression with logical ID: '${refTarget}' was not found in the template`); } return leftHalf + cfn_reference_1.CfnReference.for(refElement, 'Ref', cfn_reference_1.ReferenceRendering.FN_SUB).toString() + go(rightHalf); } else { const targetId = refTarget.substring(0, dotIndex); const refResource = self.finder.findResource(targetId); if (!refResource) { throw new Error(`Resource referenced in Fn::Sub expression with logical ID: '${targetId}' was not found in the template`); } const attribute = refTarget.substring(dotIndex + 1); return leftHalf + cfn_reference_1.CfnReference.for(refResource, attribute, cfn_reference_1.ReferenceRendering.FN_SUB).toString() + go(rightHalf); } } } handleRulesIntrinsic(key, object) { // Rules have their own set of intrinsics: // https://docs.aws.amazon.com/servicecatalog/latest/adminguide/intrinsic-function-reference-rules.html switch (key) { case 'Fn::ValueOf': { // ValueOf is special, // as it takes the name of a Parameter as its first argument const value = this.parseValue(object[key]); const parameterName = value[0]; if (parameterName in this.parameters) { // since ValueOf returns the value of a specific attribute, // fail here - this substitution is not allowed throw new Error(`Cannot substitute parameter '${parameterName}' used in Fn::ValueOf expression with attribute '${value[1]}'`); } const param = this.finder.findRefTarget(parameterName); if (!param) { throw new Error(`Rule references parameter '${parameterName}' which was not found in the template`); } // create an explicit IResolvable, // as Fn.valueOf() returns a string, // which is incorrect // (Fn::ValueOf can also return an array) return lazy_1.Lazy.any({ produce: () => ({ 'Fn::ValueOf': [param.logicalId, value[1]] }) }); } default: // I don't want to hard-code the list of supported Rules-specific intrinsics in this function; // so, just return undefined here, // and they will be treated as a regular JSON object return undefined; } } specialCaseRefs(value) { if (value in this.parameters) { return this.parameters[value]; } switch (value) { case 'AWS::AccountId': return cfn_pseudo_1.Aws.ACCOUNT_ID; case 'AWS::Region': return cfn_pseudo_1.Aws.REGION; case 'AWS::Partition': return cfn_pseudo_1.Aws.PARTITION; case 'AWS::URLSuffix': return cfn_pseudo_1.Aws.URL_SUFFIX; case 'AWS::NotificationARNs': return cfn_pseudo_1.Aws.NOTIFICATION_ARNS; case 'AWS::StackId': return cfn_pseudo_1.Aws.STACK_ID; case 'AWS::StackName': return cfn_pseudo_1.Aws.STACK_NAME; case 'AWS::NoValue': return cfn_pseudo_1.Aws.NO_VALUE; default: return undefined; } } specialCaseSubRefs(value) { if (value in this.parameters) { return this.parameters[value]; } return value.indexOf('::') === -1 ? undefined : '${' + value + '}'; } get parameters() { return this.options.parameters || {}; } } exports.CfnParser = CfnParser; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2ZuLXBhcnNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY2ZuLXBhcnNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLHNDQUErQjtBQUUvQiw4Q0FBb0M7QUFFcEMsZ0VBR2dDO0FBRWhDLGtDQUErQjtBQUMvQiw0REFBNEU7QUFHNUUsb0NBQXFEO0FBQ3JELGtDQUF1RDtBQUV2RDs7Ozs7O0dBTUc7QUFDSCxNQUFhLHdCQUF3QjtJQUluQyxZQUFtQixLQUFRO1FBQ3pCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ25CLElBQUksQ0FBQyxlQUFlLEdBQUcsRUFBRSxDQUFDO0tBQzNCO0lBRU0scUJBQXFCLENBQUMsTUFBYyxFQUFFLFVBQThDO1FBQ3pGLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsSUFBSSxFQUFFLENBQUMsRUFBRTtZQUN6RCxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsTUFBTSxJQUFJLEdBQUcsRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDO1NBQ2hEO0tBQ0Y7Q0FDRjtBQWRELDREQWNDO0FBRUQ7O0dBRUc7QUFDSCxNQUFhLGdDQUFnRSxTQUFRLHdCQUEyQjtJQUc5RztRQUNFLEtBQUssQ0FBQyxFQUFTLENBQUMsQ0FBQyxDQUFDLDJCQUEyQjtRQUg5Qix5QkFBb0IsR0FBRyxJQUFJLEdBQUcsRUFBVSxDQUFDO0tBSXpEO0lBRUQ7O09BRUc7SUFDSSxpQkFBaUIsQ0FBQyxXQUFvQixFQUFFLFdBQW1CLEVBQUUsTUFBc0M7UUFDeEcsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUMzQyxJQUFJLENBQUMsTUFBTSxFQUFFO1lBQUUsT0FBTztTQUFFO1FBQ3hCLElBQUksQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQztRQUN2QyxJQUFJLENB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