cdk-monitoring-constructs
Version:
[](https://badge.fury.io/js/cdk-monitoring-constructs) [](https://m
361 lines • 57.7 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MetricFactory = exports.DefaultMetricPeriod = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const aws_cloudwatch_1 = require("aws-cdk-lib/aws-cloudwatch");
const AnomalyDetectionMathExpression_1 = require("./AnomalyDetectionMathExpression");
const MetricStatistic_1 = require("./MetricStatistic");
const RateComputationMethod_1 = require("./RateComputationMethod");
/**
* The most common default metric period used at Amazon is currently 5 minutes.
*/
exports.DefaultMetricPeriod = aws_cdk_lib_1.Duration.minutes(5);
class MetricFactory {
constructor(scope, props) {
this.globalDefaults = props?.globalDefaults ?? {};
this.scope = scope;
}
/**
* Factory method that creates a metric. The metric properties will already be updated to comply with the global defaults.
*
* @param metricName metric name
* @param statistic aggregation statistic to use
* @param label metric label; if undefined, metric name is used by CloudWatch
* @param dimensionsMap additional dimensions to be added
* @param color metric color; if undefined, uses a CloudWatch provided color (preferred)
* @param namespace specify a custom namespace; if undefined, uses the global default
* @param period specify a custom period; if undefined, uses the global default
* @param region specify a custom region; if undefined, uses the global default
* @param account specify a custom account; if undefined, uses the global default
*/
createMetric(metricName, statistic, label, dimensionsMap, color, namespace, period, region, account) {
return new aws_cloudwatch_1.Metric({
statistic,
metricName,
label,
color,
dimensionsMap: dimensionsMap
? this.removeUndefinedEntries(dimensionsMap)
: undefined,
namespace: this.getNamespaceWithFallback(namespace),
period: period ?? this.globalDefaults.period ?? exports.DefaultMetricPeriod,
region: this.resolveRegion(region ?? this.globalDefaults.region),
account: this.resolveAccount(account ?? this.globalDefaults.account),
});
}
/**
* Factory method that creates a metric math expression. The metric properties will already be updated to comply with the global defaults.
*
* @param expression CloudWatch metric math expression (https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/using-metric-math.html)
* @param usingMetrics map of metrics, where keys are expression IDs (used in the expression) and values are metrics
* @param label metric label (required, as there is no reasonable default)
* @param color metric color; if undefined, uses a CloudWatch provided color (preferred)
* @param period specify a custom period; if undefined, uses the global default
* @param region specify a custom region; if undefined, uses the global default
* @param account specify a custom account; if undefined, uses the global default
*/
createMetricMath(expression, usingMetrics, label, color, period, region, account) {
return new aws_cloudwatch_1.MathExpression({
label,
color,
expression,
usingMetrics,
period: period ?? this.globalDefaults.period ?? exports.DefaultMetricPeriod,
searchRegion: this.resolveRegion(region ?? this.globalDefaults.region),
searchAccount: this.resolveAccount(account ?? this.globalDefaults.account),
});
}
/**
* Factory method that creates a metric search query. The metric properties will already be updated to comply with the global defaults.
* If you want to match "any value" of a specific dimension, please use `undefined` value representation in your consumer language.
* (For example, `undefined as any as string` in TypeScript, due to JSII typing quirks.)
*
* @param query metric search query (the same as the search query prompt in CloudWatch AWS Console), it might also be empty
* @param dimensionsMap dimensions, further narrowing the search results; use `undefined` if you want to represent "any value" (in TS: `undefined as any as string`)
* @param statistic aggregation statistic to use
* @param namespace specify a custom namespace; if undefined, uses the global default
* @param label specify custom label for search metrics; default is " " as it cannot be empty string
* @param period specify a custom period; if undefined, uses the global default
* @param region specify a custom region; if undefined, uses the global default
* @param account specify a custom account; if undefined, uses the global default
*/
createMetricSearch(query, dimensionsMap, statistic, namespace, label, period, region, account) {
const finalPeriod = period ?? this.globalDefaults.period ?? exports.DefaultMetricPeriod;
const searchNamespace = this.getNamespaceWithFallback(namespace);
const namespacesWithQuotations = searchNamespace
.split(",")
.map((namespace) => `"${namespace.trim()}"`);
const keysWithQuotations = Object.keys(dimensionsMap).map((key) => `"${key}"`);
const namespacePlusDimensionKeys = [
...namespacesWithQuotations,
...keysWithQuotations,
].join(",");
const metricSchema = `{${namespacePlusDimensionKeys}}`;
const dimensionKeysAndValues = Object.entries(this.removeUndefinedEntries(dimensionsMap))
.map(([key, value]) => `"${key}"="${value}"`)
.join(" ");
const expression = `SEARCH('${metricSchema} ${dimensionKeysAndValues} ${query}', '${statistic}', ${finalPeriod.toSeconds()})`;
return new aws_cloudwatch_1.MathExpression({
expression,
// see https://github.com/aws/aws-cdk/issues/7237
usingMetrics: {},
// cannot be an empty string and undefined is no good either
label: label ?? " ",
period: finalPeriod,
searchRegion: this.resolveRegion(region ?? this.globalDefaults.region),
searchAccount: this.resolveAccount(account ?? this.globalDefaults.account),
});
}
/**
* Factory method that creates anomaly detection on a metric.
* Anomaly occurs whenever a metric value falls outside of a precomputed range of predicted values.
* The detection does not need any setup. The model will start learning automatically and should be ready in a few minutes.
* Usually, the anomaly detection is paired with an alarm.
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Anomaly_Detection.html
*
* @param metric metric to detect anomaly detection of
* @param stdev standard deviation, basically the tolerance / band thickness
* @param label metric label (required, as there is no reasonable default)
* @param color metric color; if undefined, uses a CloudWatch provided color (preferred)
* @param expressionId expression ID of the metric; uses `m1` if undefined
* @param period specify a custom period; if undefined, uses the global default
* @param region specify a custom region; if undefined, uses the global default
* @param account specify a custom account; if undefined, uses the global default
*/
createMetricAnomalyDetection(metric, stdev, label, color, expressionId, period, region, account) {
const finalExpressionId = expressionId ?? "m1";
const usingMetrics = {};
usingMetrics[finalExpressionId] = metric;
return new AnomalyDetectionMathExpression_1.AnomalyDetectionMathExpression({
label,
color,
usingMetrics,
expression: `ANOMALY_DETECTION_BAND(${finalExpressionId},${stdev})`,
period: period ?? this.globalDefaults.period ?? exports.DefaultMetricPeriod,
searchRegion: this.resolveRegion(region ?? this.globalDefaults.region),
searchAccount: this.resolveAccount(account ?? this.globalDefaults.account),
});
}
/**
* Adapts properties of a foreign metric (metric created outside of this metric factory) to comply with the global defaults.
* Might modify namespace and metric period.
*
* @param metric metric to be adapted
*/
adaptMetric(metric) {
if (this.globalDefaults.region) {
metric = metric.with({ region: this.globalDefaults.region });
}
if (this.globalDefaults.account) {
metric = metric.with({ account: this.globalDefaults.account });
}
return metric.with({
period: this.globalDefaults.period ?? exports.DefaultMetricPeriod,
});
}
/**
* Adapts properties of a foreign metric (metric created outside of this metric factory) to comply with the global defaults.
* Might modify namespace. Preserves metric period.
*
* @param metric metric to be adapted
*/
adaptMetricPreservingPeriod(metric) {
return metric;
}
/**
* Creates a metric math expression that multiplies the given metric by given coefficient.
* Does nothing if the multiplier is one. Preserves the metric period.
*
* @param metric metric to multiply
* @param multiplier multiplier (must be > 1)
* @param label expression label
* @param expressionId expression ID of the metric; uses `m1` if undefined
*/
multiplyMetric(metric, multiplier, label, expressionId) {
if (multiplier == 1) {
return metric;
}
else if (multiplier < 1) {
throw new Error("Multiplier must be greater than one.");
}
else {
const finalExpressionId = expressionId ?? "m1";
const usingMetrics = {};
usingMetrics[finalExpressionId] = metric;
return this.createMetricMath(`${finalExpressionId} * ${multiplier}`, usingMetrics, label, metric.color, metric.period, this.getRegion(metric), this.getAccount(metric));
}
}
/**
* Creates a metric math expression that divides the given metric by given coefficient.
* Does nothing if the divisor is one. Preserves the metric period.
*
* @param metric metric to multiply
* @param divisor divisor (must be > 1)
* @param label expression label
* @param expressionId expression ID of the metric; uses `m1` if undefined
*/
divideMetric(metric, divisor, label, expressionId) {
if (divisor == 1) {
return metric;
}
else if (divisor < 1) {
throw new Error("Divisor must be greater than one.");
}
else {
const finalExpressionId = expressionId ?? "m1";
const usingMetrics = {};
usingMetrics[finalExpressionId] = metric;
return this.createMetricMath(`${finalExpressionId} / ${divisor}`, usingMetrics, label, metric.color, metric.period, this.getRegion(metric), this.getAccount(metric));
}
}
/**
* Creates a metric math expression that computes a rate from a regular metric.
* For example, it allows you to compute rate per second (TPS), per minute, or just an average of your transactions.
*
* @param metric metric to calculate the rate from
* @param method rate computation method
* @param addStatsToLabel add detailed statistics (min, max, average) to the label
* @param expressionId expression ID of the metric; uses `m1` if undefined
* @param fillWithZeroes if TRUE, the final metric will be zero-filled (0 on no data); false if undefined
*/
toRate(metric, method, addStatsToLabel, expressionId, fillWithZeroes) {
const finalExpressionId = expressionId ?? "m1";
const labelPrefix = metric.label ?? "Rate";
const statsInLabel = [];
if (addStatsToLabel ?? false) {
statsInLabel.push("min: ${MIN}");
statsInLabel.push("max: ${MAX}");
if (method !== RateComputationMethod_1.RateComputationMethod.AVERAGE) {
// only add average if do not have it already
statsInLabel.push("avg: ${AVG}");
}
}
const finalExpressionIdZeroed = fillWithZeroes ?? false
? `FILL(${finalExpressionId},0)`
: finalExpressionId;
const labelAppendix = statsInLabel.length > 0 ? ` (${statsInLabel.join(", ")})` : "";
switch (method) {
case RateComputationMethod_1.RateComputationMethod.AVERAGE:
const avgLabel = `${labelPrefix} (avg)${labelAppendix}`;
const avgMetric = metric.with({
label: avgLabel,
statistic: MetricStatistic_1.MetricStatistic.AVERAGE,
});
if (fillWithZeroes ?? false) {
return this.createMetricMath(finalExpressionIdZeroed, { [finalExpressionId]: avgMetric }, avgLabel, avgMetric.color, avgMetric.period, this.getRegion(avgMetric), this.getAccount(avgMetric));
}
return avgMetric;
case RateComputationMethod_1.RateComputationMethod.PER_SECOND:
let perSecondLabel = `${labelPrefix}/s${labelAppendix}`;
if (labelPrefix === "Requests" ||
labelPrefix === "Invocations" ||
labelPrefix === "Transactions") {
// currently, kept as "TPS" to reduce number of snapshot changes
perSecondLabel = `TPS${labelAppendix}`;
}
return this.createMetricMath(`${finalExpressionIdZeroed} / PERIOD(${finalExpressionId})`, { [finalExpressionId]: metric }, perSecondLabel, metric.color, metric.period, this.getRegion(metric), this.getAccount(metric));
case RateComputationMethod_1.RateComputationMethod.PER_MINUTE:
return this.createMetricMath(`(60 * ${finalExpressionIdZeroed}) / PERIOD(${finalExpressionId})`, { [finalExpressionId]: metric }, `${labelPrefix}/m${labelAppendix}`, metric.color, metric.period, this.getRegion(metric), this.getAccount(metric));
case RateComputationMethod_1.RateComputationMethod.PER_HOUR:
return this.createMetricMath(`(3600 * ${finalExpressionIdZeroed}) / PERIOD(${finalExpressionId})`, { [finalExpressionId]: metric }, `${labelPrefix}/h${labelAppendix}`, metric.color, metric.period, this.getRegion(metric), this.getAccount(metric));
case RateComputationMethod_1.RateComputationMethod.PER_DAY:
return this.createMetricMath(`(86400 * ${finalExpressionIdZeroed}) / PERIOD(${finalExpressionId})`, { [finalExpressionId]: metric }, `${labelPrefix}/d${labelAppendix}`, metric.color, metric.period, this.getRegion(metric), this.getAccount(metric));
}
}
/**
* Returns the given namespace (if defined) or the global namespace as a fallback.
* If there is no namespace to fallback to (neither the custom or the default one), it will fail.
* @param value custom namespace
*/
getNamespaceWithFallback(value) {
const namespace = value ?? this.globalDefaults.namespace;
if (!namespace) {
throw new Error("There is no custom namespace defined. Please specify it in your factory defaults.");
}
return namespace;
}
/**
* Helper method that helps to sanitize the given expression ID and removes all invalid characters.
* Valid expression ID regexp is the following: ^[a-z][a-zA-Z0-9_]*$
* As this is just to validate a suffix and not the whole ID, we do not have to verify the first lower case letter.
* @param expressionId expression ID to sanitize
*/
sanitizeMetricExpressionIdSuffix(expressionId) {
return expressionId.replace(/[^0-9a-z_]/gi, "");
}
/**
* Merges the given additional dimensions to the given target dimension hash.
* All existing dimensions with the same key are replaced.
* @param target target dimension hash to update
* @param additionalDimensions additional dimensions
*/
addAdditionalDimensions(target, additionalDimensions) {
// Add additional dimensions in the search query
Object.keys(additionalDimensions).forEach((key) => {
target[key] = additionalDimensions[key];
});
}
/**
* Removes all entries from the given dimension hash that contain an undefined value.
* @param dimensionsMap dimensions map to update
*/
removeUndefinedEntries(dimensionsMap) {
const copy = {};
Object.entries(dimensionsMap)
.filter(([_, value]) => value !== undefined)
.forEach(([key, value]) => (copy[key] = value));
return copy;
}
/**
* Attempts to get the account from the metric if it differs from the scope.
*/
resolveAccount(metricAccount) {
if (!this.scope) {
return metricAccount;
}
const { account } = aws_cdk_lib_1.Stack.of(this.scope);
if (metricAccount !== account) {
return metricAccount;
}
return;
}
getAccount(metric) {
let metricAccount;
if (metric instanceof aws_cloudwatch_1.MathExpression) {
metricAccount = metric.searchAccount;
}
else {
metricAccount = metric.account;
}
return this.resolveAccount(metricAccount);
}
/**
* Attempts to get the region from the metric if it differs from the scope.
*/
resolveRegion(metricRegion) {
if (!this.scope) {
return metricRegion;
}
const { region } = aws_cdk_lib_1.Stack.of(this.scope);
if (metricRegion !== region) {
return metricRegion;
}
return;
}
getRegion(metric) {
let metricRegion;
if (metric instanceof aws_cloudwatch_1.MathExpression) {
metricRegion = metric.searchRegion;
}
else {
metricRegion = metric.region;
}
return this.resolveRegion(metricRegion);
}
}
exports.MetricFactory = MetricFactory;
_a = JSII_RTTI_SYMBOL_1;
MetricFactory[_a] = { fqn: "cdk-monitoring-constructs.MetricFactory", version: "9.7.1" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWV0cmljRmFjdG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIk1ldHJpY0ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw2Q0FBOEM7QUFDOUMsK0RBS29DO0FBR3BDLHFGQUFrRjtBQUVsRix1REFBb0Q7QUFFcEQsbUVBQWdFO0FBRWhFOztHQUVHO0FBQ1UsUUFBQSxtQkFBbUIsR0FBRyxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztBQXlCdkQsTUFBYSxhQUFhO0lBSXhCLFlBQVksS0FBZ0IsRUFBRSxLQUEwQjtRQUN0RCxJQUFJLENBQUMsY0FBYyxHQUFHLEtBQUssRUFBRSxjQUFjLElBQUksRUFBRSxDQUFDO1FBQ2xELElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCxZQUFZLENBQ1YsVUFBa0IsRUFDbEIsU0FBMEIsRUFDMUIsS0FBYyxFQUNkLGFBQTZCLEVBQzdCLEtBQWMsRUFDZCxTQUFrQixFQUNsQixNQUFpQixFQUNqQixNQUFlLEVBQ2YsT0FBZ0I7UUFFaEIsT0FBTyxJQUFJLHVCQUFNLENBQUM7WUFDaEIsU0FBUztZQUNULFVBQVU7WUFDVixLQUFLO1lBQ0wsS0FBSztZQUNMLGFBQWEsRUFBRSxhQUFhO2dCQUMxQixDQUFDLENBQUMsSUFBSSxDQUFDLHNCQUFzQixDQUFDLGFBQWEsQ0FBQztnQkFDNUMsQ0FBQyxDQUFDLFNBQVM7WUFDYixTQUFTLEVBQUUsSUFBSSxDQUFDLHdCQUF3QixDQUFDLFNBQVMsQ0FBQztZQUNuRCxNQUFNLEVBQUUsTUFBTSxJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxJQUFJLDJCQUFtQjtZQUNuRSxNQUFNLEVBQUUsSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLElBQUksSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUM7WUFDaEUsT0FBTyxFQUFFLElBQUksQ0FBQyxj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WUFDTCxZQUFZLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztTQUM5QjtRQUVELE9BQU8sSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUMxQyxDQUFDOztBQXJmSCxzQ0FzZkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBEdXJhdGlvbiwgU3RhY2sgfSBmcm9tIFwiYXdzLWNkay1saWJcIjtcbmltcG9ydCB7XG4gIERpbWVuc2lvbnNNYXAsXG4gIElNZXRyaWMsXG4gIE1hdGhFeHByZXNzaW9uLFxuICBNZXRyaWMsXG59IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtY2xvdWR3YXRjaFwiO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSBcImNvbnN0cnVjdHNcIjtcblxuaW1wb3J0IHsgQW5vbWFseURldGVjdGlvbk1hdGhFeHByZXNzaW9uIH0gZnJvbSBcIi4vQW5vbWFseURldGVjdGlvbk1hdGhFeHByZXNzaW9uXCI7XG5pbXBvcnQgeyBCYXNlTWV0cmljRmFjdG9yeVByb3BzIH0gZnJvbSBcIi4vQmFzZU1ldHJpY0ZhY3RvcnlcIjtcbmltcG9ydCB7IE1ldHJpY1N0YXRpc3RpYyB9IGZyb20gXCIuL01ldHJpY1N0YXRpc3RpY1wiO1xuaW1wb3J0IHsgTWV0cmljV2l0aEFsYXJtU3VwcG9ydCB9IGZyb20gXCIuL01ldHJpY1dpdGhBbGFybVN1cHBvcnRcIjtcbmltcG9ydCB7IFJhdGVDb21wdXRhdGlvbk1ldGhvZCB9IGZyb20gXCIuL1JhdGVDb21wdXRhdGlvbk1ldGhvZFwiO1xuXG4vKipcbiAqIFRoZSBtb3N0IGNvbW1vbiBkZWZhdWx0IG1ldHJpYyBwZXJpb2QgdXNlZCBhdCBBbWF6b24gaXMgY3VycmVudGx5IDUgbWludXRlcy5cbiAqL1xuZXhwb3J0IGNvbnN0IERlZmF1bHRNZXRyaWNQZXJpb2QgPSBEdXJhdGlvbi5taW51dGVzKDUpO1xuXG4vKipcbiAqIFRoZXNlIGFyZSB0aGUgZ2xvYmFscyB1c2VkIGZvciBlYWNoIG1ldHJpYywgdW5sZXNzIHRoZXJlIGlzIHNvbWUga2luZCBvZiBvdmVycmlkZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBNZXRyaWNGYWN0b3J5RGVmYXVsdHMgZXh0ZW5kcyBCYXNlTWV0cmljRmFjdG9yeVByb3BzIHtcbiAgLyoqXG4gICAqIEVhY2ggbWV0cmljIGV4aXN0cyBpbiBhIG5hbWVzcGFjZS4gQVdTIFNlcnZpY2VzIGhhdmUgdGhlaXIgb3duIG5hbWVzcGFjZSwgYnV0IGhlcmUgeW91IGNhbiBzcGVjaWZ5IHlvdXIgY3VzdG9tIG9uZS5cbiAgICovXG4gIHJlYWRvbmx5IG5hbWVzcGFjZT86IHN0cmluZztcblxuICAvKipcbiAgICogTWV0cmljIHBlcmlvZC4gRGVmYXVsdCB2YWx1ZSBpcyB1c2VkIGlmIG5vdCBkZWZpbmVkLlxuICAgKiBAZGVmYXVsdCAtIERlZmF1bHRNZXRyaWNQZXJpb2RcbiAgICovXG4gIHJlYWRvbmx5IHBlcmlvZD86IER1cmF0aW9uO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1ldHJpY0ZhY3RvcnlQcm9wcyB7XG4gIC8qKlxuICAgKiBBbGxvd3MgeW91IHRvIHNwZWNpZnkgdGhlIGdsb2JhbCBkZWZhdWx0cywgd2hpY2ggY2FuIGJlIG92ZXJyaWRkZW4gaW4gdGhlIGluZGl2aWR1YWwgbWV0cmljcyBvciBhbGFybXMuXG4gICAqL1xuICByZWFkb25seSBnbG9iYWxEZWZhdWx0cz86IE1ldHJpY0ZhY3RvcnlEZWZhdWx0cztcbn1cblxuZXhwb3J0IGNsYXNzIE1ldHJpY0ZhY3Rvcnkge1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZ2xvYmFsRGVmYXVsdHM6IE1ldHJpY0ZhY3RvcnlEZWZhdWx0cztcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHNjb3BlOiBDb25zdHJ1Y3QgfCB1bmRlZmluZWQ7XG5cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgcHJvcHM/OiBNZXRyaWNGYWN0b3J5UHJvcHMpIHtcbiAgICB0aGlzLmdsb2JhbERlZmF1bHRzID0gcHJvcHM/Lmdsb2JhbERlZmF1bHRzID8/IHt9O1xuICAgIHRoaXMuc2NvcGUgPSBzY29wZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGYWN0b3J5IG1ldGhvZCB0aGF0IGNyZWF0ZXMgYSBtZXRyaWMuIFRoZSBtZXRyaWMgcHJvcGVydGllcyB3aWxsIGFscmVhZHkgYmUgdXBkYXRlZCB0byBjb21wbHkgd2l0aCB0aGUgZ2xvYmFsIGRlZmF1bHRzLlxuICAgKlxuICAgKiBAcGFyYW0gbWV0cmljTmFtZSBtZXRyaWMgbmFtZVxuICAgKiBAcGFyYW0gc3RhdGlzdGljIGFnZ3JlZ2F0aW9uIHN0YXRpc3RpYyB0byB1c2VcbiAgICogQHBhcmFtIGxhYmVsIG1ldHJpYyBsYWJlbDsgaWYgdW5kZWZpbmVkLCBtZXRyaWMgbmFtZSBpcyB1c2VkIGJ5IENsb3VkV2F0Y2hcbiAgICogQHBhcmFtIGRpbWVuc2lvbnNNYXAgYWRkaXRpb25hbCBkaW1lbnNpb25zIHRvIGJlIGFkZGVkXG4gICAqIEBwYXJhbSBjb2xvciBtZXRyaWMgY29sb3I7IGlmIHVuZGVmaW5lZCwgdXNlcyBhIENsb3VkV2F0Y2ggcHJvdmlkZWQgY29sb3IgKHByZWZlcnJlZClcbiAgICogQHBhcmFtIG5hbWVzcGFjZSBzcGVjaWZ5IGEgY3VzdG9tIG5hbWVzcGFjZTsgaWYgdW5kZWZpbmVkLCB1c2VzIHRoZSBnbG9iYWwgZGVmYXVsdFxuICAgKiBAcGFyYW0gcGVyaW9kIHNwZWNpZnkgYSBjdXN0b20gcGVyaW9kOyBpZiB1bmRlZmluZWQsIHVzZXMgdGhlIGdsb2JhbCBkZWZhdWx0XG4gICAqIEBwYXJhbSByZWdpb24gc3BlY2lmeSBhIGN1c3RvbSByZWdpb247IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICogQHBhcmFtIGFjY291bnQgc3BlY2lmeSBhIGN1c3RvbSBhY2NvdW50OyBpZiB1bmRlZmluZWQsIHVzZXMgdGhlIGdsb2JhbCBkZWZhdWx0XG4gICAqL1xuICBjcmVhdGVNZXRyaWMoXG4gICAgbWV0cmljTmFtZTogc3RyaW5nLFxuICAgIHN0YXRpc3RpYzogTWV0cmljU3RhdGlzdGljLFxuICAgIGxhYmVsPzogc3RyaW5nLFxuICAgIGRpbWVuc2lvbnNNYXA/OiBEaW1lbnNpb25zTWFwLFxuICAgIGNvbG9yPzogc3RyaW5nLFxuICAgIG5hbWVzcGFjZT86IHN0cmluZyxcbiAgICBwZXJpb2Q/OiBEdXJhdGlvbixcbiAgICByZWdpb24/OiBzdHJpbmcsXG4gICAgYWNjb3VudD86IHN0cmluZyxcbiAgKTogTWV0cmljV2l0aEFsYXJtU3VwcG9ydCB7XG4gICAgcmV0dXJuIG5ldyBNZXRyaWMoe1xuICAgICAgc3RhdGlzdGljLFxuICAgICAgbWV0cmljTmFtZSxcbiAgICAgIGxhYmVsLFxuICAgICAgY29sb3IsXG4gICAgICBkaW1lbnNpb25zTWFwOiBkaW1lbnNpb25zTWFwXG4gICAgICAgID8gdGhpcy5yZW1vdmVVbmRlZmluZWRFbnRyaWVzKGRpbWVuc2lvbnNNYXApXG4gICAgICAgIDogdW5kZWZpbmVkLFxuICAgICAgbmFtZXNwYWNlOiB0aGlzLmdldE5hbWVzcGFjZVdpdGhGYWxsYmFjayhuYW1lc3BhY2UpLFxuICAgICAgcGVyaW9kOiBwZXJpb2QgPz8gdGhpcy5nbG9iYWxEZWZhdWx0cy5wZXJpb2QgPz8gRGVmYXVsdE1ldHJpY1BlcmlvZCxcbiAgICAgIHJlZ2lvbjogdGhpcy5yZXNvbHZlUmVnaW9uKHJlZ2lvbiA/PyB0aGlzLmdsb2JhbERlZmF1bHRzLnJlZ2lvbiksXG4gICAgICBhY2NvdW50OiB0aGlzLnJlc29sdmVBY2NvdW50KGFjY291bnQgPz8gdGhpcy5nbG9iYWxEZWZhdWx0cy5hY2NvdW50KSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGYWN0b3J5IG1ldGhvZCB0aGF0IGNyZWF0ZXMgYSBtZXRyaWMgbWF0aCBleHByZXNzaW9uLiBUaGUgbWV0cmljIHByb3BlcnRpZXMgd2lsbCBhbHJlYWR5IGJlIHVwZGF0ZWQgdG8gY29tcGx5IHdpdGggdGhlIGdsb2JhbCBkZWZhdWx0cy5cbiAgICpcbiAgICogQHBhcmFtIGV4cHJlc3Npb24gQ2xvdWRXYXRjaCBtZXRyaWMgbWF0aCBleHByZXNzaW9uIChodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vQW1hem9uQ2xvdWRXYXRjaC9sYXRlc3QvbW9uaXRvcmluZy91c2luZy1tZXRyaWMtbWF0aC5odG1sKVxuICAgKiBAcGFyYW0gdXNpbmdNZXRyaWNzIG1hcCBvZiBtZXRyaWNzLCB3aGVyZSBrZXlzIGFyZSBleHByZXNzaW9uIElEcyAodXNlZCBpbiB0aGUgZXhwcmVzc2lvbikgYW5kIHZhbHVlcyBhcmUgbWV0cmljc1xuICAgKiBAcGFyYW0gbGFiZWwgbWV0cmljIGxhYmVsIChyZXF1aXJlZCwgYXMgdGhlcmUgaXMgbm8gcmVhc29uYWJsZSBkZWZhdWx0KVxuICAgKiBAcGFyYW0gY29sb3IgbWV0cmljIGNvbG9yOyBpZiB1bmRlZmluZWQsIHVzZXMgYSBDbG91ZFdhdGNoIHByb3ZpZGVkIGNvbG9yIChwcmVmZXJyZWQpXG4gICAqIEBwYXJhbSBwZXJpb2Qgc3BlY2lmeSBhIGN1c3RvbSBwZXJpb2Q7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICogQHBhcmFtIHJlZ2lvbiBzcGVjaWZ5IGEgY3VzdG9tIHJlZ2lvbjsgaWYgdW5kZWZpbmVkLCB1c2VzIHRoZSBnbG9iYWwgZGVmYXVsdFxuICAgKiBAcGFyYW0gYWNjb3VudCBzcGVjaWZ5IGEgY3VzdG9tIGFjY291bnQ7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICovXG4gIGNyZWF0ZU1ldHJpY01hdGgoXG4gICAgZXhwcmVzc2lvbjogc3RyaW5nLFxuICAgIHVzaW5nTWV0cmljczogUmVjb3JkPHN0cmluZywgSU1ldHJpYz4sXG4gICAgbGFiZWw6IHN0cmluZyxcbiAgICBjb2xvcj86IHN0cmluZyxcbiAgICBwZXJpb2Q/OiBEdXJhdGlvbixcbiAgICByZWdpb24/OiBzdHJpbmcsXG4gICAgYWNjb3VudD86IHN0cmluZyxcbiAgKTogTWV0cmljV2l0aEFsYXJtU3VwcG9ydCB7XG4gICAgcmV0dXJuIG5ldyBNYXRoRXhwcmVzc2lvbih7XG4gICAgICBsYWJlbCxcbiAgICAgIGNvbG9yLFxuICAgICAgZXhwcmVzc2lvbixcbiAgICAgIHVzaW5nTWV0cmljcyxcbiAgICAgIHBlcmlvZDogcGVyaW9kID8/IHRoaXMuZ2xvYmFsRGVmYXVsdHMucGVyaW9kID8/IERlZmF1bHRNZXRyaWNQZXJpb2QsXG4gICAgICBzZWFyY2hSZWdpb246IHRoaXMucmVzb2x2ZVJlZ2lvbihyZWdpb24gPz8gdGhpcy5nbG9iYWxEZWZhdWx0cy5yZWdpb24pLFxuICAgICAgc2VhcmNoQWNjb3VudDogdGhpcy5yZXNvbHZlQWNjb3VudChcbiAgICAgICAgYWNjb3VudCA/PyB0aGlzLmdsb2JhbERlZmF1bHRzLmFjY291bnQsXG4gICAgICApLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEZhY3RvcnkgbWV0aG9kIHRoYXQgY3JlYXRlcyBhIG1ldHJpYyBzZWFyY2ggcXVlcnkuIFRoZSBtZXRyaWMgcHJvcGVydGllcyB3aWxsIGFscmVhZHkgYmUgdXBkYXRlZCB0byBjb21wbHkgd2l0aCB0aGUgZ2xvYmFsIGRlZmF1bHRzLlxuICAgKiBJZiB5b3Ugd2FudCB0byBtYXRjaCBcImFueSB2YWx1ZVwiIG9mIGEgc3BlY2lmaWMgZGltZW5zaW9uLCBwbGVhc2UgdXNlIGB1bmRlZmluZWRgIHZhbHVlIHJlcHJlc2VudGF0aW9uIGluIHlvdXIgY29uc3VtZXIgbGFuZ3VhZ2UuXG4gICAqIChGb3IgZXhhbXBsZSwgYHVuZGVmaW5lZCBhcyBhbnkgYXMgc3RyaW5nYCBpbiBUeXBlU2NyaXB0LCBkdWUgdG8gSlNJSSB0eXBpbmcgcXVpcmtzLilcbiAgICpcbiAgICogQHBhcmFtIHF1ZXJ5IG1ldHJpYyBzZWFyY2ggcXVlcnkgKHRoZSBzYW1lIGFzIHRoZSBzZWFyY2ggcXVlcnkgcHJvbXB0IGluIENsb3VkV2F0Y2ggQVdTIENvbnNvbGUpLCBpdCBtaWdodCBhbHNvIGJlIGVtcHR5XG4gICAqIEBwYXJhbSBkaW1lbnNpb25zTWFwIGRpbWVuc2lvbnMsIGZ1cnRoZXIgbmFycm93aW5nIHRoZSBzZWFyY2ggcmVzdWx0czsgdXNlIGB1bmRlZmluZWRgIGlmIHlvdSB3YW50IHRvIHJlcHJlc2VudCBcImFueSB2YWx1ZVwiIChpbiBUUzogYHVuZGVmaW5lZCBhcyBhbnkgYXMgc3RyaW5nYClcbiAgICogQHBhcmFtIHN0YXRpc3RpYyBhZ2dyZWdhdGlvbiBzdGF0aXN0aWMgdG8gdXNlXG4gICAqIEBwYXJhbSBuYW1lc3BhY2Ugc3BlY2lmeSBhIGN1c3RvbSBuYW1lc3BhY2U7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICogQHBhcmFtIGxhYmVsIHNwZWNpZnkgY3VzdG9tIGxhYmVsIGZvciBzZWFyY2ggbWV0cmljczsgZGVmYXVsdCBpcyBcIiBcIiBhcyBpdCBjYW5ub3QgYmUgZW1wdHkgc3RyaW5nXG4gICAqIEBwYXJhbSBwZXJpb2Qgc3BlY2lmeSBhIGN1c3RvbSBwZXJpb2Q7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICogQHBhcmFtIHJlZ2lvbiBzcGVjaWZ5IGEgY3VzdG9tIHJlZ2lvbjsgaWYgdW5kZWZpbmVkLCB1c2VzIHRoZSBnbG9iYWwgZGVmYXVsdFxuICAgKiBAcGFyYW0gYWNjb3VudCBzcGVjaWZ5IGEgY3VzdG9tIGFjY291bnQ7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICovXG4gIGNyZWF0ZU1ldHJpY1NlYXJjaChcbiAgICBxdWVyeTogc3RyaW5nLFxuICAgIGRpbWVuc2lvbnNNYXA6IERpbWVuc2lvbnNNYXAsXG4gICAgc3RhdGlzdGljOiBNZXRyaWNTdGF0aXN0aWMsXG4gICAgbmFtZXNwYWNlPzogc3RyaW5nLFxuICAgIGxhYmVsPzogc3RyaW5nLFxuICAgIHBlcmlvZD86IER1cmF0aW9uLFxuICAgIHJlZ2lvbj86IHN0cmluZyxcbiAgICBhY2NvdW50Pzogc3RyaW5nLFxuICApOiBNYXRoRXhwcmVzc2lvbiB7XG4gICAgY29uc3QgZmluYWxQZXJpb2QgPVxuICAgICAgcGVyaW9kID8/IHRoaXMuZ2xvYmFsRGVmYXVsdHMucGVyaW9kID8/IERlZmF1bHRNZXRyaWNQZXJpb2Q7XG4gICAgY29uc3Qgc2VhcmNoTmFtZXNwYWNlID0gdGhpcy5nZXROYW1lc3BhY2VXaXRoRmFsbGJhY2sobmFtZXNwYWNlKTtcbiAgICBjb25zdCBuYW1lc3BhY2VzV2l0aFF1b3RhdGlvbnMgPSBzZWFyY2hOYW1lc3BhY2VcbiAgICAgIC5zcGxpdChcIixcIilcbiAgICAgIC5tYXAoKG5hbWVzcGFjZSkgPT4gYFwiJHtuYW1lc3BhY2UudHJpbSgpfVwiYCk7XG4gICAgY29uc3Qga2V5c1dpdGhRdW90YXRpb25zID0gT2JqZWN0LmtleXMoZGltZW5zaW9uc01hcCkubWFwKFxuICAgICAgKGtleSkgPT4gYFwiJHtrZXl9XCJgLFxuICAgICk7XG4gICAgY29uc3QgbmFtZXNwYWNlUGx1c0RpbWVuc2lvbktleXMgPSBbXG4gICAgICAuLi5uYW1lc3BhY2VzV2l0aFF1b3RhdGlvbnMsXG4gICAgICAuLi5rZXlzV2l0aFF1b3RhdGlvbnMsXG4gICAgXS5qb2luKFwiLFwiKTtcbiAgICBjb25zdCBtZXRyaWNTY2hlbWEgPSBgeyR7bmFtZXNwYWNlUGx1c0RpbWVuc2lvbktleXN9fWA7XG5cbiAgICBjb25zdCBkaW1lbnNpb25LZXlzQW5kVmFsdWVzID0gT2JqZWN0LmVudHJpZXMoXG4gICAgICB0aGlzLnJlbW92ZVVuZGVmaW5lZEVudHJpZXMoZGltZW5zaW9uc01hcCksXG4gICAgKVxuICAgICAgLm1hcCgoW2tleSwgdmFsdWVdKSA9PiBgXCIke2tleX1cIj1cIiR7dmFsdWV9XCJgKVxuICAgICAgLmpvaW4oXCIgXCIpO1xuXG4gICAgY29uc3QgZXhwcmVzc2lvbiA9IGBTRUFSQ0goJyR7bWV0cmljU2NoZW1hfSAke2RpbWVuc2lvbktleXNBbmRWYWx1ZXN9ICR7cXVlcnl9JywgJyR7c3RhdGlzdGljfScsICR7ZmluYWxQZXJpb2QudG9TZWNvbmRzKCl9KWA7XG5cbiAgICByZXR1cm4gbmV3IE1hdGhFeHByZXNzaW9uKHtcbiAgICAgIGV4cHJlc3Npb24sXG4gICAgICAvLyBzZWUgaHR0cHM6Ly9naXRodWIuY29tL2F3cy9hd3MtY2RrL2lzc3Vlcy83MjM3XG4gICAgICB1c2luZ01ldHJpY3M6IHt9LFxuICAgICAgLy8gY2Fubm90IGJlIGFuIGVtcHR5IHN0cmluZyBhbmQgdW5kZWZpbmVkIGlzIG5vIGdvb2QgZWl0aGVyXG4gICAgICBsYWJlbDogbGFiZWwgPz8gXCIgXCIsXG4gICAgICBwZXJpb2Q6IGZpbmFsUGVyaW9kLFxuICAgICAgc2VhcmNoUmVnaW9uOiB0aGlzLnJlc29sdmVSZWdpb24ocmVnaW9uID8/IHRoaXMuZ2xvYmFsRGVmYXVsdHMucmVnaW9uKSxcbiAgICAgIHNlYXJjaEFjY291bnQ6IHRoaXMucmVzb2x2ZUFjY291bnQoXG4gICAgICAgIGFjY291bnQgPz8gdGhpcy5nbG9iYWxEZWZhdWx0cy5hY2NvdW50LFxuICAgICAgKSxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGYWN0b3J5IG1ldGhvZCB0aGF0IGNyZWF0ZXMgYW5vbWFseSBkZXRlY3Rpb24gb24gYSBtZXRyaWMuXG4gICAqIEFub21hbHkgb2NjdXJzIHdoZW5ldmVyIGEgbWV0cmljIHZhbHVlIGZhbGxzIG91dHNpZGUgb2YgYSBwcmVjb21wdXRlZCByYW5nZSBvZiBwcmVkaWN0ZWQgdmFsdWVzLlxuICAgKiBUaGUgZGV0ZWN0aW9uIGRvZXMgbm90IG5lZWQgYW55IHNldHVwLiBUaGUgbW9kZWwgd2lsbCBzdGFydCBsZWFybmluZyBhdXRvbWF0aWNhbGx5IGFuZCBzaG91bGQgYmUgcmVhZHkgaW4gYSBmZXcgbWludXRlcy5cbiAgICogVXN1YWxseSwgdGhlIGFub21hbHkgZGV0ZWN0aW9uIGlzIHBhaXJlZCB3aXRoIGFuIGFsYXJtLlxuICAgKiBAc2VlIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9BbWF6b25DbG91ZFdhdGNoL2xhdGVzdC9tb25pdG9yaW5nL0Nsb3VkV2F0Y2hfQW5vbWFseV9EZXRlY3Rpb24uaHRtbFxuICAgKlxuICAgKiBAcGFyYW0gbWV0cmljIG1ldHJpYyB0byBkZXRlY3QgYW5vbWFseSBkZXRlY3Rpb24gb2ZcbiAgICogQHBhcmFtIHN0ZGV2IHN0YW5kYXJkIGRldmlhdGlvbiwgYmFzaWNhbGx5IHRoZSB0b2xlcmFuY2UgLyBiYW5kIHRoaWNrbmVzc1xuICAgKiBAcGFyYW0gbGFiZWwgbWV0cmljIGxhYmVsIChyZXF1aXJlZCwgYXMgdGhlcmUgaXMgbm8gcmVhc29uYWJsZSBkZWZhdWx0KVxuICAgKiBAcGFyYW0gY29sb3IgbWV0cmljIGNvbG9yOyBpZiB1bmRlZmluZWQsIHVzZXMgYSBDbG91ZFdhdGNoIHByb3ZpZGVkIGNvbG9yIChwcmVmZXJyZWQpXG4gICAqIEBwYXJhbSBleHByZXNzaW9uSWQgZXhwcmVzc2lvbiBJRCBvZiB0aGUgbWV0cmljOyB1c2VzIGBtMWAgaWYgdW5kZWZpbmVkXG4gICAqIEBwYXJhbSBwZXJpb2Qgc3BlY2lmeSBhIGN1c3RvbSBwZXJpb2Q7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICogQHBhcmFtIHJlZ2lvbiBzcGVjaWZ5IGEgY3VzdG9tIHJlZ2lvbjsgaWYgdW5kZWZpbmVkLCB1c2VzIHRoZSBnbG9iYWwgZGVmYXVsdFxuICAgKiBAcGFyYW0gYWNjb3VudCBzcGVjaWZ5IGEgY3VzdG9tIGFjY291bnQ7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICovXG4gIGNyZWF0ZU1ldHJpY0Fub21hbHlEZXRlY3Rpb24oXG4gICAgbWV0cmljOiBJTWV0cmljLFxuICAgIHN0ZGV2OiBudW1iZXIsXG4gICAgbGFiZWw6IHN0cmluZyxcbiAgICBjb2xvcj86IHN0cmluZyxcbiAgICBleHByZXNzaW9uSWQ/OiBzdHJpbmcsXG4gICAgcGVyaW9kPzogRHVyYXRpb24sXG4gICAgcmVnaW9uPzogc3RyaW5nLFxuICAgIGFjY291bnQ/OiBzdHJpbmcsXG4gICk6IE1ldHJpY1dpdGhBbGFybVN1cHBvcnQge1xuICAgIGNvbnN0IGZpbmFsRXhwcmVzc2lvbklkID0gZXhwcmVzc2lvbklkID8/IFwibTFcIjtcbiAgICBjb25zdCB1c2luZ01ldHJpY3M6IFJlY29yZDxzdHJpbmcsIElNZXRyaWM+ID0ge307XG4gICAgdXNpbmdNZXRyaWNzW2ZpbmFsRXhwcmVzc2lvbklkXSA9IG1ldHJpYztcbiAgICByZXR1cm4gbmV3IEFub21hbHlEZXRlY3Rpb25NYXRoRXhwcmVzc2lvbih7XG4gICAgICBsYWJlbCxcbiAgICAgIGNvbG9yLFxuICAgICAgdXNpbmdNZXRyaWNzLFxuICAgICAgZXhwcmVzc2lvbjogYEFOT01BTFlfREVURUNUSU9OX0JBTkQoJHtmaW5hbEV4cHJlc3Npb25JZH0sJHtzdGRldn0pYCxcbiAgICAgIHBlcmlvZDogcGVyaW9kID8/IHRoaXMuZ2xvYmFsRGVmYXVsdHMucGVyaW9kID8/IERlZmF1bHRNZXRyaWNQZXJpb2QsXG4gICAgICBzZWFyY2hSZWdpb246IHRoaXMucmVzb2x2ZVJlZ2lvbihyZWdpb24gPz8gdGhpcy5nbG9iYWxEZWZhdWx0cy5yZWdpb24pLFxuICAgICAgc2VhcmNoQWNjb3VudDogdGhpcy5yZXNvbHZlQWNjb3VudChcbiAgICAgICAgYWNjb3VudCA/PyB0aGlzLmdsb2JhbERlZmF1bHRzLmFjY291bnQsXG4gICAgICApLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkYXB0cyBwcm9wZXJ0aWVzIG9mIGEgZm9yZWlnbiBtZXRyaWMgKG1ldHJpYyBjcmVhdGVkIG91dHNpZGUgb2YgdGhpcyBtZXRyaWMgZmFjdG9yeSkgdG8gY29tcGx5IHdpdGggdGhlIGdsb2JhbCBkZWZhdWx0cy5cbiAgICogTWlnaHQgbW9kaWZ5IG5hbWVzcGFjZSBhbmQgbWV0cmljIHBlcmlvZC5cbiAgICpcbiAgICogQHBhcmFtIG1ldHJpYyBtZXRyaWMgdG8gYmUgYWRhcHRlZFxuICAgKi9cbiAgYWRhcHRNZXRyaWMobWV0cmljOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0KTogTWV0cmljV2l0aEFsYXJtU3VwcG9ydCB7XG4gICAgaWYgKHRoaXMuZ2xvYmFsRGVmYXVsdHMucmVnaW9uKSB7XG4gICAgICBtZXRyaWMgPSBtZXRyaWMud2l0aCh7IHJlZ2lvbjogdGhpcy5nbG9iYWxEZWZhdWx0cy5yZWdpb24gfSk7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuZ2xvYmFsRGVmYXVsdHMuYWNjb3VudCkge1xuICAgICAgbWV0cmljID0gbWV0cmljLndpdGgoeyBhY2NvdW50OiB0aGlzLmdsb2JhbERlZmF1bHRzLmFjY291bnQgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG1ldHJpYy53aXRoKHtcbiAgICAgIHBlcmlvZDogdGhpcy5nbG9iYWxEZWZhdWx0cy5wZXJpb2QgPz8gRGVmYXVsdE1ldHJpY1BlcmlvZCxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGFwdHMgcHJvcGVydGllcyBvZiBhIGZvcmVpZ24gbWV0cmljIChtZXRyaWMgY3JlYXRlZCBvdXRzaWRlIG9mIHRoaXMgbWV0cmljIGZhY3RvcnkpIHRvIGNvbXBseSB3aXRoIHRoZSBnbG9iYWwgZGVmYXVsdHMuXG4gICAqIE1pZ2h0IG1vZGlmeSBuYW1lc3BhY2UuIFByZXNlcnZlcyBtZXRyaWMgcGVyaW9kLlxuICAgKlxuICAgKiBAcGFyYW0gbWV0cmljIG1ldHJpYyB0byBiZSBhZGFwdGVkXG4gICAqL1xuICBhZGFwdE1ldHJpY1ByZXNlcnZpbmdQZXJpb2QoXG4gICAgbWV0cmljOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0LFxuICApOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0IHtcbiAgICByZXR1cm4gbWV0cmljO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBtZXRyaWMgbWF0aCBleHByZXNzaW9uIHRoYXQgbXVsdGlwbGllcyB0aGUgZ2l2ZW4gbWV0cmljIGJ5IGdpdmVuIGNvZWZmaWNpZW50LlxuICAgKiBEb2VzIG5vdGhpbmcgaWYgdGhlIG11bHRpcGxpZXIgaXMgb25lLiBQcmVzZXJ2ZXMgdGhlIG1ldHJpYyBwZXJpb2QuXG4gICAqXG4gICAqIEBwYXJhbSBtZXRyaWMgbWV0cmljIHRvIG11bHRpcGx5XG4gICAqIEBwYXJhbSBtdWx0aXBsaWVyIG11bHRpcGxpZXIgKG11c3QgYmUgPiAxKVxuICAgKiBAcGFyYW0gbGFiZWwgZXhwcmVzc2lvbiBsYWJlbFxuICAgKiBAcGFyYW0gZXhwcmVzc2lvbklkIGV4cHJlc3Npb24gSUQgb2YgdGhlIG1ldHJpYzsgdXNlcyBgbTFgIGlmIHVuZGVmaW5lZFxuICAgKi9cbiAgbXVsdGlwbHlNZXRyaWMoXG4gICAgbWV0cmljOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0LFxuICAgIG11bHRpcGxpZXI6IG51bWJlcixcbiAgICBsYWJlbDogc3RyaW5nLFxuICAgIGV4cHJlc3Npb25JZD86IHN0cmluZyxcbiAgKTogTWV0cmljV2l0aEFsYXJtU3VwcG9ydCB7XG4gICAgaWYgKG11bHRpcGxpZXIgPT0gMSkge1xuICAgICAgcmV0dXJuIG1ldHJpYztcbiAgICB9IGVsc2UgaWYgKG11bHRpcGxpZXIgPCAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJNdWx0aXBsaWVyIG11c3QgYmUgZ3JlYXRlciB0aGFuIG9uZS5cIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGZpbmFsRXhwcmVzc2lvbklkID0gZXhwcmVzc2lvbklkID8/IFwibTFcIjtcbiAgICAgIGNvbnN0IHVzaW5nTWV0cmljczogUmVjb3JkPHN0cmluZywgSU1ldHJpYz4gPSB7fTtcbiAgICAgIHVzaW5nTWV0cmljc1tmaW5hbEV4cHJlc3Npb25JZF0gPSBtZXRyaWM7XG4gICAgICByZXR1cm4gdGhpcy5jcmVhdGVNZXRyaWNNYXRoKFxuICAgICAgICBgJHtmaW5hbEV4cHJlc3Npb25JZH0gKiAke211bHRpcGxpZXJ9YCxcbiAgICAgICAgdXNpbmdNZXRyaWNzLFxuICAgICAgICBsYWJlbCxcbiAgICAgICAgbWV0cmljLmNvbG9yLFxuICAgICAgICBtZXRyaWMucGVyaW9kLFxuICAgICAgICB0aGlzLmdldFJlZ2lvbihtZXRyaWMpLFxuICAgICAgICB0aGlzLmdldEFjY291bnQobWV0cmljKSxcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBtZXRyaWMgbWF0aCBleHByZXNzaW9uIHRoYXQgZGl2aWRlcyB0aGUgZ2l2ZW4gbWV0cmljIGJ5IGdpdmVuIGNvZWZmaWNpZW50LlxuICAgKiBEb2VzIG5vdGhpbmcgaWYgdGhlIGRpdmlzb3IgaXMgb25lLiBQcmVzZXJ2ZXMgdGhlIG1ldHJpYyBwZXJpb2QuXG4gICAqXG4gICAqIEBwYXJhbSBtZXRyaWMgbWV0cmljIHRvIG11bHRpcGx5XG4gICAqIEBwYXJhbSBkaXZpc29yIGRpdmlzb3IgKG11c3QgYmUgPiAxKVxuICAgKiBAcGFyYW0gbGFiZWwgZXhwcmVzc2lvbiBsYWJlbFxuICAgKiBAcGFyYW0gZXhwcmVzc2lvbklkIGV4cHJlc3Npb24gSUQgb2YgdGhlIG1ldHJpYzsgdXNlcyBgbTFgIGlmIHVuZGVmaW5lZFxuICAgKi9cbiAgZGl2aWRlTWV0cmljKFxuICAgIG1ldHJpYzogTWV0cmljV2l0aEFsYXJtU3VwcG9ydCxcbiAgICBkaXZpc29yOiBudW1iZXIsXG4gICAgbGFiZWw6IHN0cmluZyxcbiAgICBleHByZXNzaW9uSWQ/OiBzdHJpbmcsXG4gICk6IE1ldHJpY1dpdGhBbGFybVN1cHBvcnQge1xuICAgIGlmIChkaXZpc29yID09IDEpIHtcbiAgICAgIHJldHVybiBtZXRyaWM7XG4gICAgfSBlbHNlIGlmIChkaXZpc29yIDwgMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRGl2aXNvciBtdXN0IGJlIGdyZWF0ZXIgdGhhbiBvbmUuXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBmaW5hbEV4cHJlc3Npb25JZCA9IGV4cHJlc3Npb25JZCA/PyBcIm0xXCI7XG4gICAgICBjb25zdCB1c2luZ01ldHJpY3M6IFJlY29yZDxzdHJpbmcsIElNZXRyaWM+ID0ge307XG4gICAgICB1c2luZ01ldHJpY3NbZmluYWxFeHByZXNzaW9uSWRdID0gbWV0cmljO1xuICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlTWV0cmljTWF0aChcbiAgICAgICAgYCR7ZmluYWxFeHByZXNzaW9uSWR9IC8gJHtkaXZpc29yfWAsXG4gICAgICAgIHVzaW5nTWV0cmljcyxcbiAgICAgICAgbGFiZWwsXG4gICAgICAgIG1ldHJpYy5jb2xvcixcbiAgICAgICAgbWV0cmljLnBlcmlvZCxcbiAgICAgICAgdGhpcy5nZXRSZWdpb24obWV0cmljKSxcbiAgICAgICAgdGhpcy5nZXRBY2NvdW50KG1ldHJpYyksXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbWV0cmljIG1hdGggZXhwcmVzc2lvbiB0aGF0IGNvbXB1dGVzIGEgcmF0ZSBmcm9tIGEgcmVndWxhciBtZXRyaWMuXG4gICAqIEZvciBleGFtcGxlLCBpdCBhbGxvd3MgeW91IHRvIGNvbXB1dGUgcmF0ZSBwZXIgc2Vjb25kIChUUFMpLCBwZXIgbWludXRlLCBvciBqdXN0IGFuIGF2ZXJhZ2Ugb2YgeW91ciB0cmFuc2FjdGlvbnMuXG4gICAqXG4gICAqIEBwYXJhbSBtZXRyaWMgbWV0cmljIHRvIGNhbGN1bGF0ZSB0aGUgcmF0ZSBmcm9tXG4gICAqIEBwYXJhbSBtZXRob2QgcmF0ZSBjb21wdXRhdGlvbiBtZXRob2RcbiAgICogQHBhcmFtIGFkZFN0YXRzVG9MYWJlbCBhZGQgZGV0YWlsZWQgc3RhdGlzdGljcyAobWluLCBtYXgsIGF2ZXJhZ2UpIHRvIHRoZSBsYWJlbFxuICAgKiBAcGFyYW0gZXhwcmVzc2lvbklkIGV4cHJlc3Npb24gSUQgb2YgdGhlIG1ldHJpYzsgdXNlcyBgbTFgIGlmIHVuZGVmaW5lZFxuICAgKiBAcGFyYW0gZmlsbFdpdGhaZXJvZXMgaWYgVFJVRSwgdGhlIGZpbmFsIG1ldHJpYyB3aWxsIGJlIHplcm8tZmlsbGVkICgwIG9uIG5vIGRhdGEpOyBmYWxzZSBpZiB1bmRlZmluZWRcbiAgICovXG4gIHRvUmF0ZShcbiAgICBtZXRyaWM6IE1ldHJpY1dpdGhBbGFybVN1cHBvcnQsXG4gICAgbWV0aG9kOiBSYXRlQ29tcHV0YXRpb25NZXRob2QsXG4gICAgYWRkU3RhdHNUb0xhYmVsPzogYm9vbGVhbixcbiAgICBleHByZXNzaW9uSWQ/OiBzdHJpbmcsXG4gICAgZmlsbFdpdGhaZXJvZXM/OiBib29sZWFuLFxuICApOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0IHtcbiAgICBjb25zdCBmaW5hbEV4cHJlc3Npb25JZCA9IGV4cHJlc3Npb25JZCA/PyBcIm0xXCI7XG4gICAgY29uc3QgbGFiZWxQcmVmaXggPSBtZXRyaWMubGFiZWwgPz8gXCJSYXRlXCI7XG5cbiAgICBjb25zdCBzdGF0c0luTGFiZWw6IHN0cmluZ1tdID0gW107XG4gICAgaWYgKGFkZFN0YXRzVG9MYWJlbCA/PyBmYWxzZSkge1xuICAgICAgc3RhdHNJbkxhYmVsLnB1c2goXCJtaW46ICR7TUlOfVwiKTtcbiAgICAgIHN0YXRzSW5MYWJlbC5wdXNoKFwibWF4OiAke01BWH1cIik7XG4gICAgICBpZiAobWV0aG9kICE9PSBSYXRlQ29tcHV0YXRpb25NZXRob2QuQVZFUkFHRSkge1xuICAgICAgICAvLyBvbmx5IGFkZCBhdmVyYWdlIGlmIGRvIG5vdCBoYXZlIGl0IGFscmVhZHlcbiAgICAgICAgc3RhdHNJbkxhYmVsLnB1c2goXCJhdmc6ICR7QVZHfVwiKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBmaW5hbEV4cHJlc3Npb25JZFplcm9lZCA9XG4gICAgICBmaWxsV2l0aFplcm9lcyA/PyBmYWxzZVxuICAgICAgICA/IGBGSUxMKCR7ZmluYWxFeHByZXNzaW9uSWR9LDApYFxuICAgICAgICA6IGZpbmFsRXhwcmVzc2lvbklkO1xuICAgIGNvbnN0IGxhYmVsQXBwZW5kaXggPVxuICAgICAgc3RhdHNJbkxhYmVsLmxlbmd0aCA+IDAgPyBgICgke3N0YXRzSW5MYWJlbC5qb2luKFwiLCBcIil9KWAgOiBcIlwiO1xuXG4gICAgc3dpdGNoIChtZXRob2QpIHtcbiAgICAgIGNhc2UgUmF0ZUNvbXB1dGF0aW9uTWV0aG9kLkFWRVJBR0U6XG4gICAgICAgIGNvbnN0IGF2Z0xhYmVsID0gYCR7bGFiZWxQcmVmaXh9IChhdmcpJHtsYWJlbEFwcGVuZGl4fWA7XG4gICAgICAgIGNvbnN0IGF2Z01ldHJpYyA9IG1ldHJpYy53aXRoKHtcbiAgICAgIC