cdk-monitoring-constructs
Version:
[](https://gitpod.io/#https://github.com/cdklabs/cdk-monitoring-constructs) [](https://badge
288 lines • 44.9 kB
JavaScript
;
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(props) {
this.globalDefaults = props?.globalDefaults ?? {};
}
/**
* 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
*/
createMetric(metricName, statistic, label, dimensionsMap, color, namespace, period) {
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,
});
}
/**
* 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
*/
createMetricMath(expression, usingMetrics, label, color, period) {
return new aws_cloudwatch_1.MathExpression({
label,
color,
expression,
usingMetrics,
period: period ?? this.globalDefaults.period ?? exports.DefaultMetricPeriod,
});
}
/**
* 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
*/
createMetricSearch(query, dimensionsMap, statistic, namespace, label, period) {
const finalPeriod = period ?? this.globalDefaults.period ?? exports.DefaultMetricPeriod;
const searchNamespace = this.getNamespaceWithFallback(namespace);
const namespacePlusDimensionKeys = [
searchNamespace,
...Object.keys(dimensionsMap),
].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,
});
}
/**
* 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
*/
createMetricAnomalyDetection(metric, stdev, label, color, expressionId, period) {
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,
});
}
/**
* 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) {
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);
}
}
/**
* 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);
}
}
/**
* 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);
}
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);
case RateComputationMethod_1.RateComputationMethod.PER_MINUTE:
return this.createMetricMath(`(60 * ${finalExpressionIdZeroed}) / PERIOD(${finalExpressionId})`, { [finalExpressionId]: metric }, `${labelPrefix}/m${labelAppendix}`, metric.color, metric.period);
case RateComputationMethod_1.RateComputationMethod.PER_HOUR:
return this.createMetricMath(`(3600 * ${finalExpressionIdZeroed}) / PERIOD(${finalExpressionId})`, { [finalExpressionId]: metric }, `${labelPrefix}/h${labelAppendix}`, metric.color, metric.period);
case RateComputationMethod_1.RateComputationMethod.PER_DAY:
return this.createMetricMath(`(86400 * ${finalExpressionIdZeroed}) / PERIOD(${finalExpressionId})`, { [finalExpressionId]: metric }, `${labelPrefix}/d${labelAppendix}`, metric.color, metric.period);
}
}
/**
* 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;
}
}
exports.MetricFactory = MetricFactory;
_a = JSII_RTTI_SYMBOL_1;
MetricFactory[_a] = { fqn: "cdk-monitoring-constructs.MetricFactory", version: "1.21.0" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWV0cmljRmFjdG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIk1ldHJpY0ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSw2Q0FBdUM7QUFDdkMsK0RBS29DO0FBRXBDLHFGQUFrRjtBQUNsRix1REFBb0Q7QUFFcEQsbUVBQWdFO0FBRWhFOztHQUVHO0FBQ1UsUUFBQSxtQkFBbUIsR0FBRyxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztBQXlCdkQsTUFBYSxhQUFhO0lBR3hCLFlBQVksS0FBMEI7UUFDcEMsSUFBSSxDQUFDLGNBQWMsR0FBRyxLQUFLLEVBQUUsY0FBYyxJQUFJLEVBQUUsQ0FBQztJQUNwRCxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7T0FVRztJQUNILFlBQVksQ0FDVixVQUFrQixFQUNsQixTQUEwQixFQUMxQixLQUFjLEVBQ2QsYUFBNkIsRUFDN0IsS0FBYyxFQUNkLFNBQWtCLEVBQ2xCLE1BQWlCO1FBRWpCLE9BQU8sSUFBSSx1QkFBTSxDQUFDO1lBQ2hCLFNBQVM7WUFDVCxVQUFVO1lBQ1YsS0FBSztZQUNMLEtBQUs7WUFDTCxhQUFhLEVBQUUsYUFBYTtnQkFDMUIsQ0FBQyxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxhQUFhLENBQUM7Z0JBQzVDLENBQUMsQ0FBQyxTQUFTO1lBQ2IsU0FBUyxFQUFFLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxTQUFTLENBQUM7WUFDbkQsTUFBTSxFQUFFLE1BQU0sSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sSUFBSSwyQkFBbUI7U0FDcEUsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0gsZ0JBQWdCLENBQ2QsVUFBa0IsRUFDbEIsWUFBcUMsRUFDckMsS0FBYSxFQUNiLEtBQWMsRUFDZCxNQUFpQjtRQUVqQixPQUFPLElBQUksK0JBQWMsQ0FBQztZQUN4QixLQUFLO1lBQ0wsS0FBSztZQUNMLFVBQVU7WUFDVixZ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LEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBRWxELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQzs7QUFuWUgsc0NBb1lDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRHVyYXRpb24gfSBmcm9tIFwiYXdzLWNkay1saWJcIjtcbmltcG9ydCB7XG4gIERpbWVuc2lvbnNNYXAsXG4gIElNZXRyaWMsXG4gIE1hdGhFeHByZXNzaW9uLFxuICBNZXRyaWMsXG59IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtY2xvdWR3YXRjaFwiO1xuXG5pbXBvcnQgeyBBbm9tYWx5RGV0ZWN0aW9uTWF0aEV4cHJlc3Npb24gfSBmcm9tIFwiLi9Bbm9tYWx5RGV0ZWN0aW9uTWF0aEV4cHJlc3Npb25cIjtcbmltcG9ydCB7IE1ldHJpY1N0YXRpc3RpYyB9IGZyb20gXCIuL01ldHJpY1N0YXRpc3RpY1wiO1xuaW1wb3J0IHsgTWV0cmljV2l0aEFsYXJtU3VwcG9ydCB9IGZyb20gXCIuL01ldHJpY1dpdGhBbGFybVN1cHBvcnRcIjtcbmltcG9ydCB7IFJhdGVDb21wdXRhdGlvbk1ldGhvZCB9IGZyb20gXCIuL1JhdGVDb21wdXRhdGlvbk1ldGhvZFwiO1xuXG4vKipcbiAqIFRoZSBtb3N0IGNvbW1vbiBkZWZhdWx0IG1ldHJpYyBwZXJpb2QgdXNlZCBhdCBBbWF6b24gaXMgY3VycmVudGx5IDUgbWludXRlcy5cbiAqL1xuZXhwb3J0IGNvbnN0IERlZmF1bHRNZXRyaWNQZXJpb2QgPSBEdXJhdGlvbi5taW51dGVzKDUpO1xuXG4vKipcbiAqIFRoZXNlIGFyZSB0aGUgZ2xvYmFscyB1c2VkIGZvciBlYWNoIG1ldHJpYywgdW5sZXNzIHRoZXJlIGlzIHNvbWUga2luZCBvZiBvdmVycmlkZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBNZXRyaWNGYWN0b3J5RGVmYXVsdHMge1xuICAvKipcbiAgICogRWFjaCBtZXRyaWMgZXhpc3RzIGluIGEgbmFtZXNwYWNlLiBBV1MgU2VydmljZXMgaGF2ZSB0aGVpciBvd24gbmFtZXNwYWNlLCBidXQgaGVyZSB5b3UgY2FuIHNwZWNpZnkgeW91ciBjdXN0b20gb25lLlxuICAgKi9cbiAgcmVhZG9ubHkgbmFtZXNwYWNlPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBNZXRyaWMgcGVyaW9kLiBEZWZhdWx0IHZhbHVlIGlzIHVzZWQgaWYgbm90IGRlZmluZWQuXG4gICAqIEBkZWZhdWx0IC0gRGVmYXVsdE1ldHJpY1BlcmlvZFxuICAgKi9cbiAgcmVhZG9ubHkgcGVyaW9kPzogRHVyYXRpb247XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWV0cmljRmFjdG9yeVByb3BzIHtcbiAgLyoqXG4gICAqIEFsbG93cyB5b3UgdG8gc3BlY2lmeSB0aGUgZ2xvYmFsIGRlZmF1bHRzLCB3aGljaCBjYW4gYmUgb3ZlcnJpZGRlbiBpbiB0aGUgaW5kaXZpZHVhbCBtZXRyaWNzIG9yIGFsYXJtcy5cbiAgICovXG4gIHJlYWRvbmx5IGdsb2JhbERlZmF1bHRzPzogTWV0cmljRmFjdG9yeURlZmF1bHRzO1xufVxuXG5leHBvcnQgY2xhc3MgTWV0cmljRmFjdG9yeSB7XG4gIHByb3RlY3RlZCByZWFkb25seSBnbG9iYWxEZWZhdWx0czogTWV0cmljRmFjdG9yeURlZmF1bHRzO1xuXG4gIGNvbnN0cnVjdG9yKHByb3BzPzogTWV0cmljRmFjdG9yeVByb3BzKSB7XG4gICAgdGhpcy5nbG9iYWxEZWZhdWx0cyA9IHByb3BzPy5nbG9iYWxEZWZhdWx0cyA/PyB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGYWN0b3J5IG1ldGhvZCB0aGF0IGNyZWF0ZXMgYSBtZXRyaWMuIFRoZSBtZXRyaWMgcHJvcGVydGllcyB3aWxsIGFscmVhZHkgYmUgdXBkYXRlZCB0byBjb21wbHkgd2l0aCB0aGUgZ2xvYmFsIGRlZmF1bHRzLlxuICAgKlxuICAgKiBAcGFyYW0gbWV0cmljTmFtZSBtZXRyaWMgbmFtZVxuICAgKiBAcGFyYW0gc3RhdGlzdGljIGFnZ3JlZ2F0aW9uIHN0YXRpc3RpYyB0byB1c2VcbiAgICogQHBhcmFtIGxhYmVsIG1ldHJpYyBsYWJlbDsgaWYgdW5kZWZpbmVkLCBtZXRyaWMgbmFtZSBpcyB1c2VkIGJ5IENsb3VkV2F0Y2hcbiAgICogQHBhcmFtIGRpbWVuc2lvbnNNYXAgYWRkaXRpb25hbCBkaW1lbnNpb25zIHRvIGJlIGFkZGVkXG4gICAqIEBwYXJhbSBjb2xvciBtZXRyaWMgY29sb3I7IGlmIHVuZGVmaW5lZCwgdXNlcyBhIENsb3VkV2F0Y2ggcHJvdmlkZWQgY29sb3IgKHByZWZlcnJlZClcbiAgICogQHBhcmFtIG5hbWVzcGFjZSBzcGVjaWZ5IGEgY3VzdG9tIG5hbWVzcGFjZTsgaWYgdW5kZWZpbmVkLCB1c2VzIHRoZSBnbG9iYWwgZGVmYXVsdFxuICAgKiBAcGFyYW0gcGVyaW9kIHNwZWNpZnkgYSBjdXN0b20gcGVyaW9kOyBpZiB1bmRlZmluZWQsIHVzZXMgdGhlIGdsb2JhbCBkZWZhdWx0XG4gICAqL1xuICBjcmVhdGVNZXRyaWMoXG4gICAgbWV0cmljTmFtZTogc3RyaW5nLFxuICAgIHN0YXRpc3RpYzogTWV0cmljU3RhdGlzdGljLFxuICAgIGxhYmVsPzogc3RyaW5nLFxuICAgIGRpbWVuc2lvbnNNYXA/OiBEaW1lbnNpb25zTWFwLFxuICAgIGNvbG9yPzogc3RyaW5nLFxuICAgIG5hbWVzcGFjZT86IHN0cmluZyxcbiAgICBwZXJpb2Q/OiBEdXJhdGlvblxuICApOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0IHtcbiAgICByZXR1cm4gbmV3IE1ldHJpYyh7XG4gICAgICBzdGF0aXN0aWMsXG4gICAgICBtZXRyaWNOYW1lLFxuICAgICAgbGFiZWwsXG4gICAgICBjb2xvcixcbiAgICAgIGRpbWVuc2lvbnNNYXA6IGRpbWVuc2lvbnNNYXBcbiAgICAgICAgPyB0aGlzLnJlbW92ZVVuZGVmaW5lZEVudHJpZXMoZGltZW5zaW9uc01hcClcbiAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgICBuYW1lc3BhY2U6IHRoaXMuZ2V0TmFtZXNwYWNlV2l0aEZhbGxiYWNrKG5hbWVzcGFjZSksXG4gICAgICBwZXJpb2Q6IHBlcmlvZCA/PyB0aGlzLmdsb2JhbERlZmF1bHRzLnBlcmlvZCA/PyBEZWZhdWx0TWV0cmljUGVyaW9kLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEZhY3RvcnkgbWV0aG9kIHRoYXQgY3JlYXRlcyBhIG1ldHJpYyBtYXRoIGV4cHJlc3Npb24uIFRoZSBtZXRyaWMgcHJvcGVydGllcyB3aWxsIGFscmVhZHkgYmUgdXBkYXRlZCB0byBjb21wbHkgd2l0aCB0aGUgZ2xvYmFsIGRlZmF1bHRzLlxuICAgKlxuICAgKiBAcGFyYW0gZXhwcmVzc2lvbiBDbG91ZFdhdGNoIG1ldHJpYyBtYXRoIGV4cHJlc3Npb24gKGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9BbWF6b25DbG91ZFdhdGNoL2xhdGVzdC9tb25pdG9yaW5nL3VzaW5nLW1ldHJpYy1tYXRoLmh0bWwpXG4gICAqIEBwYXJhbSB1c2luZ01ldHJpY3MgbWFwIG9mIG1ldHJpY3MsIHdoZXJlIGtleXMgYXJlIGV4cHJlc3Npb24gSURzICh1c2VkIGluIHRoZSBleHByZXNzaW9uKSBhbmQgdmFsdWVzIGFyZSBtZXRyaWNzXG4gICAqIEBwYXJhbSBsYWJlbCBtZXRyaWMgbGFiZWwgKHJlcXVpcmVkLCBhcyB0aGVyZSBpcyBubyByZWFzb25hYmxlIGRlZmF1bHQpXG4gICAqIEBwYXJhbSBjb2xvciBtZXRyaWMgY29sb3I7IGlmIHVuZGVmaW5lZCwgdXNlcyBhIENsb3VkV2F0Y2ggcHJvdmlkZWQgY29sb3IgKHByZWZlcnJlZClcbiAgICogQHBhcmFtIHBlcmlvZCBzcGVjaWZ5IGEgY3VzdG9tIHBlcmlvZDsgaWYgdW5kZWZpbmVkLCB1c2VzIHRoZSBnbG9iYWwgZGVmYXVsdFxuICAgKi9cbiAgY3JlYXRlTWV0cmljTWF0aChcbiAgICBleHByZXNzaW9uOiBzdHJpbmcsXG4gICAgdXNpbmdNZXRyaWNzOiBSZWNvcmQ8c3RyaW5nLCBJTWV0cmljPixcbiAgICBsYWJlbDogc3RyaW5nLFxuICAgIGNvbG9yPzogc3RyaW5nLFxuICAgIHBlcmlvZD86IER1cmF0aW9uXG4gICk6IE1ldHJpY1dpdGhBbGFybVN1cHBvcnQge1xuICAgIHJldHVybiBuZXcgTWF0aEV4cHJlc3Npb24oe1xuICAgICAgbGFiZWwsXG4gICAgICBjb2xvcixcbiAgICAgIGV4cHJlc3Npb24sXG4gICAgICB1c2luZ01ldHJpY3MsXG4gICAgICBwZXJpb2Q6IHBlcmlvZCA/PyB0aGlzLmdsb2JhbERlZmF1bHRzLnBlcmlvZCA/PyBEZWZhdWx0TWV0cmljUGVyaW9kLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEZhY3RvcnkgbWV0aG9kIHRoYXQgY3JlYXRlcyBhIG1ldHJpYyBzZWFyY2ggcXVlcnkuIFRoZSBtZXRyaWMgcHJvcGVydGllcyB3aWxsIGFscmVhZHkgYmUgdXBkYXRlZCB0byBjb21wbHkgd2l0aCB0aGUgZ2xvYmFsIGRlZmF1bHRzLlxuICAgKiBJZiB5b3Ugd2FudCB0byBtYXRjaCBcImFueSB2YWx1ZVwiIG9mIGEgc3BlY2lmaWMgZGltZW5zaW9uLCBwbGVhc2UgdXNlIGB1bmRlZmluZWRgIHZhbHVlIHJlcHJlc2VudGF0aW9uIGluIHlvdXIgY29uc3VtZXIgbGFuZ3VhZ2UuXG4gICAqIChGb3IgZXhhbXBsZSwgYHVuZGVmaW5lZCBhcyBhbnkgYXMgc3RyaW5nYCBpbiBUeXBlU2NyaXB0LCBkdWUgdG8gSlNJSSB0eXBpbmcgcXVpcmtzLilcbiAgICpcbiAgICogQHBhcmFtIHF1ZXJ5IG1ldHJpYyBzZWFyY2ggcXVlcnkgKHRoZSBzYW1lIGFzIHRoZSBzZWFyY2ggcXVlcnkgcHJvbXB0IGluIENsb3VkV2F0Y2ggQVdTIENvbnNvbGUpLCBpdCBtaWdodCBhbHNvIGJlIGVtcHR5XG4gICAqIEBwYXJhbSBkaW1lbnNpb25zTWFwIGRpbWVuc2lvbnMsIGZ1cnRoZXIgbmFycm93aW5nIHRoZSBzZWFyY2ggcmVzdWx0czsgdXNlIGB1bmRlZmluZWRgIGlmIHlvdSB3YW50IHRvIHJlcHJlc2VudCBcImFueSB2YWx1ZVwiIChpbiBUUzogYHVuZGVmaW5lZCBhcyBhbnkgYXMgc3RyaW5nYClcbiAgICogQHBhcmFtIHN0YXRpc3RpYyBhZ2dyZWdhdGlvbiBzdGF0aXN0aWMgdG8gdXNlXG4gICAqIEBwYXJhbSBuYW1lc3BhY2Ugc3BlY2lmeSBhIGN1c3RvbSBuYW1lc3BhY2U7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICogQHBhcmFtIGxhYmVsIHNwZWNpZnkgY3VzdG9tIGxhYmVsIGZvciBzZWFyY2ggbWV0cmljczsgZGVmYXVsdCBpcyBcIiBcIiBhcyBpdCBjYW5ub3QgYmUgZW1wdHkgc3RyaW5nXG4gICAqIEBwYXJhbSBwZXJpb2Qgc3BlY2lmeSBhIGN1c3RvbSBwZXJpb2Q7IGlmIHVuZGVmaW5lZCwgdXNlcyB0aGUgZ2xvYmFsIGRlZmF1bHRcbiAgICovXG4gIGNyZWF0ZU1ldHJpY1NlYXJjaChcbiAgICBxdWVyeTogc3RyaW5nLFxuICAgIGRpbWVuc2lvbnNNYXA6IERpbWVuc2lvbnNNYXAsXG4gICAgc3RhdGlzdGljOiBNZXRyaWNTdGF0aXN0aWMsXG4gICAgbmFtZXNwYWNlPzogc3RyaW5nLFxuICAgIGxhYmVsPzogc3RyaW5nLFxuICAgIHBlcmlvZD86IER1cmF0aW9uXG4gICk6IElNZXRyaWMge1xuICAgIGNvbnN0IGZpbmFsUGVyaW9kID1cbiAgICAgIHBlcmlvZCA/PyB0aGlzLmdsb2JhbERlZmF1bHRzLnBlcmlvZCA/PyBEZWZhdWx0TWV0cmljUGVyaW9kO1xuICAgIGNvbnN0IHNlYXJjaE5hbWVzcGFjZSA9IHRoaXMuZ2V0TmFtZXNwYWNlV2l0aEZhbGxiYWNrKG5hbWVzcGFjZSk7XG4gICAgY29uc3QgbmFtZXNwYWNlUGx1c0RpbWVuc2lvbktleXMgPSBbXG4gICAgICBzZWFyY2hOYW1lc3BhY2UsXG4gICAgICAuLi5PYmplY3Qua2V5cyhkaW1lbnNpb25zTWFwKSxcbiAgICBdLmpvaW4oXCIsXCIpO1xuICAgIGNvbnN0IG1ldHJpY1NjaGVtYSA9IGB7JHtuYW1lc3BhY2VQbHVzRGltZW5zaW9uS2V5c319YDtcblxuICAgIGNvbnN0IGRpbWVuc2lvbktleXNBbmRWYWx1ZXMgPSBPYmplY3QuZW50cmllcyhcbiAgICAgIHRoaXMucmVtb3ZlVW5kZWZpbmVkRW50cmllcyhkaW1lbnNpb25zTWFwKVxuICAgIClcbiAgICAgIC5tYXAoKFtrZXksIHZhbHVlXSkgPT4gYCR7a2V5fT1cIiR7dmFsdWV9XCJgKVxuICAgICAgLmpvaW4oXCIgXCIpO1xuXG4gICAgY29uc3QgZXhwcmVzc2lvbiA9IGBTRUFSQ0goJyR7bWV0cmljU2NoZW1hfSAke2RpbWVuc2lvbktleXNBbmRWYWx1ZXN9ICR7cXVlcnl9JywgJyR7c3RhdGlzdGljfScsICR7ZmluYWxQZXJpb2QudG9TZWNvbmRzKCl9KWA7XG5cbiAgICByZXR1cm4gbmV3IE1hdGhFeHByZXNzaW9uKHtcbiAgICAgIGV4cHJlc3Npb24sXG4gICAgICAvLyBzZWUgaHR0cHM6Ly9naXRodWIuY29tL2F3cy9hd3MtY2RrL2lzc3Vlcy83MjM3XG4gICAgICB1c2luZ01ldHJpY3M6IHt9LFxuICAgICAgLy8gY2Fubm90IGJlIGFuIGVtcHR5IHN0cmluZyBhbmQgdW5kZWZpbmVkIGlzIG5vIGdvb2QgZWl0aGVyXG4gICAgICBsYWJlbDogbGFiZWwgPz8gXCIgXCIsXG4gICAgICBwZXJpb2Q6IGZpbmFsUGVyaW9kLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEZhY3RvcnkgbWV0aG9kIHRoYXQgY3JlYXRlcyBhbm9tYWx5IGRldGVjdGlvbiBvbiBhIG1ldHJpYy5cbiAgICogQW5vbWFseSBvY2N1cnMgd2hlbmV2ZXIgYSBtZXRyaWMgdmFsdWUgZmFsbHMgb3V0c2lkZSBvZiBhIHByZWNvbXB1dGVkIHJhbmdlIG9mIHByZWRpY3RlZCB2YWx1ZXMuXG4gICAqIFRoZSBkZXRlY3Rpb24gZG9lcyBub3QgbmVlZCBhbnkgc2V0dXAuIFRoZSBtb2RlbCB3aWxsIHN0YXJ0IGxlYXJuaW5nIGF1dG9tYXRpY2FsbHkgYW5kIHNob3VsZCBiZSByZWFkeSBpbiBhIGZldyBtaW51dGVzLlxuICAgKiBVc3VhbGx5LCB0aGUgYW5vbWFseSBkZXRlY3Rpb24gaXMgcGFpcmVkIHdpdGggYW4gYWxhcm0uXG4gICAqIEBzZWUgaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL0FtYXpvbkNsb3VkV2F0Y2gvbGF0ZXN0L21vbml0b3JpbmcvQ2xvdWRXYXRjaF9Bbm9tYWx5X0RldGVjdGlvbi5odG1sXG4gICAqXG4gICAqIEBwYXJhbSBtZXRyaWMgbWV0cmljIHRvIGRldGVjdCBhbm9tYWx5IGRldGVjdGlvbiBvZlxuICAgKiBAcGFyYW0gc3RkZXYgc3RhbmRhcmQgZGV2aWF0aW9uLCBiYXNpY2FsbHkgdGhlIHRvbGVyYW5jZSAvIGJhbmQgdGhpY2tuZXNzXG4gICAqIEBwYXJhbSBsYWJlbCBtZXRyaWMgbGFiZWwgKHJlcXVpcmVkLCBhcyB0aGVyZSBpcyBubyByZWFzb25hYmxlIGRlZmF1bHQpXG4gICAqIEBwYXJhbSBjb2xvciBtZXRyaWMgY29sb3I7IGlmIHVuZGVmaW5lZCwgdXNlcyBhIENsb3VkV2F0Y2ggcHJvdmlkZWQgY29sb3IgKHByZWZlcnJlZClcbiAgICogQHBhcmFtIGV4cHJlc3Npb25JZCBleHByZXNzaW9uIElEIG9mIHRoZSBtZXRyaWM7IHVzZXMgYG0xYCBpZiB1bmRlZmluZWRcbiAgICogQHBhcmFtIHBlcmlvZCBzcGVjaWZ5IGEgY3VzdG9tIHBlcmlvZDsgaWYgdW5kZWZpbmVkLCB1c2VzIHRoZSBnbG9iYWwgZGVmYXVsdFxuICAgKi9cbiAgY3JlYXRlTWV0cmljQW5vbWFseURldGVjdGlvbihcbiAgICBtZXRyaWM6IElNZXRyaWMsXG4gICAgc3RkZXY6IG51bWJlcixcbiAgICBsYWJlbDogc3RyaW5nLFxuICAgIGNvbG9yPzogc3RyaW5nLFxuICAgIGV4cHJlc3Npb25JZD86IHN0cmluZyxcbiAgICBwZXJpb2Q/OiBEdXJhdGlvblxuICApOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0IHtcbiAgICBjb25zdCBmaW5hbEV4cHJlc3Npb25JZCA9IGV4cHJlc3Npb25JZCA/PyBcIm0xXCI7XG4gICAgY29uc3QgdXNpbmdNZXRyaWNzOiBSZWNvcmQ8c3RyaW5nLCBJTWV0cmljPiA9IHt9O1xuICAgIHVzaW5nTWV0cmljc1tmaW5hbEV4cHJlc3Npb25JZF0gPSBtZXRyaWM7XG4gICAgcmV0dXJuIG5ldyBBbm9tYWx5RGV0ZWN0aW9uTWF0aEV4cHJlc3Npb24oe1xuICAgICAgbGFiZWwsXG4gICAgICBjb2xvcixcbiAgICAgIHVzaW5nTWV0cmljcyxcbiAgICAgIGV4cHJlc3Npb246IGBBTk9NQUxZX0RFVEVDVElPTl9CQU5EKCR7ZmluYWxFeHByZXNzaW9uSWR9LCR7c3RkZXZ9KWAsXG4gICAgICBwZXJpb2Q6IHBlcmlvZCA/PyB0aGlzLmdsb2JhbERlZmF1bHRzLnBlcmlvZCA/PyBEZWZhdWx0TWV0cmljUGVyaW9kLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkYXB0cyBwcm9wZXJ0aWVzIG9mIGEgZm9yZWlnbiBtZXRyaWMgKG1ldHJpYyBjcmVhdGVkIG91dHNpZGUgb2YgdGhpcyBtZXRyaWMgZmFjdG9yeSkgdG8gY29tcGx5IHdpdGggdGhlIGdsb2JhbCBkZWZhdWx0cy5cbiAgICogTWlnaHQgbW9kaWZ5IG5hbWVzcGFjZSBhbmQgbWV0cmljIHBlcmlvZC5cbiAgICpcbiAgICogQHBhcmFtIG1ldHJpYyBtZXRyaWMgdG8gYmUgYWRhcHRlZFxuICAgKi9cbiAgYWRhcHRNZXRyaWMobWV0cmljOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0KTogTWV0cmljV2l0aEFsYXJtU3VwcG9ydCB7XG4gICAgcmV0dXJuIG1ldHJpYy53aXRoKHtcbiAgICAgIHBlcmlvZDogdGhpcy5nbG9iYWxEZWZhdWx0cy5wZXJpb2QgPz8gRGVmYXVsdE1ldHJpY1BlcmlvZCxcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGFwdHMgcHJvcGVydGllcyBvZiBhIGZvcmVpZ24gbWV0cmljIChtZXRyaWMgY3JlYXRlZCBvdXRzaWRlIG9mIHRoaXMgbWV0cmljIGZhY3RvcnkpIHRvIGNvbXBseSB3aXRoIHRoZSBnbG9iYWwgZGVmYXVsdHMuXG4gICAqIE1pZ2h0IG1vZGlmeSBuYW1lc3BhY2UuIFByZXNlcnZlcyBtZXRyaWMgcGVyaW9kLlxuICAgKlxuICAgKiBAcGFyYW0gbWV0cmljIG1ldHJpYyB0byBiZSBhZGFwdGVkXG4gICAqL1xuICBhZGFwdE1ldHJpY1ByZXNlcnZpbmdQZXJpb2QoXG4gICAgbWV0cmljOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0XG4gICk6IE1ldHJpY1dpdGhBbGFybVN1cHBvcnQge1xuICAgIHJldHVybiBtZXRyaWM7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG1ldHJpYyBtYXRoIGV4cHJlc3Npb24gdGhhdCBtdWx0aXBsaWVzIHRoZSBnaXZlbiBtZXRyaWMgYnkgZ2l2ZW4gY29lZmZpY2llbnQuXG4gICAqIERvZXMgbm90aGluZyBpZiB0aGUgbXVsdGlwbGllciBpcyBvbmUuIFByZXNlcnZlcyB0aGUgbWV0cmljIHBlcmlvZC5cbiAgICpcbiAgICogQHBhcmFtIG1ldHJpYyBtZXRyaWMgdG8gbXVsdGlwbHlcbiAgICogQHBhcmFtIG11bHRpcGxpZXIgbXVsdGlwbGllciAobXVzdCBiZSA+IDEpXG4gICAqIEBwYXJhbSBsYWJlbCBleHByZXNzaW9uIGxhYmVsXG4gICAqIEBwYXJhbSBleHByZXNzaW9uSWQgZXhwcmVzc2lvbiBJRCBvZiB0aGUgbWV0cmljOyB1c2VzIGBtMWAgaWYgdW5kZWZpbmVkXG4gICAqL1xuICBtdWx0aXBseU1ldHJpYyhcbiAgICBtZXRyaWM6IE1ldHJpY1dpdGhBbGFybVN1cHBvcnQsXG4gICAgbXVsdGlwbGllcjogbnVtYmVyLFxuICAgIGxhYmVsOiBzdHJpbmcsXG4gICAgZXhwcmVzc2lvbklkPzogc3RyaW5nXG4gICk6IE1ldHJpY1dpdGhBbGFybVN1cHBvcnQge1xuICAgIGlmIChtdWx0aXBsaWVyID09IDEpIHtcbiAgICAgIHJldHVybiBtZXRyaWM7XG4gICAgfSBlbHNlIGlmIChtdWx0aXBsaWVyIDwgMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiTXVsdGlwbGllciBtdXN0IGJlIGdyZWF0ZXIgdGhhbiBvbmUuXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBmaW5hbEV4cHJlc3Npb25JZCA9IGV4cHJlc3Npb25JZCA/PyBcIm0xXCI7XG4gICAgICBjb25zdCB1c2luZ01ldHJpY3M6IFJlY29yZDxzdHJpbmcsIElNZXRyaWM+ID0ge307XG4gICAgICB1c2luZ01ldHJpY3NbZmluYWxFeHByZXNzaW9uSWRdID0gbWV0cmljO1xuICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlTWV0cmljTWF0aChcbiAgICAgICAgYCR7ZmluYWxFeHByZXNzaW9uSWR9ICogJHttdWx0aXBsaWVyfWAsXG4gICAgICAgIHVzaW5nTWV0cmljcyxcbiAgICAgICAgbGFiZWwsXG4gICAgICAgIG1ldHJpYy5jb2xvcixcbiAgICAgICAgbWV0cmljLnBlcmlvZFxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG1ldHJpYyBtYXRoIGV4cHJlc3Npb24gdGhhdCBkaXZpZGVzIHRoZSBnaXZlbiBtZXRyaWMgYnkgZ2l2ZW4gY29lZmZpY2llbnQuXG4gICAqIERvZXMgbm90aGluZyBpZiB0aGUgZGl2aXNvciBpcyBvbmUuIFByZXNlcnZlcyB0aGUgbWV0cmljIHBlcmlvZC5cbiAgICpcbiAgICogQHBhcmFtIG1ldHJpYyBtZXRyaWMgdG8gbXVsdGlwbHlcbiAgICogQHBhcmFtIGRpdmlzb3IgZGl2aXNvciAobXVzdCBiZSA+IDEpXG4gICAqIEBwYXJhbSBsYWJlbCBleHByZXNzaW9uIGxhYmVsXG4gICAqIEBwYXJhbSBleHByZXNzaW9uSWQgZXhwcmVzc2lvbiBJRCBvZiB0aGUgbWV0cmljOyB1c2VzIGBtMWAgaWYgdW5kZWZpbmVkXG4gICAqL1xuICBkaXZpZGVNZXRyaWMoXG4gICAgbWV0cmljOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0LFxuICAgIGRpdmlzb3I6IG51bWJlcixcbiAgICBsYWJlbDogc3RyaW5nLFxuICAgIGV4cHJlc3Npb25JZD86IHN0cmluZ1xuICApOiBNZXRyaWNXaXRoQWxhcm1TdXBwb3J0IHtcbiAgICBpZiAoZGl2aXNvciA9PSAxKSB7XG4gICAgICByZXR1cm4gbWV0cmljO1xuICAgIH0gZWxzZSBpZiAoZGl2aXNvciA8IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkRpdmlzb3IgbXVzdCBiZSBncmVhdGVyIHRoYW4gb25lLlwiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgZmluYWxFeHByZXNzaW9uSWQgPSBleHByZXNzaW9uSWQgPz8gXCJtMVwiO1xuICAgICAgY29uc3QgdXNpbmdNZXRyaWNzOiBSZWNvcmQ8c3RyaW5nLCBJTWV0cmljPiA9IHt9O1xuICAgICAgdXNpbmdNZXRyaWNzW2ZpbmFsRXhwcmVzc2lvbklkXSA9IG1ldHJpYztcbiAgICAgIHJldHVybiB0aGlzLmNyZWF0ZU1ldHJpY01hdGgoXG4gICAgICAgIGAke2ZpbmFsRXhwcmVzc2lvbklkfSAvICR7ZGl2aXNvcn1gLFxuICAgICAgICB1c2luZ01ldHJpY3MsXG4gICAgICAgIGxhYmVsLFxuICAgICAgICBtZXRyaWMuY29sb3IsXG4gICAgICAgIG1ldHJpYy5wZXJpb2RcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBtZXRyaWMgbWF0aCBleHByZXNzaW9uIHRoYXQgY29tcHV0ZXMgYSByYXRlIGZyb20gYSByZWd1bGFyIG1ldHJpYy5cbiAgICogRm9yIGV4YW1wbGUsIGl0IGFsbG93cyB5b3UgdG8gY29tcHV0ZSByYXRlIHBlciBzZWNvbmQgKFRQUyksIHBlciBtaW51dGUsIG9yIGp1c3QgYW4gYXZlcmFnZSBvZiB5b3VyIHRyYW5zYWN0aW9ucy5cbiAgICpcbiAgICogQHBhcmFtIG1ldHJpYyBtZXRyaWMgdG8gY2FsY3VsYXRlIHRoZSByYXRlIGZyb21cbiAgICogQHBhcmFtIG1ldGhvZCByYXRlIGNvbXB1dGF0aW9uIG1ldGhvZFxuICAgKiBAcGFyYW0gYWRkU3RhdHNUb0xhYmVsIGFkZCBkZXRhaWxlZCBzdGF0aXN0aWNzIChtaW4sIG1heCwgYXZlcmFnZSkgdG8gdGhlIGxhYmVsXG4gICAqIEBwYXJhbSBleHByZXNzaW9uSWQgZXhwcmVzc2lvbiBJRCBvZiB0aGUgbWV0cmljOyB1c2VzIGBtMWAgaWYgdW5kZWZpbmVkXG4gICAqIEBwYXJhbSBmaWxsV2l0aFplcm9lcyBpZiBUUlVFLCB0aGUgZmluYWwgbWV0cmljIHdpbGwgYmUgemVyby1maWxsZWQgKDAgb24gbm8gZGF0YSk7IGZhbHNlIGlmIHVuZGVmaW5lZFxuICAgKi9cbiAgdG9SYXRlKFxuICAgIG1ldHJpYzogTWV0cmljV2l0aEFsYXJtU3VwcG9ydCxcbiAgICBtZXRob2Q6IFJhdGVDb21wdXRhdGlvbk1ldGhvZCxcbiAgICBhZGRTdGF0c1RvTGFiZWw/OiBib29sZWFuLFxuICAgIGV4cHJlc3Npb25JZD86IHN0cmluZyxcbiAgICBmaWxsV2l0aFplcm9lcz86IGJvb2xlYW5cbiAgKTogTWV0cmljV2l0aEFsYXJtU3VwcG9ydCB7XG4gICAgY29uc3QgZmluYWxFeHByZXNzaW9uSWQgPSBleHByZXNzaW9uSWQgPz8gXCJtMVwiO1xuICAgIGNvbnN0IGxhYmVsUHJlZml4ID0gbWV0cmljLmxhYmVsID8/IFwiUmF0ZVwiO1xuXG4gICAgY29uc3Qgc3RhdHNJbkxhYmVsOiBzdHJpbmdbXSA9IFtdO1xuICAgIGlmIChhZGRTdGF0c1RvTGFiZWwgPz8gZmFsc2UpIHtcbiAgICAgIHN0YXRzSW5MYWJlbC5wdXNoKFwibWluOiAke01JTn1cIik7XG4gICAgICBzdGF0c0luTGFiZWwucHVzaChcIm1heDogJHtNQVh9XCIpO1xuICAgICAgaWYgKG1ldGhvZCAhPT0gUmF0ZUNvbXB1dGF0aW9uTWV0aG9kLkFWRVJBR0UpIHtcbiAgICAgICAgLy8gb25seSBhZGQgYXZlcmFnZSBpZiBkbyBub3QgaGF2ZSBpdCBhbHJlYWR5XG4gICAgICAgIHN0YXRzSW5MYWJlbC5wdXNoKFwiYXZnOiAke0FWR31cIik7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgZmluYWxFeHByZXNzaW9uSWRaZXJvZWQgPVxuICAgICAgZmlsbFdpdGhaZXJvZXMgPz8gZmFsc2VcbiAgICAgICAgPyBgRklMTCgke2ZpbmFsRXhwcmVzc2lvbklkfSwwKWBcbiAgICAgICAgOiBmaW5hbEV4cHJlc3Npb25JZDtcbiAgICBjb25zdCBsYWJlbEFwcGVuZGl4ID1cbiAgICAgIHN0YXRzSW5MYWJlbC5sZW5ndGggPiAwID8gYCAoJHtzdGF0c0luTGFiZWwuam9pbihcIiwgXCIpfSlgIDogXCJcIjtcblxuICAgIHN3aXRjaCAobWV0aG9kKSB7XG4gICAgICBjYXNlIFJhdGVDb21wdXRhdGlvbk1ldGhvZC5BVkVSQUdFOlxuICAgICAgICBjb25zdCBhdmdMYWJlbCA9IGAke2xhYmVsUHJlZml4fSAoYXZnKSR7bGFiZWxBcHBlbmRpeH1gO1xuICAgICAgICBjb25zdCBhdmdNZXRyaWMgPSBtZXRyaWMud2l0aCh7XG4gICAgICAgICAgbGFiZWw6IGF2Z0xhYmVsLFxuICAgICAgICAgIHN0YXRpc3RpYzogTWV0cmljU3RhdGlzdGljLkFWRVJBR0UsXG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoZmlsbFdpdGhaZXJvZXMgPz8gZmFsc2UpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcy5jcmVhdGVNZXRyaWNNYXRoKFxuICAgICAgICAgICAgZmluYWxFeHByZXNzaW9uSWRaZXJvZWQsXG4gICAgICAgICAgICB7IFtmaW5hbEV4cHJlc3Npb25JZF06IGF2Z01ldHJpYyB9LFxuICAgICAgICAgICAgYXZnTGFiZWwsXG4gICAgICAgICAgICBhdmdNZXRyaWMuY29sb3IsXG4gICAgICAgICAgICBhdmdNZXRyaWMucGVyaW9kXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gYXZnTWV0cmljO1xuICAgICAgY2FzZSBSYXRlQ29tcHV0YXRpb25NZXRob2QuUEVSX1NFQ09ORDpcbiAgICAgICAgbGV0IHBlclNlY29uZExhYmVsID0gYCR7bGFiZWxQcmVmaXh9L3Mke2xhYmVsQXBwZW5kaXh9YDtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGxhYmVsUHJlZml4ID09PSBcIlJlcXVlc3RzXCIgfHxcbiAgICAgICAgICBsYWJlbFByZWZpeCA9PT0gXCJJbnZvY2F0aW9uc1wiIHx8XG4gICAgICAgICAgbGFiZWxQcmVmaXggPT09IFwiVHJhbnNhY3Rpb25zXCJcbiAgICAgICAgKSB7XG4gICAgICAgICAgLy8gY3VycmVudGx5LCBrZXB0IGFzIFwiVFBTXCIgdG8gcmVkdWNlIG51bWJlciBvZiBzbmFwc2hvdCBjaGFuZ2VzXG4gICAgICAgICAgcGVyU2Vjb25kTGFiZWwgPSBgVFBTJHtsYWJlbEFwcGVuZGl4fWA7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlTWV0cmljTWF0aChcbiAgICAgICAgICBgJHtmaW5hbEV4cHJlc3Npb25JZFplcm9lZH0gLyBQRVJJT0QoJHtmaW5hbEV4cHJlc3Npb25JZH0pYCxcbiAgICAgICAgICB7IFtmaW5hbEV4cHJlc3Npb25JZF06IG1ldHJpYyB9LFxuICAgICAgICAgIHBlclNlY29uZExhYmVsLFxuICAgICAgICAgIG1ldHJpYy5jb2xvcixcbiAgICAgICAgICBtZXRyaWMucGVyaW9kXG4gICAgICAgICk7XG4gICAgICBjYXNlIFJhdGVDb21wdXRhdGlvbk1ldGhvZC5QRVJfTUlOVVRFOlxuICAgICAgICByZXR1cm4gdGhpcy5jcmVhdGVNZXRyaWNNYXRoKFxuICAgICAgICAgIGAoNjAgKiAke2ZpbmFsRXhwcmVzc2lvbklkWmVyb2VkfSkgLyBQRVJJT0QoJHtmaW5hbEV4cHJlc3Npb25JZH0pYCxcbiAgICAgICAgICB7IFtmaW5hbEV4cHJlc3Npb25JZF06IG1ldHJpYyB9LFxuICAgICAgICAgIGAke2xhYmVsUHJlZml4fS9tJHtsYWJlbEFwcGVuZGl4fWAsXG4gICAgICAgICAgbWV0cmljLmNvbG9yLFxuICAgICAgICAgIG1ldHJpYy5wZXJpb2RcbiAgICAgICAgKTtcbiAgICAgIGNhc2UgUmF0ZUNvbXB1dGF0aW9uTWV0aG9kLlBFUl9IT1VSOlxuICAgICAgICByZXR1cm4gdGhpcy5jcmVhdGVNZXRyaWNNYXRoKFxuICAgICAgICAgIGAoMzYwMCAqICR7ZmluYWxFeHByZXNzaW9uSWRaZXJvZWR9KSAvIFBFUklPRCgke2ZpbmFsRXhwcmVzc2lvbklkfSlgLFxuICAgICAgICAgIHsgW2ZpbmFsRXhwcmVzc2lvbklkXTogbWV0cmljIH0sXG4gICAgICAgICAgYCR7bGFiZWxQcmVmaXh9L2gke2xhYmVsQXBwZW5kaXh9YCxcbiAgICAgICAgICBtZXRyaWMuY29sb3IsXG4gICAgICAgICAgbWV0cmljLnBlcmlvZFxuICAgICAgICApO1xuICAgICAgY2FzZSBSYXRlQ29tcHV0YXRpb25NZXRob2QuUEVSX0RBWTpcbiAgICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlTWV0cmljTWF0aChcbiAgICAgICAgICBgKDg2NDAwICogJHtmaW5hbEV4cHJlc3Npb25JZFplcm9lZH0pIC8gUEVSSU9EKCR7ZmluYWxFeHByZXNzaW9uSWR9KWAsXG4gICAgICAgICAgeyBbZmluYWxFeHByZXNzaW9uSWRdOiBtZXRyaWMgfSxcbiAgICAgICAgICBgJHtsYWJlbFByZWZpeH0vZCR7bGFiZWxBcHBlbmRpeH1gLFxuICAgICAgICAgIG1ldHJpYy5jb2xvcixcbiAgICAgICAgICBtZXRyaWMucGVyaW9kXG4gICAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGdpdmVuIG5hbWVzcGFjZSAoaWYgZGVmaW5lZCkgb3IgdGhlIGdsb2JhbCBuYW1lc3BhY2UgYXMgYSBmYWxsYmFjay5cbiAgICogSWYgdGhlcmUgaXMgbm8gbmFtZXNwYWNlIHRvIGZhbGxiYWNrIHRvIChuZWl0aGVyIHRoZSBjdXN0b20gb3IgdGhlIGRlZmF1bHQgb25lKSwgaXQgd2lsbCBmYWlsLlxuICAgKiBAcGFyYW0gdmFsdWUgY3VzdG9tIG5hbWVzcGFjZVxuICAgKi9cbiAgZ2V0TmFtZXNwYWNlV2l0aEZhbGxiYWNrKHZhbHVlPzogc3RyaW5nKSB7XG4gICAgY29uc3QgbmFtZXNwYWNlID0gdmFsdWUgPz8gdGhpcy5nbG9iYWxEZWZhdWx0cy5uYW1lc3BhY2U7XG4gICAgaWYgKCFuYW1lc3BhY2UpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJUaGVyZSBpcyBubyBjdXN0b20gbmFtZXNwYWNlIGRlZmluZWQuIFBsZWFzZSBzcGVjaWZ5IGl0IGluIHlvdXIgZmFjdG9yeSBkZWZhdWx0cy5cIlxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIG5hbWVzcGFjZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBIZWxwZXIgbWV0aG9kIHRoYXQgaGVscHMgdG8gc2FuaXRpemUgdGhlIGdpdmVuIGV4cHJlc3Npb24gSUQgYW5kIHJlbW92ZXMgYWxsIGludmFsaWQgY2hhcmFjdGVycy5cbiAgICogVmFsaWQgZXhwcmVzc2lvbiBJRCByZWdleHAgaXMgdGhlIGZvbGxvd2luZzogXlthLXpdW2EtekEtWjAtOV9dKiRcbiAgICogQXMgdGhpcyBpcyBqdXN0IHRvIHZhbGlkYXRlIGEgc3VmZml4IGFuZCBub3QgdGhlIHdob2xlIElELCB3ZSBkbyBub3QgaGF2ZSB0byB2ZXJpZnkgdGhlIGZpcnN0IGxvd2VyIGNhc2UgbGV0dGVyLlxuICAgKiBAcGFyYW0gZXhwcmVzc2lvbklkIGV4cHJlc3Npb24gSUQgdG8gc2FuaXRpemVcbiAgICovXG4gIHNhbml0aXplTWV0cmljRXhwcmVzc2lvbklkU3VmZml4KGV4cHJlc3Npb25JZDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGV4cHJlc3Npb25JZC5yZXBsYWNlKC9bXjAtOWEtel9dL2dpLCBcIlwiKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXJnZXMgdGhlIGdpdmVuIGFkZGl0aW9uYWwgZGltZW5zaW9ucyB0byB0aGUgZ2l2ZW4gdGFyZ2V0IGRpbWVuc2lvbiBoYXNoLlxuICAgKiBBbGwgZXhpc3RpbmcgZGltZW5zaW9ucyB3aXRoIHRoZSBzYW1lIGtleSBhcmUgcmVwbGFjZWQuXG4gICAqIEBwYXJhbSB0YXJnZXQgdGFyZ2V0IGRpbWVuc2lvbiBoYXNoIHRvIHVwZGF0ZVxuICAgKiBAcGFyYW0gYWRkaXRpb25hbERpbWVuc2lvbnMgYWRkaXRpb25hbCBkaW1lbnNpb25zXG4gICAqL1xuICBhZGRBZGRpdGlvbmFsRGltZW5zaW9ucyhcbiAgICB0YXJnZXQ6IERpbWVuc2lvbnNNYXAsXG4gICAgYWRkaXRpb25hbERpbWVuc2lvbnM6IERpbWVuc2lvbnNNYXBcbiAgKSB7XG4gICAgLy8gQWRkIGFkZGl0aW9uYWwgZGltZW5zaW9ucyBpbiB0aGUgc2VhcmNoIHF1ZXJ5XG4gICAgT2JqZWN0LmtleXMoYWRkaXRpb25hbERpbWVuc2lvbnMpLmZvckVhY2goKGtleSkgPT4ge1xuICAgICAgdGFyZ2V0W2tleV0gPSBhZGRpdGlvbmFsRGltZW5zaW9uc1trZXldO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYWxsIGVudHJpZXMgZnJvbSB0aGUgZ2l2ZW4gZGltZW5zaW9uIGhhc2ggdGhhdCBjb250YWluIGFuIHVuZGVmaW5lZCB2YWx1ZS5cbiAgICogQHBhcmFtIGRpbWVuc2lvbnNNYXAgZGltZW5zaW9ucyBtYXAgdG8gdXBkYXRlXG4gICAqL1xuICBwcml2YXRlIHJlbW92ZVVuZGVmaW5lZEVudHJpZXMoZGltZW5zaW9uc01hcDogRGltZW5zaW9uc01hcCkge1xuICAgIGNvbnN0IGNvcHk6IERpbWVuc2lvbnNNYXAgPSB7fTtcblxuICAgIE9iamVjdC5lbnRyaWVzKGRpbWVuc2lvbnNNYXApXG4gICAgICAuZmlsdGVyKChbXywgdmFsdWVdKSA9PiB2YWx1ZSAhPT0gdW5kZWZpbmVkKVxuICAgICAgLmZvckVhY2goKFtrZXksIHZhbHVlXSkgPT4gKGNvcHlba2V5XSA9IHZhbHVlKSk7XG5cbiAgICByZXR1cm4gY29weTtcbiAgfVxufVxuIl19