construct-hub 0.3.313 → 0.3.316
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +1 -0
- package/.jsii +199 -93
- package/API.md +86 -0
- package/changelog.md +2 -2
- package/lib/api.d.ts +1 -0
- package/lib/api.js +2 -1
- package/lib/backend/catalog-builder/index.d.ts +5 -0
- package/lib/backend/catalog-builder/index.js +2 -1
- package/lib/backend/deny-list/deny-list.d.ts +2 -0
- package/lib/backend/deny-list/deny-list.js +2 -1
- package/lib/backend/deny-list/prune.d.ts +5 -0
- package/lib/backend/deny-list/prune.js +3 -1
- package/lib/backend/ingestion/index.d.ts +2 -0
- package/lib/backend/ingestion/index.js +3 -1
- package/lib/backend/inventory/index.d.ts +5 -0
- package/lib/backend/inventory/index.js +3 -1
- package/lib/backend/orchestration/index.d.ts +5 -0
- package/lib/backend/orchestration/index.js +3 -1
- package/lib/construct-hub.js +13 -3
- package/lib/monitoring/index.js +1 -1
- package/lib/overview-dashboard/api.d.ts +21 -0
- package/lib/overview-dashboard/api.js +3 -0
- package/lib/overview-dashboard/index.d.ts +53 -0
- package/lib/overview-dashboard/index.js +168 -0
- package/lib/overview-dashboard/sqs-dlq-stats-widget-function.d.ts +7 -0
- package/lib/overview-dashboard/sqs-dlq-stats-widget-function.js +21 -0
- package/lib/overview-dashboard/sqs-dlq-stats-widget-function.lambda.bundle/index.js +181 -0
- package/lib/overview-dashboard/sqs-dlq-stats-widget-function.lambda.bundle/index.js.map +7 -0
- package/lib/overview-dashboard/sqs-dlq-stats-widget-function.lambda.d.ts +56 -0
- package/lib/overview-dashboard/sqs-dlq-stats-widget-function.lambda.js +147 -0
- package/lib/overview-dashboard/sqs-dlq-widget.d.ts +59 -0
- package/lib/overview-dashboard/sqs-dlq-widget.js +82 -0
- package/lib/package-source.d.ts +5 -0
- package/lib/package-source.js +1 -1
- package/lib/package-sources/code-artifact.d.ts +1 -1
- package/lib/package-sources/code-artifact.js +5 -3
- package/lib/package-sources/npmjs.d.ts +1 -1
- package/lib/package-sources/npmjs.js +7 -3
- package/lib/package-tag/index.js +3 -3
- package/lib/package-tag-group/index.js +2 -2
- package/lib/preload-file/index.js +1 -1
- package/lib/s3/storage.js +1 -1
- package/lib/spdx-license.js +1 -1
- package/lib/webapp/index.d.ts +5 -0
- package/lib/webapp/index.js +2 -1
- package/package.json +3 -1
- package/releasetag.txt +1 -1
- package/version.txt +1 -1
@@ -0,0 +1,21 @@
|
|
1
|
+
import { IFunction } from '@aws-cdk/aws-lambda';
|
2
|
+
import { IQueue } from '@aws-cdk/aws-sqs';
|
3
|
+
/**
|
4
|
+
* ConstructHub overview dashboard exposed to extension points.
|
5
|
+
*/
|
6
|
+
export interface IOverviewDashboard {
|
7
|
+
/**
|
8
|
+
* Adds widgets to overview dashboard with link to the dashboard and number of visible messages.
|
9
|
+
* @param name of the DLQ that will be used in the dashboard
|
10
|
+
* @param deadLetterQueue Dead Letter Queue to be used in the dashboard
|
11
|
+
* @param reDriveFunction a lambda function that will be used to re-drive the DLQ
|
12
|
+
*/
|
13
|
+
addDLQMetricToDashboard(name: string, deadLetterQueue: IQueue, reDriveFunction?: IFunction): void;
|
14
|
+
/**
|
15
|
+
* Adds a metric widget to the overview dashboard showing the total number concurrent executions
|
16
|
+
* of a Lambda function and the percentage of SERVICE_QUOTA utilized by the function. This can be
|
17
|
+
* used to see which function has the most impact of the service quota.
|
18
|
+
* @param fn Lambda function to be monitored
|
19
|
+
*/
|
20
|
+
addConcurrentExecutionMetricToDashboard(fn: IFunction, name?: string): void;
|
21
|
+
}
|
@@ -0,0 +1,3 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL292ZXJ2aWV3LWRhc2hib2FyZC9hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IElGdW5jdGlvbiB9IGZyb20gJ0Bhd3MtY2RrL2F3cy1sYW1iZGEnO1xuaW1wb3J0IHsgSVF1ZXVlIH0gZnJvbSAnQGF3cy1jZGsvYXdzLXNxcyc7XG5cbi8qKlxuICogQ29uc3RydWN0SHViIG92ZXJ2aWV3IGRhc2hib2FyZCBleHBvc2VkIHRvIGV4dGVuc2lvbiBwb2ludHMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSU92ZXJ2aWV3RGFzaGJvYXJkIHtcbiAgLyoqXG4gICAqIEFkZHMgd2lkZ2V0cyB0byBvdmVydmlldyBkYXNoYm9hcmQgd2l0aCBsaW5rIHRvIHRoZSBkYXNoYm9hcmQgYW5kIG51bWJlciBvZiB2aXNpYmxlIG1lc3NhZ2VzLlxuICAgKiBAcGFyYW0gbmFtZSBvZiB0aGUgRExRIHRoYXQgd2lsbCBiZSB1c2VkIGluIHRoZSBkYXNoYm9hcmRcbiAgICogQHBhcmFtIGRlYWRMZXR0ZXJRdWV1ZSBEZWFkIExldHRlciBRdWV1ZSB0byBiZSB1c2VkIGluIHRoZSBkYXNoYm9hcmRcbiAgICogQHBhcmFtIHJlRHJpdmVGdW5jdGlvbiBhIGxhbWJkYSBmdW5jdGlvbiB0aGF0IHdpbGwgYmUgdXNlZCB0byByZS1kcml2ZSB0aGUgRExRXG4gICAqL1xuICBhZGRETFFNZXRyaWNUb0Rhc2hib2FyZChuYW1lOiBzdHJpbmcsIGRlYWRMZXR0ZXJRdWV1ZTogSVF1ZXVlLCByZURyaXZlRnVuY3Rpb24/OiBJRnVuY3Rpb24pOiB2b2lkO1xuXG4gIC8qKlxuICAgKiBBZGRzIGEgbWV0cmljIHdpZGdldCB0byB0aGUgb3ZlcnZpZXcgZGFzaGJvYXJkIHNob3dpbmcgdGhlIHRvdGFsIG51bWJlciBjb25jdXJyZW50IGV4ZWN1dGlvbnNcbiAgICogb2YgYSBMYW1iZGEgZnVuY3Rpb24gYW5kIHRoZSBwZXJjZW50YWdlIG9mIFNFUlZJQ0VfUVVPVEEgdXRpbGl6ZWQgYnkgdGhlIGZ1bmN0aW9uLiBUaGlzIGNhbiBiZVxuICAgKiB1c2VkIHRvIHNlZSB3aGljaCBmdW5jdGlvbiBoYXMgdGhlIG1vc3QgaW1wYWN0IG9mIHRoZSBzZXJ2aWNlIHF1b3RhLlxuICAgKiBAcGFyYW0gZm4gTGFtYmRhIGZ1bmN0aW9uIHRvIGJlIG1vbml0b3JlZFxuICAgKi9cbiAgYWRkQ29uY3VycmVudEV4ZWN1dGlvbk1ldHJpY1RvRGFzaGJvYXJkKGZuOiBJRnVuY3Rpb24sIG5hbWU/OiBzdHJpbmcpOiB2b2lkO1xuXG59XG4iXX0=
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import { IDistribution } from '@aws-cdk/aws-cloudfront';
|
2
|
+
import { IFunction } from '@aws-cdk/aws-lambda';
|
3
|
+
import { IQueue } from '@aws-cdk/aws-sqs';
|
4
|
+
import { Construct } from '@aws-cdk/core';
|
5
|
+
import { Inventory } from '../backend/inventory';
|
6
|
+
import { IOverviewDashboard } from './api';
|
7
|
+
/**
|
8
|
+
* Properties for OverviewDashboard
|
9
|
+
*/
|
10
|
+
export interface OverviewDashboardProps {
|
11
|
+
readonly lambdaServiceAlarmThreshold?: number;
|
12
|
+
readonly dashboardName?: string;
|
13
|
+
}
|
14
|
+
/**
|
15
|
+
* Construct hub overview
|
16
|
+
*
|
17
|
+
* This construct generates a dashboard to provide Overview of the Construct Hub operation
|
18
|
+
* The dashboard includes details like DLQ, CloudFront errors and Lambda Service Quota
|
19
|
+
*
|
20
|
+
* Components should use the APIs of this module to add widgets to overview dashboard
|
21
|
+
*/
|
22
|
+
export declare class OverviewDashboard extends Construct implements IOverviewDashboard {
|
23
|
+
private static mLambdaUsage;
|
24
|
+
private static mLambdaQuota;
|
25
|
+
private readonly dashboard;
|
26
|
+
private queueMetricWidget?;
|
27
|
+
private cloudFrontMetricWidget?;
|
28
|
+
private readonly lambdaServiceLimitGraph;
|
29
|
+
private readonly lambdaServiceAlarmThreshold;
|
30
|
+
private metricCount;
|
31
|
+
constructor(scope: Construct, id: string, props?: OverviewDashboardProps);
|
32
|
+
/**
|
33
|
+
* Adds a metric widget to the Overview dashboard showing the total number concurrent executions
|
34
|
+
* of a Lambda function and the percentage of SERVICE_QUOTA utilized by the function. This can be
|
35
|
+
* used to see which function has the most impact of the service quota.
|
36
|
+
* @param fn Lambda function to be monitored
|
37
|
+
*/
|
38
|
+
addConcurrentExecutionMetricToDashboard(fn: IFunction, name?: string): void;
|
39
|
+
/**
|
40
|
+
* Adds widgets to Overview dashboard with link to the dashboard and number of visible messages.
|
41
|
+
* @param name of the DLQ that will be used in the dashboard
|
42
|
+
* @param deadLetterQueue Dead Letter Queue to be used in the dashboard
|
43
|
+
* @param reDriveFunction a lambda function that will be used to re-drive the DLQ
|
44
|
+
*/
|
45
|
+
addDLQMetricToDashboard(name: string, deadLetterQueue: IQueue, reDriveFunction?: IFunction): void;
|
46
|
+
/**
|
47
|
+
* Adds a widget to the Overview dashboard showing the number of requests to CloudFront
|
48
|
+
*/
|
49
|
+
addDistributionMetricToDashboard(distribution: IDistribution): void;
|
50
|
+
private addCloudFrontMetricWidget;
|
51
|
+
private addLambdaServiceQuotaWidgetToDashboard;
|
52
|
+
addInventoryMetrics(inventory: Inventory): void;
|
53
|
+
}
|
@@ -0,0 +1,168 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.OverviewDashboard = void 0;
|
4
|
+
const aws_cloudwatch_1 = require("@aws-cdk/aws-cloudwatch");
|
5
|
+
const core_1 = require("@aws-cdk/core");
|
6
|
+
const runbook_url_1 = require("../runbook-url");
|
7
|
+
const sqs_dlq_widget_1 = require("./sqs-dlq-widget");
|
8
|
+
/**
|
9
|
+
* Construct hub overview
|
10
|
+
*
|
11
|
+
* This construct generates a dashboard to provide Overview of the Construct Hub operation
|
12
|
+
* The dashboard includes details like DLQ, CloudFront errors and Lambda Service Quota
|
13
|
+
*
|
14
|
+
* Components should use the APIs of this module to add widgets to overview dashboard
|
15
|
+
*/
|
16
|
+
class OverviewDashboard extends core_1.Construct {
|
17
|
+
constructor(scope, id, props) {
|
18
|
+
var _a;
|
19
|
+
super(scope, id);
|
20
|
+
this.metricCount = 1;
|
21
|
+
this.lambdaServiceAlarmThreshold = (_a = props === null || props === void 0 ? void 0 : props.lambdaServiceAlarmThreshold) !== null && _a !== void 0 ? _a : 70;
|
22
|
+
this.dashboard = new aws_cloudwatch_1.Dashboard(this, 'Overview dashboard', { dashboardName: props === null || props === void 0 ? void 0 : props.dashboardName });
|
23
|
+
this.lambdaServiceLimitGraph = this.addLambdaServiceQuotaWidgetToDashboard();
|
24
|
+
this.dashboard.addWidgets(this.lambdaServiceLimitGraph);
|
25
|
+
}
|
26
|
+
/**
|
27
|
+
* Adds a metric widget to the Overview dashboard showing the total number concurrent executions
|
28
|
+
* of a Lambda function and the percentage of SERVICE_QUOTA utilized by the function. This can be
|
29
|
+
* used to see which function has the most impact of the service quota.
|
30
|
+
* @param fn Lambda function to be monitored
|
31
|
+
*/
|
32
|
+
addConcurrentExecutionMetricToDashboard(fn, name) {
|
33
|
+
const metricName = `m${this.metricCount++}`;
|
34
|
+
const invocationCount = fn.metricInvocations({
|
35
|
+
statistic: aws_cloudwatch_1.Statistic.MAXIMUM,
|
36
|
+
label: name !== null && name !== void 0 ? name : `${fn.functionName}`,
|
37
|
+
});
|
38
|
+
this.lambdaServiceLimitGraph.addRightMetric(new aws_cloudwatch_1.MathExpression({
|
39
|
+
expression: `${metricName} / mLambdaQuota * 100`,
|
40
|
+
usingMetrics: { [metricName]: invocationCount, lambdaQuotaLimit: OverviewDashboard.mLambdaQuota },
|
41
|
+
label: `${name !== null && name !== void 0 ? name : fn.functionName} quota usage %`,
|
42
|
+
}));
|
43
|
+
}
|
44
|
+
/**
|
45
|
+
* Adds widgets to Overview dashboard with link to the dashboard and number of visible messages.
|
46
|
+
* @param name of the DLQ that will be used in the dashboard
|
47
|
+
* @param deadLetterQueue Dead Letter Queue to be used in the dashboard
|
48
|
+
* @param reDriveFunction a lambda function that will be used to re-drive the DLQ
|
49
|
+
*/
|
50
|
+
addDLQMetricToDashboard(name, deadLetterQueue, reDriveFunction) {
|
51
|
+
if (!this.queueMetricWidget) {
|
52
|
+
this.queueMetricWidget = this.queueMetricWidget = new sqs_dlq_widget_1.SQSDLQWidget(this, 'QueueMetricWidget', {
|
53
|
+
queues: [],
|
54
|
+
key: 'QueueMetricWidget',
|
55
|
+
});
|
56
|
+
this.dashboard.addWidgets(this.queueMetricWidget);
|
57
|
+
}
|
58
|
+
this.queueMetricWidget.addQueue(name, deadLetterQueue, reDriveFunction);
|
59
|
+
}
|
60
|
+
/**
|
61
|
+
* Adds a widget to the Overview dashboard showing the number of requests to CloudFront
|
62
|
+
*/
|
63
|
+
addDistributionMetricToDashboard(distribution) {
|
64
|
+
if (!this.cloudFrontMetricWidget) {
|
65
|
+
this.cloudFrontMetricWidget = this.addCloudFrontMetricWidget();
|
66
|
+
this.dashboard.addWidgets(this.cloudFrontMetricWidget);
|
67
|
+
}
|
68
|
+
const totalRequest = new aws_cloudwatch_1.Metric({
|
69
|
+
metricName: 'Requests',
|
70
|
+
namespace: 'AWS/CloudFront',
|
71
|
+
statistic: aws_cloudwatch_1.Statistic.AVERAGE,
|
72
|
+
dimensionsMap: {
|
73
|
+
DistributionId: distribution.distributionId,
|
74
|
+
Region: 'Global',
|
75
|
+
},
|
76
|
+
region: 'us-east-1',
|
77
|
+
});
|
78
|
+
const errorRate4xx = new aws_cloudwatch_1.Metric({
|
79
|
+
metricName: '4xxErrorRate',
|
80
|
+
namespace: 'AWS/CloudFront',
|
81
|
+
statistic: aws_cloudwatch_1.Statistic.AVERAGE,
|
82
|
+
dimensionsMap: {
|
83
|
+
DistributionId: distribution.distributionId,
|
84
|
+
Region: 'Global',
|
85
|
+
},
|
86
|
+
region: 'us-east-1',
|
87
|
+
});
|
88
|
+
const errorRate5xx = new aws_cloudwatch_1.Metric({
|
89
|
+
metricName: '5xxErrorRate',
|
90
|
+
namespace: 'AWS/CloudFront',
|
91
|
+
statistic: aws_cloudwatch_1.Statistic.AVERAGE,
|
92
|
+
dimensionsMap: {
|
93
|
+
DistributionId: distribution.distributionId,
|
94
|
+
Region: 'Global',
|
95
|
+
},
|
96
|
+
region: 'us-east-1',
|
97
|
+
});
|
98
|
+
this.cloudFrontMetricWidget.addLeftMetric(totalRequest);
|
99
|
+
this.cloudFrontMetricWidget.addRightMetric(errorRate4xx);
|
100
|
+
this.cloudFrontMetricWidget.addRightMetric(errorRate5xx);
|
101
|
+
}
|
102
|
+
addCloudFrontMetricWidget() {
|
103
|
+
const widget = new aws_cloudwatch_1.GraphWidget({
|
104
|
+
title: 'CloudFront Metrics',
|
105
|
+
height: 8,
|
106
|
+
width: 12,
|
107
|
+
leftYAxis: { label: 'Requests count' },
|
108
|
+
rightYAxis: { label: 'Request Percent', min: 0, max: 100 },
|
109
|
+
});
|
110
|
+
return widget;
|
111
|
+
}
|
112
|
+
addLambdaServiceQuotaWidgetToDashboard() {
|
113
|
+
const serviceQuotaLimit = new aws_cloudwatch_1.MathExpression({
|
114
|
+
expression: 'mLambdaUsage / mLambdaQuota * 100',
|
115
|
+
label: 'Concurrent executions quota usage %',
|
116
|
+
usingMetrics: { mLambdaUsage: OverviewDashboard.mLambdaUsage, mLambdaQuota: OverviewDashboard.mLambdaQuota },
|
117
|
+
});
|
118
|
+
const alarm = serviceQuotaLimit.createAlarm(this, 'OverviewDashboard/lambdaServiceQuota', {
|
119
|
+
alarmDescription: [
|
120
|
+
`Lambda concurrent execution exceeded ${this.lambdaServiceAlarmThreshold}% of SERVICE_QUOTA`,
|
121
|
+
'',
|
122
|
+
`RunBook: ${runbook_url_1.RUNBOOK_URL}`,
|
123
|
+
'',
|
124
|
+
'Request a service quota increase for lambda functions',
|
125
|
+
].join('\n'),
|
126
|
+
comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
|
127
|
+
evaluationPeriods: 5,
|
128
|
+
threshold: this.lambdaServiceAlarmThreshold,
|
129
|
+
treatMissingData: aws_cloudwatch_1.TreatMissingData.MISSING,
|
130
|
+
});
|
131
|
+
const lambdaServiceQuotaWidget = new aws_cloudwatch_1.GraphWidget({
|
132
|
+
title: 'Lambda concurrent execution quota',
|
133
|
+
height: 8,
|
134
|
+
width: 12,
|
135
|
+
right: [
|
136
|
+
alarm.metric,
|
137
|
+
],
|
138
|
+
rightYAxis: { label: 'Quota Percent', min: 0, max: 100 },
|
139
|
+
rightAnnotations: [{
|
140
|
+
value: this.lambdaServiceAlarmThreshold,
|
141
|
+
}],
|
142
|
+
});
|
143
|
+
return lambdaServiceQuotaWidget;
|
144
|
+
}
|
145
|
+
addInventoryMetrics(inventory) {
|
146
|
+
this.dashboard.addWidgets(new aws_cloudwatch_1.GraphWidget({
|
147
|
+
title: 'Construct Hub Inventory',
|
148
|
+
height: 8,
|
149
|
+
width: 12,
|
150
|
+
left: [
|
151
|
+
inventory.metricPackageCount(),
|
152
|
+
inventory.metricPackageMajorCount(),
|
153
|
+
],
|
154
|
+
leftYAxis: { label: 'Package count' },
|
155
|
+
}));
|
156
|
+
}
|
157
|
+
}
|
158
|
+
exports.OverviewDashboard = OverviewDashboard;
|
159
|
+
OverviewDashboard.mLambdaUsage = new aws_cloudwatch_1.Metric({
|
160
|
+
metricName: 'ConcurrentExecutions',
|
161
|
+
namespace: 'AWS/Lambda',
|
162
|
+
statistic: aws_cloudwatch_1.Statistic.MAXIMUM,
|
163
|
+
});
|
164
|
+
OverviewDashboard.mLambdaQuota = new aws_cloudwatch_1.MathExpression({
|
165
|
+
expression: 'SERVICE_QUOTA(mLambdaUsage)',
|
166
|
+
usingMetrics: { mLambdaUsage: OverviewDashboard.mLambdaUsage },
|
167
|
+
});
|
168
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3ZlcnZpZXctZGFzaGJvYXJkL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLDREQUEwSTtBQUcxSSx3Q0FBMEM7QUFFMUMsZ0RBQTZDO0FBRTdDLHFEQUFnRDtBQVVoRDs7Ozs7OztHQU9HO0FBQ0gsTUFBYSxpQkFBa0IsU0FBUSxnQkFBUztJQXlCOUMsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUE4Qjs7UUFDdEUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUhYLGdCQUFXLEdBQVcsQ0FBQyxDQUFDO1FBSTlCLElBQUksQ0FBQywyQkFBMkIsU0FBRyxLQUFLLGFBQUwsS0FBSyx1QkFBTCxLQUFLLENBQUUsMkJBQTJCLG1DQUFJLEVBQUUsQ0FBQztRQUM1RSxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksMEJBQVMsQ0FBQyxJQUFJLEVBQUUsb0JBQW9CLEVBQUUsRUFBRSxhQUFhLEVBQUUsS0FBSyxhQUFMLEtBQUssdUJBQUwsS0FBSyxDQUFFLGFBQWEsRUFBRSxDQUFDLENBQUM7UUFDcEcsSUFBSSxDQUFDLHVCQUF1QixHQUFHLElBQUksQ0FBQyxzQ0FBc0MsRUFBRSxDQUFDO1FBQzdFLElBQUksQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO0lBQzFELENBQUM7SUFFRDs7Ozs7T0FLRztJQUVJLHVDQUF1QyxDQUFDLEVBQWEsRUFBRSxJQUFhO1FBQ3pFLE1BQU0sVUFBVSxHQUFHLElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7UUFDNUMsTUFBTSxlQUFlLEdBQUcsRUFBRSxDQUFDLGlCQUFpQixDQUFDO1lBQzNDLFNBQVMsRUFBRSwwQkFBUyxDQUFDLE9BQU87WUFDNUIsS0FBSyxFQUFFLElBQUksYUFBSixJQUFJLGNBQUosSUFBSSxHQUFJLEdBQUcsRUFB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SyxDQUFDLE1BQU07YUFDYjtZQUNELFVBQVUsRUFBRSxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFO1lBQ3hELGdCQUFnQixFQUFFLENBQUM7b0JBQ2pCLEtBQUssRUFBRSxJQUFJLENBQUMsMkJBQTJCO2lCQUN4QyxDQUFDO1NBQ0gsQ0FBQyxDQUFDO1FBRUgsT0FBTyx3QkFBd0IsQ0FBQztJQUNsQyxDQUFDO0lBRU0sbUJBQW1CLENBQUMsU0FBb0I7UUFDN0MsSUFBSSxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsSUFBSSw0QkFBVyxDQUFDO1lBQ3hDLEtBQUssRUFBRSx5QkFBeUI7WUFDaEMsTUFBTSxFQUFFLENBQUM7WUFDVCxLQUFLLEVBQUUsRUFBRTtZQUNULElBQUksRUFBRTtnQkFDSixTQUFTLENBQUMsa0JBQWtCLEVBQUU7Z0JBQzlCLFNBQVMsQ0FBQyx1QkFBdUIsRUFBRTthQUNwQztZQUNELFNBQVMsRUFBRSxFQUFFLEtBQUssRUFBRSxlQUFlLEVBQUU7U0FDdEMsQ0FBQyxDQUFDLENBQUM7SUFDTixDQUFDOztBQWpMSCw4Q0FrTEM7QUFqTGdCLDhCQUFZLEdBQVcsSUFBSSx1QkFBTSxDQUFDO0lBQy9DLFVBQVUsRUFBRSxzQkFBc0I7SUFDbEMsU0FBUyxFQUFFLFlBQVk7SUFDdkIsU0FBUyxFQUFFLDBCQUFTLENBQUMsT0FBTztDQUM3QixDQUFDLENBQUM7QUFFWSw4QkFBWSxHQUFtQixJQUFJLCtCQUFjLENBQUM7SUFDL0QsVUFBVSxFQUFFLDZCQUE2QjtJQUN6QyxZQUFZLEVBQUUsRUFBRSxZQUFZLEVBQUUsaUJBQWlCLENBQUMsWUFBWSxFQUFFO0NBQy9ELENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IElEaXN0cmlidXRpb24gfSBmcm9tICdAYXdzLWNkay9hd3MtY2xvdWRmcm9udCc7XG5pbXBvcnQgeyBDb21wYXJpc29uT3BlcmF0b3IsIERhc2hib2FyZCwgR3JhcGhXaWRnZXQsIE1hdGhFeHByZXNzaW9uLCBNZXRyaWMsIFN0YXRpc3RpYywgVHJlYXRNaXNzaW5nRGF0YSB9IGZyb20gJ0Bhd3MtY2RrL2F3cy1jbG91ZHdhdGNoJztcbmltcG9ydCB7IElGdW5jdGlvbiB9IGZyb20gJ0Bhd3MtY2RrL2F3cy1sYW1iZGEnO1xuaW1wb3J0IHsgSVF1ZXVlIH0gZnJvbSAnQGF3cy1jZGsvYXdzLXNxcyc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdAYXdzLWNkay9jb3JlJztcbmltcG9ydCB7IEludmVudG9yeSB9IGZyb20gJy4uL2JhY2tlbmQvaW52ZW50b3J5JztcbmltcG9ydCB7IFJVTkJPT0tfVVJMIH0gZnJvbSAnLi4vcnVuYm9vay11cmwnO1xuaW1wb3J0IHsgSU92ZXJ2aWV3RGFzaGJvYXJkIH0gZnJvbSAnLi9hcGknO1xuaW1wb3J0IHsgU1FTRExRV2lkZ2V0IH0gZnJvbSAnLi9zcXMtZGxxLXdpZGdldCc7XG5cbi8qKlxuICogUHJvcGVydGllcyBmb3IgT3ZlcnZpZXdEYXNoYm9hcmRcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBPdmVydmlld0Rhc2hib2FyZFByb3BzIHtcbiAgcmVhZG9ubHkgbGFtYmRhU2VydmljZUFsYXJtVGhyZXNob2xkPzogbnVtYmVyO1xuICByZWFkb25seSBkYXNoYm9hcmROYW1lPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIENvbnN0cnVjdCBodWIgb3ZlcnZpZXdcbiAqXG4gKiBUaGlzIGNvbnN0cnVjdCBnZW5lcmF0ZXMgYSBkYXNoYm9hcmQgdG8gcHJvdmlkZSBPdmVydmlldyBvZiB0aGUgQ29uc3RydWN0IEh1YiBvcGVyYXRpb25cbiAqIFRoZSBkYXNoYm9hcmQgaW5jbHVkZXMgZGV0YWlscyBsaWtlIERMUSwgQ2xvdWRGcm9udCBlcnJvcnMgYW5kIExhbWJkYSBTZXJ2aWNlIFF1b3RhXG4gKlxuICogQ29tcG9uZW50cyBzaG91bGQgdXNlIHRoZSBBUElzIG9mIHRoaXMgbW9kdWxlIHRvIGFkZCB3aWRnZXRzIHRvIG92ZXJ2aWV3IGRhc2hib2FyZFxuICovXG5leHBvcnQgY2xhc3MgT3ZlcnZpZXdEYXNoYm9hcmQgZXh0ZW5kcyBDb25zdHJ1Y3QgaW1wbGVtZW50cyBJT3ZlcnZpZXdEYXNoYm9hcmQge1xuICBwcml2YXRlIHN0YXRpYyBtTGFtYmRhVXNhZ2U6IE1ldHJpYyA9IG5ldyBNZXRyaWMoe1xuICAgIG1ldHJpY05hbWU6ICdDb25jdXJyZW50RXhlY3V0aW9ucycsXG4gICAgbmFtZXNwYWNlOiAnQVdTL0xhbWJkYScsXG4gICAgc3RhdGlzdGljOiBTdGF0aXN0aWMuTUFYSU1VTSxcbiAgfSk7XG5cbiAgcHJpdmF0ZSBzdGF0aWMgbUxhbWJkYVF1b3RhOiBNYXRoRXhwcmVzc2lvbiA9IG5ldyBNYXRoRXhwcmVzc2lvbih7XG4gICAgZXhwcmVzc2lvbjogJ1NFUlZJQ0VfUVVPVEEobUxhbWJkYVVzYWdlKScsXG4gICAgdXNpbmdNZXRyaWNzOiB7IG1MYW1iZGFVc2FnZTogT3ZlcnZpZXdEYXNoYm9hcmQubUxhbWJkYVVzYWdlIH0sXG4gIH0pO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgZGFzaGJvYXJkOiBEYXNoYm9hcmQ7XG5cbiAgcHJpdmF0ZSBxdWV1ZU1ldHJpY1dpZGdldD86IFNRU0RMUVdpZGdldDtcblxuICBwcml2YXRlIGNsb3VkRnJvbnRNZXRyaWNXaWRnZXQ/OiBHcmFwaFdpZGdldDtcblxuICBwcml2YXRlIHJlYWRvbmx5IGxhbWJkYVNlcnZpY2VMaW1pdEdyYXBoOiBHcmFwaFdpZGdldDtcblxuICBwcml2YXRlIHJlYWRvbmx5IGxhbWJkYVNlcnZpY2VBbGFybVRocmVzaG9sZDogbnVtYmVyO1xuXG5cbiAgcHJpdmF0ZSBtZXRyaWNDb3VudDogbnVtYmVyID0gMTtcblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IE92ZXJ2aWV3RGFzaGJvYXJkUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuICAgIHRoaXMubGFtYmRhU2VydmljZUFsYXJtVGhyZXNob2xkID0gcHJvcHM/LmxhbWJkYVNlcnZpY2VBbGFybVRocmVzaG9sZCA/PyA3MDtcbiAgICB0aGlzLmRhc2hib2FyZCA9IG5ldyBEYXNoYm9hcmQodGhpcywgJ092ZXJ2aWV3IGRhc2hib2FyZCcsIHsgZGFzaGJvYXJkTmFtZTogcHJvcHM/LmRhc2hib2FyZE5hbWUgfSk7XG4gICAgdGhpcy5sYW1iZGFTZXJ2aWNlTGltaXRHcmFwaCA9IHRoaXMuYWRkTGFtYmRhU2VydmljZVF1b3RhV2lkZ2V0VG9EYXNoYm9hcmQoKTtcbiAgICB0aGlzLmRhc2hib2FyZC5hZGRXaWRnZXRzKHRoaXMubGFtYmRhU2VydmljZUxpbWl0R3JhcGgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSBtZXRyaWMgd2lkZ2V0IHRvIHRoZSBPdmVydmlldyBkYXNoYm9hcmQgc2hvd2luZyB0aGUgdG90YWwgbnVtYmVyIGNvbmN1cnJlbnQgZXhlY3V0aW9uc1xuICAgICogb2YgYSBMYW1iZGEgZnVuY3Rpb24gYW5kIHRoZSBwZXJjZW50YWdlIG9mIFNFUlZJQ0VfUVVPVEEgdXRpbGl6ZWQgYnkgdGhlIGZ1bmN0aW9uLiBUaGlzIGNhbiBiZVxuICAgICogdXNlZCB0byBzZWUgd2hpY2ggZnVuY3Rpb24gaGFzIHRoZSBtb3N0IGltcGFjdCBvZiB0aGUgc2VydmljZSBxdW90YS5cbiAgICogQHBhcmFtIGZuIExhbWJkYSBmdW5jdGlvbiB0byBiZSBtb25pdG9yZWRcbiAgICovXG5cbiAgcHVibGljIGFkZENvbmN1cnJlbnRFeGVjdXRpb25NZXRyaWNUb0Rhc2hib2FyZChmbjogSUZ1bmN0aW9uLCBuYW1lPzogc3RyaW5nKSB7XG4gICAgY29uc3QgbWV0cmljTmFtZSA9IGBtJHt0aGlzLm1ldHJpY0NvdW50Kyt9YDtcbiAgICBjb25zdCBpbnZvY2F0aW9uQ291bnQgPSBmbi5tZXRyaWNJbnZvY2F0aW9ucyh7XG4gICAgICBzdGF0aXN0aWM6IFN0YXRpc3RpYy5NQVhJTVVNLFxuICAgICAgbGFiZWw6IG5hbWUgPz8gYCR7Zm4uZnVuY3Rpb25OYW1lfWAsXG4gICAgfSk7XG4gICAgdGhpcy5sYW1iZGFTZXJ2aWNlTGltaXRHcmFwaC5hZGRSaWdodE1ldHJpYyhuZXcgTWF0aEV4cHJlc3Npb24oe1xuICAgICAgZXhwcmVzc2lvbjogYCR7bWV0cmljTmFtZX0gLyBtTGFtYmRhUXVvdGEgKiAxMDBgLFxuICAgICAgdXNpbmdNZXRyaWNzOiB7IFttZXRyaWNOYW1lXTogaW52b2NhdGlvbkNvdW50LCBsYW1iZGFRdW90YUxpbWl0OiBPdmVydmlld0Rhc2hib2FyZC5tTGFtYmRhUXVvdGEgfSxcbiAgICAgIGxhYmVsOiBgJHtuYW1lID8/IGZuLmZ1bmN0aW9uTmFtZX0gcXVvdGEgdXNhZ2UgJWAsXG4gICAgfSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgd2lkZ2V0cyB0byBPdmVydmlldyBkYXNoYm9hcmQgd2l0aCBsaW5rIHRvIHRoZSBkYXNoYm9hcmQgYW5kIG51bWJlciBvZiB2aXNpYmxlIG1lc3NhZ2VzLlxuICAgKiBAcGFyYW0gbmFtZSBvZiB0aGUgRExRIHRoYXQgd2lsbCBiZSB1c2VkIGluIHRoZSBkYXNoYm9hcmRcbiAgICogQHBhcmFtIGRlYWRMZXR0ZXJRdWV1ZSBEZWFkIExldHRlciBRdWV1ZSB0byBiZSB1c2VkIGluIHRoZSBkYXNoYm9hcmRcbiAgICogQHBhcmFtIHJlRHJpdmVGdW5jdGlvbiBhIGxhbWJkYSBmdW5jdGlvbiB0aGF0IHdpbGwgYmUgdXNlZCB0byByZS1kcml2ZSB0aGUgRExRXG4gICAqL1xuICBwdWJsaWMgYWRkRExRTWV0cmljVG9EYXNoYm9hcmQobmFtZTogc3RyaW5nLCBkZWFkTGV0dGVyUXVldWU6IElRdWV1ZSwgcmVEcml2ZUZ1bmN0aW9uPzogSUZ1bmN0aW9uKSB7XG4gICAgaWYgKCF0aGlzLnF1ZXVlTWV0cmljV2lkZ2V0KSB7XG4gICAgICB0aGlzLnF1ZXVlTWV0cmljV2lkZ2V0ID0gdGhpcy5xdWV1ZU1ldHJpY1dpZGdldCA9IG5ldyBTUVNETFFXaWRnZXQodGhpcywgJ1F1ZXVlTWV0cmljV2lkZ2V0Jywge1xuICAgICAgICBxdWV1ZXM6IFtdLFxuICAgICAgICBrZXk6ICdRdWV1ZU1ldHJpY1dpZGdldCcsXG4gICAgICB9KTtcbiAgICAgIHRoaXMuZGFzaGJvYXJkLmFkZFdpZGdldHModGhpcy5xdWV1ZU1ldHJpY1dpZGdldCk7XG4gICAgfVxuICAgIHRoaXMucXVldWVNZXRyaWNXaWRnZXQuYWRkUXVldWUobmFtZSwgZGVhZExldHRlclF1ZXVlLCByZURyaXZlRnVuY3Rpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqICBBZGRzIGEgd2lkZ2V0IHRvIHRoZSBPdmVydmlldyBkYXNoYm9hcmQgc2hvd2luZyB0aGUgbnVtYmVyIG9mIHJlcXVlc3RzIHRvIENsb3VkRnJvbnRcbiAgICovXG5cbiAgcHVibGljIGFkZERpc3RyaWJ1dGlvbk1ldHJpY1RvRGFzaGJvYXJkKGRpc3RyaWJ1dGlvbjogSURpc3RyaWJ1dGlvbikge1xuICAgIGlmICghdGhpcy5jbG91ZEZyb250TWV0cmljV2lkZ2V0KSB7XG4gICAgICB0aGlzLmNsb3VkRnJvbnRNZXRyaWNXaWRnZXQgPSB0aGlzLmFkZENsb3VkRnJvbnRNZXRyaWNXaWRnZXQoKTtcbiAgICAgIHRoaXMuZGFzaGJvYXJkLmFkZFdpZGdldHModGhpcy5jbG91ZEZyb250TWV0cmljV2lkZ2V0KTtcbiAgICB9XG5cbiAgICBjb25zdCB0b3RhbFJlcXVlc3QgPSBuZXcgTWV0cmljKHtcbiAgICAgIG1ldHJpY05hbWU6ICdSZXF1ZXN0cycsXG4gICAgICBuYW1lc3BhY2U6ICdBV1MvQ2xvdWRGcm9udCcsXG4gICAgICBzdGF0aXN0aWM6IFN0YXRpc3RpYy5BVkVSQUdFLFxuICAgICAgZGltZW5zaW9uc01hcDoge1xuICAgICAgICBEaXN0cmlidXRpb25JZDogZGlzdHJpYnV0aW9uLmRpc3RyaWJ1dGlvbklkLFxuICAgICAgICBSZWdpb246ICdHbG9iYWwnLFxuICAgICAgfSxcbiAgICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsIC8vIGdsb2JhbCBtZXRyaWNcbiAgICB9KTtcblxuICAgIGNvbnN0IGVycm9yUmF0ZTR4eCA9IG5ldyBNZXRyaWMoe1xuICAgICAgbWV0cmljTmFtZTogJzR4eEVycm9yUmF0ZScsXG4gICAgICBuYW1lc3BhY2U6ICdBV1MvQ2xvdWRGcm9udCcsXG4gICAgICBzdGF0aXN0aWM6IFN0YXRpc3RpYy5BVkVSQUdFLFxuICAgICAgZGltZW5zaW9uc01hcDoge1xuICAgICAgICBEaXN0cmlidXRpb25JZDogZGlzdHJpYnV0aW9uLmRpc3RyaWJ1dGlvbklkLFxuICAgICAgICBSZWdpb246ICdHbG9iYWwnLFxuICAgICAgfSxcbiAgICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsIC8vIGdsb2JhbCBtZXRyaWNcbiAgICB9KTtcblxuICAgIGNvbnN0IGVycm9yUmF0ZTV4eCA9IG5ldyBNZXRyaWMoe1xuICAgICAgbWV0cmljTmFtZTogJzV4eEVycm9yUmF0ZScsXG4gICAgICBuYW1lc3BhY2U6ICdBV1MvQ2xvdWRGcm9udCcsXG4gICAgICBzdGF0aXN0aWM6IFN0YXRpc3RpYy5BVkVSQUdFLFxuICAgICAgZGltZW5zaW9uc01hcDoge1xuICAgICAgICBEaXN0cmlidXRpb25JZDogZGlzdHJpYnV0aW9uLmRpc3RyaWJ1dGlvbklkLFxuICAgICAgICBSZWdpb246ICdHbG9iYWwnLFxuICAgICAgfSxcbiAgICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsIC8vIGdsb2JhbCBtZXRyaWNcbiAgICB9KTtcblxuICAgIHRoaXMuY2xvdWRGcm9udE1ldHJpY1dpZGdldC5hZGRMZWZ0TWV0cmljKHRvdGFsUmVxdWVzdCk7XG4gICAgdGhpcy5jbG91ZEZyb250TWV0cmljV2lkZ2V0LmFkZFJpZ2h0TWV0cmljKGVycm9yUmF0ZTR4eCk7XG4gICAgdGhpcy5jbG91ZEZyb250TWV0cmljV2lkZ2V0LmFkZFJpZ2h0TWV0cmljKGVycm9yUmF0ZTV4eCk7XG4gIH1cblxuICBwcml2YXRlIGFkZENsb3VkRnJvbnRNZXRyaWNXaWRnZXQoKSB7XG4gICAgY29uc3Qgd2lkZ2V0ID0gbmV3IEdyYXBoV2lkZ2V0KHtcbiAgICAgIHRpdGxlOiAnQ2xvdWRGcm9udCBNZXRyaWNzJyxcbiAgICAgIGhlaWdodDogOCxcbiAgICAgIHdpZHRoOiAxMixcbiAgICAgIGxlZnRZQXhpczogeyBsYWJlbDogJ1JlcXVlc3RzIGNvdW50JyB9LFxuICAgICAgcmlnaHRZQXhpczogeyBsYWJlbDogJ1JlcXVlc3QgUGVyY2VudCcsIG1pbjogMCwgbWF4OiAxMDAgfSxcbiAgICB9KTtcbiAgICByZXR1cm4gd2lkZ2V0O1xuICB9XG5cbiAgcHJpdmF0ZSBhZGRMYW1iZGFTZXJ2aWNlUXVvdGFXaWRnZXRUb0Rhc2hib2FyZCgpIHtcbiAgICBjb25zdCBzZXJ2aWNlUXVvdGFMaW1pdCA9IG5ldyBNYXRoRXhwcmVzc2lvbih7XG4gICAgICBleHByZXNzaW9uOiAnbUxhbWJkYVVzYWdlIC8gbUxhbWJkYVF1b3RhICogMTAwJyxcbiAgICAgIGxhYmVsOiAnQ29uY3VycmVudCBleGVjdXRpb25zIHF1b3RhIHVzYWdlICUnLFxuICAgICAgdXNpbmdNZXRyaWNzOiB7IG1MYW1iZGFVc2FnZTogT3ZlcnZpZXdEYXNoYm9hcmQubUxhbWJkYVVzYWdlLCBtTGFtYmRhUXVvdGE6IE92ZXJ2aWV3RGFzaGJvYXJkLm1MYW1iZGFRdW90YSB9LFxuICAgIH0pO1xuXG4gICAgY29uc3QgYWxhcm0gPSBzZXJ2aWNlUXVvdGFMaW1pdC5jcmVhdGVBbGFybSh0aGlzLCAnT3ZlcnZpZXdEYXNoYm9hcmQvbGFtYmRhU2VydmljZVF1b3RhJywge1xuICAgICAgYWxhcm1EZXNjcmlwdGlvbjogW1xuICAgICAgICBgTGFtYmRhIGNvbmN1cnJlbnQgZXhlY3V0aW9uIGV4Y2VlZGVkICR7dGhpcy5sYW1iZGFTZXJ2aWNlQWxhcm1UaHJlc2hvbGR9JSBvZiBTRVJWSUNFX1FVT1RBYCxcbiAgICAgICAgJycsXG4gICAgICAgIGBSdW5Cb29rOiAke1JVTkJPT0tfVVJMfWAsXG4gICAgICAgICcnLFxuICAgICAgICAnUmVxdWVzdCBhIHNlcnZpY2UgcXVvdGEgaW5jcmVhc2UgZm9yIGxhbWJkYSBmdW5jdGlvbnMnLFxuICAgICAgXS5qb2luKCdcXG4nKSxcbiAgICAgIGNvbXBhcmlzb25PcGVyYXRvcjogQ29tcGFyaXNvbk9wZXJhdG9yLkdSRUFURVJfVEhBTl9USFJFU0hPTEQsXG4gICAgICBldmFsdWF0aW9uUGVyaW9kczogNSxcbiAgICAgIHRocmVzaG9sZDogdGhpcy5sYW1iZGFTZXJ2aWNlQWxhcm1UaHJlc2hvbGQsXG4gICAgICB0cmVhdE1pc3NpbmdEYXRhOiBUcmVhdE1pc3NpbmdEYXRhLk1JU1NJTkcsXG4gICAgfSk7XG5cbiAgICBjb25zdCBsYW1iZGFTZXJ2aWNlUXVvdGFXaWRnZXQgPSBuZXcgR3JhcGhXaWRnZXQoe1xuICAgICAgdGl0bGU6ICdMYW1iZGEgY29uY3VycmVudCBleGVjdXRpb24gcXVvdGEnLFxuICAgICAgaGVpZ2h0OiA4LFxuICAgICAgd2lkdGg6IDEyLFxuICAgICAgcmlnaHQ6IFtcbiAgICAgICAgYWxhcm0ubWV0cmljLFxuICAgICAgXSxcbiAgICAgIHJpZ2h0WUF4aXM6IHsgbGFiZWw6ICdRdW90YSBQZXJjZW50JywgbWluOiAwLCBtYXg6IDEwMCB9LFxuICAgICAgcmlnaHRBbm5vdGF0aW9uczogW3tcbiAgICAgICAgdmFsdWU6IHRoaXMubGFtYmRhU2VydmljZUFsYXJtVGhyZXNob2xkLFxuICAgICAgfV0sXG4gICAgfSk7XG5cbiAgICByZXR1cm4gbGFtYmRhU2VydmljZVF1b3RhV2lkZ2V0O1xuICB9XG5cbiAgcHVibGljIGFkZEludmVudG9yeU1ldHJpY3MoaW52ZW50b3J5OiBJbnZlbnRvcnkpIHtcbiAgICB0aGlzLmRhc2hib2FyZC5hZGRXaWRnZXRzKG5ldyBHcmFwaFdpZGdldCh7XG4gICAgICB0aXRsZTogJ0NvbnN0cnVjdCBIdWIgSW52ZW50b3J5JyxcbiAgICAgIGhlaWdodDogOCxcbiAgICAgIHdpZHRoOiAxMixcbiAgICAgIGxlZnQ6IFtcbiAgICAgICAgaW52ZW50b3J5Lm1ldHJpY1BhY2thZ2VDb3VudCgpLFxuICAgICAgICBpbnZlbnRvcnkubWV0cmljUGFja2FnZU1ham9yQ291bnQoKSxcbiAgICAgIF0sXG4gICAgICBsZWZ0WUF4aXM6IHsgbGFiZWw6ICdQYWNrYWdlIGNvdW50JyB9LFxuICAgIH0pKTtcbiAgfVxufSJdfQ==
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import * as lambda from '@aws-cdk/aws-lambda';
|
2
|
+
import { Construct } from '@aws-cdk/core';
|
3
|
+
export interface SqsDlqStatsWidgetFunctionProps extends lambda.FunctionOptions {
|
4
|
+
}
|
5
|
+
export declare class SqsDlqStatsWidgetFunction extends lambda.SingletonFunction {
|
6
|
+
constructor(scope: Construct, id: string, props?: SqsDlqStatsWidgetFunctionProps);
|
7
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.SqsDlqStatsWidgetFunction = void 0;
|
4
|
+
// ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
|
5
|
+
const path = require("path");
|
6
|
+
const lambda = require("@aws-cdk/aws-lambda");
|
7
|
+
class SqsDlqStatsWidgetFunction extends lambda.SingletonFunction {
|
8
|
+
constructor(scope, id, props) {
|
9
|
+
super(scope, id, {
|
10
|
+
description: 'overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts',
|
11
|
+
...props,
|
12
|
+
uuid: 'fc176846-044f-5e56-baf2-c71723501885',
|
13
|
+
lambdaPurpose: 'SQSDLQStatsWidget-Handler',
|
14
|
+
runtime: lambda.Runtime.NODEJS_14_X,
|
15
|
+
handler: 'index.handler',
|
16
|
+
code: lambda.Code.fromAsset(path.join(__dirname, '/sqs-dlq-stats-widget-function.lambda.bundle')),
|
17
|
+
});
|
18
|
+
}
|
19
|
+
}
|
20
|
+
exports.SqsDlqStatsWidgetFunction = SqsDlqStatsWidgetFunction;
|
21
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3FzLWRscS1zdGF0cy13aWRnZXQtZnVuY3Rpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb3ZlcnZpZXctZGFzaGJvYXJkL3Nxcy1kbHEtc3RhdHMtd2lkZ2V0LWZ1bmN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDZFQUE2RTtBQUM3RSw2QkFBNkI7QUFDN0IsOENBQThDO0FBTTlDLE1BQWEseUJBQTBCLFNBQVEsTUFBTSxDQUFDLGlCQUFpQjtJQUNyRSxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQXNDO1FBQzlFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFO1lBQ2YsV0FBVyxFQUFFLDREQUE0RDtZQUN6RSxHQUFHLEtBQUs7WUFDUixJQUFJLEVBQUUsc0NBQXNDO1lBQzVDLGFBQWEsRUFBRSwyQkFBMkI7WUFDMUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVztZQUNuQyxPQUFPLEVBQUUsZUFBZTtZQUN4QixJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsOENBQThDLENBQUMsQ0FBQztTQUNsRyxDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUFaRCw4REFZQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIH5+IEdlbmVyYXRlZCBieSBwcm9qZW4uIFRvIG1vZGlmeSwgZWRpdCAucHJvamVucmMuanMgYW5kIHJ1biBcIm5weCBwcm9qZW5cIi5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBsYW1iZGEgZnJvbSAnQGF3cy1jZGsvYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdAYXdzLWNkay9jb3JlJztcblxuZXhwb3J0IGludGVyZmFjZSBTcXNEbHFTdGF0c1dpZGdldEZ1bmN0aW9uUHJvcHMgZXh0ZW5kcyBsYW1iZGEuRnVuY3Rpb25PcHRpb25zIHtcbn1cblxuZXhwb3J0IGNsYXNzIFNxc0RscVN0YXRzV2lkZ2V0RnVuY3Rpb24gZXh0ZW5kcyBsYW1iZGEuU2luZ2xldG9uRnVuY3Rpb24ge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IFNxc0RscVN0YXRzV2lkZ2V0RnVuY3Rpb25Qcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCwge1xuICAgICAgZGVzY3JpcHRpb246ICdvdmVydmlldy1kYXNoYm9hcmQvc3FzLWRscS1zdGF0cy13aWRnZXQtZnVuY3Rpb24ubGFtYmRhLnRzJyxcbiAgICAgIC4uLnByb3BzLFxuICAgICAgdXVpZDogJ2ZjMTc2ODQ2LTA0NGYtNWU1Ni1iYWYyLWM3MTcyMzUwMTg4NScsXG4gICAgICBsYW1iZGFQdXJwb3NlOiAnU1FTRExRU3RhdHNXaWRnZXQtSGFuZGxlcicsXG4gICAgICBydW50aW1lOiBsYW1iZGEuUnVudGltZS5OT0RFSlNfMTRfWCxcbiAgICAgIGhhbmRsZXI6ICdpbmRleC5oYW5kbGVyJyxcbiAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Bc3NldChwYXRoLmpvaW4oX19kaXJuYW1lLCAnL3Nxcy1kbHEtc3RhdHMtd2lkZ2V0LWZ1bmN0aW9uLmxhbWJkYS5idW5kbGUnKSksXG4gICAgfSk7XG4gIH1cbn0iXX0=
|
@@ -0,0 +1,181 @@
|
|
1
|
+
var __defProp = Object.defineProperty;
|
2
|
+
var __defProps = Object.defineProperties;
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
6
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
8
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
9
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
10
|
+
var __spreadValues = (a, b) => {
|
11
|
+
for (var prop in b || (b = {}))
|
12
|
+
if (__hasOwnProp.call(b, prop))
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
14
|
+
if (__getOwnPropSymbols)
|
15
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
16
|
+
if (__propIsEnum.call(b, prop))
|
17
|
+
__defNormalProp(a, prop, b[prop]);
|
18
|
+
}
|
19
|
+
return a;
|
20
|
+
};
|
21
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
22
|
+
var __export = (target, all) => {
|
23
|
+
for (var name in all)
|
24
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
25
|
+
};
|
26
|
+
var __copyProps = (to, from, except, desc) => {
|
27
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
28
|
+
for (let key of __getOwnPropNames(from))
|
29
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
30
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
31
|
+
}
|
32
|
+
return to;
|
33
|
+
};
|
34
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
35
|
+
|
36
|
+
// src/overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts
|
37
|
+
var sqs_dlq_stats_widget_function_lambda_exports = {};
|
38
|
+
__export(sqs_dlq_stats_widget_function_lambda_exports, {
|
39
|
+
handler: () => handler
|
40
|
+
});
|
41
|
+
module.exports = __toCommonJS(sqs_dlq_stats_widget_function_lambda_exports);
|
42
|
+
var import_aws_sdk = require("aws-sdk");
|
43
|
+
var DOCS = `
|
44
|
+
|
45
|
+
### Widget parameters
|
46
|
+
Param | Description
|
47
|
+
---|---
|
48
|
+
**title** | Title of the widget
|
49
|
+
**emptyQueueMessage** | Message to display when there are no visible messages in any of the queues
|
50
|
+
**nonEmptyQueueMessage** | Message to display when any of the queue contains a message
|
51
|
+
**queues** | Object containing information about the queues to display
|
52
|
+
|
53
|
+
### Example parameters
|
54
|
+
\`\`\` yaml
|
55
|
+
{
|
56
|
+
"description": "",
|
57
|
+
"title": "SQS DLQ",
|
58
|
+
"emptyQueueMessage": "There are no messages in the DLQs. This is normal and no action is required.",
|
59
|
+
"nonEmptyQueueMessage": "There are some message in the DLQ. The table below lists the queues with non-empty DLQs. Please check the DLQs and re-drive the messages.",
|
60
|
+
"queues": {
|
61
|
+
"construct-hub-dev-ConstructHubOrchestrationDLQ9C6D9BD4-rlooG5g8o3YA": {
|
62
|
+
"name": "Orchestration DLQ",
|
63
|
+
"queueName": "construct-hub-dev-ConstructHubOrchestrationDLQ9C6D9BD4-rlooG5g8o3YA",
|
64
|
+
"reDriveFunctionArn": "arn:aws:lambda:us-east-2:123457424:function:construct-hub-dev-ConstructHubOrchestrationRedrive-bWYSY5KWfUUU"
|
65
|
+
},
|
66
|
+
"construct-hub-dev-ConstructHubIngestionDLQ3E96A5F2-6niMeQlEH0X0": {
|
67
|
+
"name": "Ingestion DLQ",
|
68
|
+
"queueName": "construct-hub-dev-ConstructHubIngestionDLQ3E96A5F2-6niMeQlEH0X0"
|
69
|
+
},
|
70
|
+
"construct-hub-dev-ConstructHubSourcesStagerDLQ80BD2600-AlCVN3KCXFPo": {
|
71
|
+
"name": "NPM JS Stager DLQ",
|
72
|
+
"queueName": "construct-hub-dev-ConstructHubSourcesStagerDLQ80BD2600-AlCVN3KCXFPo"
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
\`\`\`
|
77
|
+
`;
|
78
|
+
var DURATION = 5;
|
79
|
+
async function handler(event) {
|
80
|
+
var _a, _b;
|
81
|
+
console.log(JSON.stringify(event));
|
82
|
+
try {
|
83
|
+
if (event.describe) {
|
84
|
+
return DOCS;
|
85
|
+
}
|
86
|
+
const queues = (_a = event.widgetContext.params) == null ? void 0 : _a.queues;
|
87
|
+
if (Object.keys(queues).length === 0) {
|
88
|
+
return "No queue names specified";
|
89
|
+
}
|
90
|
+
const data = await getVisibleMessageCount(queues);
|
91
|
+
const heading = `<h4>${event.widgetContext.title}</h4>`;
|
92
|
+
const description = event.widgetContext.params.description ? `<p>${event.widgetContext.params.description}</p>` : "";
|
93
|
+
let rest = "";
|
94
|
+
if (Object.keys(data).length > 0 && Object.values(data).some((d) => d > 0)) {
|
95
|
+
const filteredData = Object.entries(data).filter(([_, count]) => count > 0).reduce((acc, [queueName, count]) => __spreadProps(__spreadValues({}, acc), { [queueName]: count }), {});
|
96
|
+
const table = generateTable(filteredData, queues, event.widgetContext);
|
97
|
+
console.log(table);
|
98
|
+
rest = `<p>${event.widgetContext.params.nonEmptyQueueMessage}</p>${table}`;
|
99
|
+
} else {
|
100
|
+
rest = `<p>${event.widgetContext.params.emptyQueueMessage}</p>`;
|
101
|
+
}
|
102
|
+
return [heading, description, rest].join("\n");
|
103
|
+
} catch (error) {
|
104
|
+
if (error instanceof Error) {
|
105
|
+
return {
|
106
|
+
markdown: [
|
107
|
+
"**\u26A0\uFE0F An error occurred**",
|
108
|
+
`- **name:** \`${error.name}\``,
|
109
|
+
`- **message:** ${error.message}`,
|
110
|
+
"- **stack:**",
|
111
|
+
" ```",
|
112
|
+
(_b = error.stack) == null ? void 0 : _b.replace(/^/g, " "),
|
113
|
+
" ```"
|
114
|
+
].join("\n")
|
115
|
+
};
|
116
|
+
}
|
117
|
+
;
|
118
|
+
throw error;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
var getVisibleMessageCount = async (queueConfigs) => {
|
122
|
+
const queues = Object.values(queueConfigs);
|
123
|
+
const params = {
|
124
|
+
StartTime: new Date(new Date().getTime() - DURATION * 60 * 1e3),
|
125
|
+
EndTime: new Date(),
|
126
|
+
ScanBy: "TimestampDescending",
|
127
|
+
MetricDataQueries: queues.map((queue, index) => ({
|
128
|
+
Id: `m${index}`,
|
129
|
+
MetricStat: {
|
130
|
+
Period: 60,
|
131
|
+
Stat: "Maximum",
|
132
|
+
Metric: {
|
133
|
+
Namespace: "AWS/SQS",
|
134
|
+
MetricName: "ApproximateNumberOfMessagesVisible",
|
135
|
+
Dimensions: [
|
136
|
+
{
|
137
|
+
Name: "QueueName",
|
138
|
+
Value: queue.queueName
|
139
|
+
}
|
140
|
+
]
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}))
|
144
|
+
};
|
145
|
+
const cloudwatch = new import_aws_sdk.CloudWatch();
|
146
|
+
const response = await cloudwatch.getMetricData(params).promise();
|
147
|
+
const data = (response.MetricDataResults ?? []).reduce((acc, result) => {
|
148
|
+
var _a;
|
149
|
+
if (result.Id) {
|
150
|
+
const id = Number.parseInt(result.Id.replace("m", ""), 10);
|
151
|
+
return __spreadProps(__spreadValues({}, acc), { [queues[id].queueName]: ((_a = result.Values) == null ? void 0 : _a[0]) ?? 0 });
|
152
|
+
}
|
153
|
+
return acc;
|
154
|
+
}, {});
|
155
|
+
return data;
|
156
|
+
};
|
157
|
+
function generateTable(data, queueConfigs, widgetContext) {
|
158
|
+
const rows = Object.entries(data).map(([queueName, count]) => {
|
159
|
+
const queueConfig = queueConfigs[queueName];
|
160
|
+
if (queueConfig) {
|
161
|
+
const url = sqsQueueUrl(queueName, widgetContext);
|
162
|
+
const reDriveButton = (queueConfig == null ? void 0 : queueConfig.reDriveFunctionArn) ? `<a class="btn btn-primary">ReDrive</a><cwdb-action action="call" display="widget" endpoint="${queueConfig.reDriveFunctionArn}" event="click"></<cwdb-action>` : "";
|
163
|
+
const queueLink = `<a class="btn ${!reDriveButton ? "btn-primary" : ""}" href="${url}">Goto Queue</a>`;
|
164
|
+
const row = [queueConfig.name, count, `${queueLink} ${reDriveButton}`].map((d) => `<td>${d}</td>`).join("");
|
165
|
+
return `<tr>${row}</tr>`;
|
166
|
+
}
|
167
|
+
return false;
|
168
|
+
}).filter(Boolean);
|
169
|
+
const tableHeader = ["Queue Name", "Visible Messages", "Action"].map((h) => `<th>${h}</th>`);
|
170
|
+
const tableHeaderRow = `<tr>${tableHeader.join("")}</tr>`;
|
171
|
+
const table = ["<table>", tableHeaderRow, ...rows, "</table>"].join("\n");
|
172
|
+
return table;
|
173
|
+
}
|
174
|
+
function sqsQueueUrl(queueName, widgetContext) {
|
175
|
+
return `/sqs/v2/home#/queues/https%3A%2F%2Fsqs.${process.env.AWS_REGION}.amazonaws.com%2F${widgetContext.accountId}%2F${queueName}`;
|
176
|
+
}
|
177
|
+
// Annotate the CommonJS export names for ESM import in node:
|
178
|
+
0 && (module.exports = {
|
179
|
+
handler
|
180
|
+
});
|
181
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +1,7 @@
|
|
1
|
+
{
|
2
|
+
"version": 3,
|
3
|
+
"sources": ["../../../src/overview-dashboard/sqs-dlq-stats-widget-function.lambda.ts"],
|
4
|
+
"sourcesContent": ["/// @singleton SQSDLQStatsWidget-Handler\nconst DOCS = `\n\n### Widget parameters\nParam | Description\n---|---\n**title** | Title of the widget\n**emptyQueueMessage** | Message to display when there are no visible messages in any of the queues\n**nonEmptyQueueMessage** | Message to display when any of the queue contains a message\n**queues** | Object containing information about the queues to display\n\n### Example parameters\n\\`\\`\\` yaml\n{\n \"description\": \"\",\n \"title\": \"SQS DLQ\",\n \"emptyQueueMessage\": \"There are no messages in the DLQs. This is normal and no action is required.\",\n \"nonEmptyQueueMessage\": \"There are some message in the DLQ. The table below lists the queues with non-empty DLQs. Please check the DLQs and re-drive the messages.\",\n \"queues\": {\n \"construct-hub-dev-ConstructHubOrchestrationDLQ9C6D9BD4-rlooG5g8o3YA\": {\n \"name\": \"Orchestration DLQ\",\n \"queueName\": \"construct-hub-dev-ConstructHubOrchestrationDLQ9C6D9BD4-rlooG5g8o3YA\",\n \"reDriveFunctionArn\": \"arn:aws:lambda:us-east-2:123457424:function:construct-hub-dev-ConstructHubOrchestrationRedrive-bWYSY5KWfUUU\"\n },\n \"construct-hub-dev-ConstructHubIngestionDLQ3E96A5F2-6niMeQlEH0X0\": {\n \"name\": \"Ingestion DLQ\",\n \"queueName\": \"construct-hub-dev-ConstructHubIngestionDLQ3E96A5F2-6niMeQlEH0X0\"\n },\n \"construct-hub-dev-ConstructHubSourcesStagerDLQ80BD2600-AlCVN3KCXFPo\": {\n \"name\": \"NPM JS Stager DLQ\",\n \"queueName\": \"construct-hub-dev-ConstructHubSourcesStagerDLQ80BD2600-AlCVN3KCXFPo\"\n }\n }\n}\n\\`\\`\\`\n`;\n\nimport { CloudWatch } from 'aws-sdk';\nconst DURATION = 5; // minutes\n\ninterface Event {\n readonly key: string;\n readonly description: string;\n readonly widgetContext: WidgetContext;\n describe?: unknown;\n}\n\ntype QueueConfig = {\n queueName: string;\n name: string;\n reDriveFunctionArn?: string;\n}\n\nexport async function handler(event: Event): Promise<string | { markdown: string }> {\n console.log(JSON.stringify(event));\n try {\n if (event.describe) {\n return DOCS;\n }\n\n const queues = event.widgetContext.params?.queues;\n if (Object.keys(queues).length === 0) {\n return 'No queue names specified';\n }\n\n const data = await getVisibleMessageCount(queues);\n\n const heading = `<h4>${event.widgetContext.title}</h4>`;\n const description = event.widgetContext.params.description ? `<p>${event.widgetContext.params.description}</p>` : '';\n let rest = '';\n if (Object.keys(data).length > 0 && Object.values(data).some(d => d > 0)) {\n const filteredData = Object.entries(data)\n .filter(([_, count]) => count > 0)\n .reduce((acc, [queueName, count]) => ({ ...acc, [queueName]: count }), {} as Record<string, number>);\n const table = generateTable(filteredData, queues, event.widgetContext);\n console.log(table);\n rest = `<p>${event.widgetContext.params.nonEmptyQueueMessage}</p>${table}`;\n } else {\n rest = `<p>${event.widgetContext.params.emptyQueueMessage}</p>`;\n }\n return [heading, description, rest].join('\\n');\n } catch (error) {\n if (error instanceof Error) {\n return {\n markdown: [\n '**\u26A0\uFE0F An error occurred**',\n `- **name:** \\`${error.name}\\``,\n `- **message:** ${error.message}`,\n '- **stack:**',\n ' ```',\n error.stack?.replace(/^/g, ' '),\n ' ```',\n ].join('\\n'),\n };\n };\n throw error;\n }\n}\n\nexport interface WidgetContext {\n readonly dashboardName: string;\n readonly widgetId: string;\n readonly domain: string;\n readonly accountId: string;\n readonly locale: string;\n readonly timezone: {\n readonly label: string;\n readonly offsetISO: string;\n readonly offsetInMinutes: number;\n };\n readonly period: number;\n readonly isAutoPeriod: true;\n readonly timeRange: {\n readonly mode: 'relative' | 'absolute';\n readonly start: number;\n readonly end: number;\n readonly relativeStart: number;\n readonly zoom: {\n readonly start: number;\n readonly end: number;\n };\n };\n readonly theme: 'light' | 'dark';\n readonly linkCharts: boolean;\n readonly title: string;\n readonly forms: {\n readonly all: { readonly [key: string]: string };\n };\n readonly params: {\n queues: Record<string, QueueConfig>;\n title?: string;\n description?: string;\n emptyQueueMessage?: string;\n nonEmptyQueueMessage?: string;\n };\n readonly width: number;\n readonly height: number;\n}\n\nconst getVisibleMessageCount = async (queueConfigs: Record<string, QueueConfig>): Promise<Record<string, number>> => {\n // Keeping the time period to 5 minutes to show current state of the queue when re-drive happens\n const queues = Object.values(queueConfigs);\n const params: CloudWatch.GetMetricDataInput = {\n StartTime: new Date(new Date().getTime() - (DURATION * 60 * 1000)), // 5 minutes ago\n EndTime: new Date(), // now\n ScanBy: 'TimestampDescending',\n MetricDataQueries: queues.map((queue, index) => ({\n Id: `m${index}`,\n MetricStat: {\n Period: 60,\n Stat: 'Maximum',\n Metric: {\n Namespace: 'AWS/SQS',\n MetricName: 'ApproximateNumberOfMessagesVisible',\n Dimensions: [\n {\n Name: 'QueueName',\n Value: queue.queueName,\n },\n ],\n },\n },\n })),\n };\n const cloudwatch = new CloudWatch();\n const response = await cloudwatch.getMetricData(params).promise();\n const data = (response.MetricDataResults ?? []).reduce((acc, result) => {\n if (result.Id) {\n const id = Number.parseInt(result.Id.replace('m', ''), 10);\n return { ...acc, [queues[id].queueName]: result.Values?.[0] ?? 0 };\n }\n return acc;\n }, {} as Record<string, number>);\n return data;\n\n};\n\nfunction generateTable(data: Record<string, number>, queueConfigs: Record<string, QueueConfig>, widgetContext: WidgetContext): string {\n\n const rows = Object.entries(data).map(([queueName, count]) => {\n const queueConfig = queueConfigs[queueName];\n if (queueConfig) {\n const url = sqsQueueUrl(queueName, widgetContext);\n const reDriveButton = queueConfig?.reDriveFunctionArn ? `<a class=\"btn btn-primary\">ReDrive</a><cwdb-action action=\"call\" display=\"widget\" endpoint=\"${queueConfig.reDriveFunctionArn}\" event=\"click\"></<cwdb-action>` : '';\n const queueLink = `<a class=\"btn ${!reDriveButton ? 'btn-primary' : ''}\" href=\"${url}\">Goto Queue</a>`;\n const row = [queueConfig.name, count, `${queueLink} ${reDriveButton}`].map(d => `<td>${d}</td>`).join('');\n return `<tr>${row}</tr>`;\n }\n return false;\n }).filter(Boolean);\n\n const tableHeader = ['Queue Name', 'Visible Messages', 'Action'].map(h => `<th>${h}</th>`);\n const tableHeaderRow = `<tr>${tableHeader.join('')}</tr>`;\n const table = ['<table>', tableHeaderRow, ...rows, '</table>'].join('\\n');\n return table;\n}\n\nfunction sqsQueueUrl(queueName: string, widgetContext: WidgetContext): string {\n return `/sqs/v2/home#/queues/https%3A%2F%2Fsqs.${process.env.AWS_REGION}.amazonaws.com%2F${widgetContext.accountId}%2F${queueName}`;\n}\n"],
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCA,qBAA2B;AApC3B,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqCb,IAAM,WAAW;AAejB,uBAA8B,OAAsD;AArDpF;AAsDE,UAAQ,IAAI,KAAK,UAAU,KAAK,CAAC;AACjC,MAAI;AACF,QAAI,MAAM,UAAU;AAClB,aAAO;AAAA,IACT;AAEA,UAAM,SAAS,YAAM,cAAc,WAApB,mBAA4B;AAC3C,QAAI,OAAO,KAAK,MAAM,EAAE,WAAW,GAAG;AACpC,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,MAAM,uBAAuB,MAAM;AAEhD,UAAM,UAAU,OAAO,MAAM,cAAc;AAC3C,UAAM,cAAc,MAAM,cAAc,OAAO,cAAc,MAAM,MAAM,cAAc,OAAO,oBAAoB;AAClH,QAAI,OAAO;AACX,QAAI,OAAO,KAAK,IAAI,EAAE,SAAS,KAAK,OAAO,OAAO,IAAI,EAAE,KAAK,OAAK,IAAI,CAAC,GAAG;AACxE,YAAM,eAAe,OAAO,QAAQ,IAAI,EACrC,OAAO,CAAC,CAAC,GAAG,WAAW,QAAQ,CAAC,EAChC,OAAO,CAAC,KAAK,CAAC,WAAW,WAAY,iCAAK,MAAL,GAAW,YAAY,MAAM,IAAI,CAAC,CAA2B;AACrG,YAAM,QAAQ,cAAc,cAAc,QAAQ,MAAM,aAAa;AACrE,cAAQ,IAAI,KAAK;AACjB,aAAO,MAAM,MAAM,cAAc,OAAO,2BAA2B;AAAA,IACrE,OAAO;AACL,aAAO,MAAM,MAAM,cAAc,OAAO;AAAA,IAC1C;AACA,WAAO,CAAC,SAAS,aAAa,IAAI,EAAE,KAAK,IAAI;AAAA,EAC/C,SAAS,OAAP;AACA,QAAI,iBAAiB,OAAO;AAC1B,aAAO;AAAA,QACL,UAAU;AAAA,UACR;AAAA,UACA,iBAAiB,MAAM;AAAA,UACvB,kBAAkB,MAAM;AAAA,UACxB;AAAA,UACA;AAAA,UACA,YAAM,UAAN,mBAAa,QAAQ,MAAM;AAAA,UAC3B;AAAA,QACF,EAAE,KAAK,IAAI;AAAA,MACb;AAAA,IACF;AAAC;AACD,UAAM;AAAA,EACR;AACF;AA0CA,IAAM,yBAAyB,OAAO,iBAA+E;AAEnH,QAAM,SAAS,OAAO,OAAO,YAAY;AACzC,QAAM,SAAwC;AAAA,IAC5C,WAAW,IAAI,KAAK,IAAI,KAAK,EAAE,QAAQ,IAAK,WAAW,KAAK,GAAK;AAAA,IACjE,SAAS,IAAI,KAAK;AAAA,IAClB,QAAQ;AAAA,IACR,mBAAmB,OAAO,IAAI,CAAC,OAAO,UAAW;AAAA,MAC/C,IAAI,IAAI;AAAA,MACR,YAAY;AAAA,QACV,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,WAAW;AAAA,UACX,YAAY;AAAA,UACZ,YAAY;AAAA,YACV;AAAA,cACE,MAAM;AAAA,cACN,OAAO,MAAM;AAAA,YACf;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,EAAE;AAAA,EACJ;AACA,QAAM,aAAa,IAAI,0BAAW;AAClC,QAAM,WAAW,MAAM,WAAW,cAAc,MAAM,EAAE,QAAQ;AAChE,QAAM,OAAQ,UAAS,qBAAqB,CAAC,GAAG,OAAO,CAAC,KAAK,WAAW;AAtK1E;AAuKI,QAAI,OAAO,IAAI;AACb,YAAM,KAAK,OAAO,SAAS,OAAO,GAAG,QAAQ,KAAK,EAAE,GAAG,EAAE;AACzD,aAAO,iCAAK,MAAL,GAAW,OAAO,IAAI,YAAY,cAAO,WAAP,mBAAgB,OAAM,EAAE;AAAA,IACnE;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAA2B;AAC/B,SAAO;AAET;AAEA,uBAAuB,MAA8B,cAA2C,eAAsC;AAEpI,QAAM,OAAO,OAAO,QAAQ,IAAI,EAAE,IAAI,CAAC,CAAC,WAAW,WAAW;AAC5D,UAAM,cAAc,aAAa;AACjC,QAAI,aAAa;AACf,YAAM,MAAM,YAAY,WAAW,aAAa;AAChD,YAAM,gBAAgB,4CAAa,sBAAqB,+FAA+F,YAAY,sDAAsD;AACzN,YAAM,YAAY,iBAAiB,CAAC,gBAAgB,gBAAgB,aAAa;AACjF,YAAM,MAAM,CAAC,YAAY,MAAM,OAAO,GAAG,aAAa,eAAe,EAAE,IAAI,OAAK,OAAO,QAAQ,EAAE,KAAK,EAAE;AACxG,aAAO,OAAO;AAAA,IAChB;AACA,WAAO;AAAA,EACT,CAAC,EAAE,OAAO,OAAO;AAEjB,QAAM,cAAc,CAAC,cAAc,oBAAoB,QAAQ,EAAE,IAAI,OAAK,OAAO,QAAQ;AACzF,QAAM,iBAAiB,OAAO,YAAY,KAAK,EAAE;AACjD,QAAM,QAAQ,CAAC,WAAW,gBAAgB,GAAG,MAAM,UAAU,EAAE,KAAK,IAAI;AACxE,SAAO;AACT;AAEA,qBAAqB,WAAmB,eAAsC;AAC5E,SAAO,0CAA0C,QAAQ,IAAI,8BAA8B,cAAc,eAAe;AAC1H;",
|
6
|
+
"names": []
|
7
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
interface Event {
|
2
|
+
readonly key: string;
|
3
|
+
readonly description: string;
|
4
|
+
readonly widgetContext: WidgetContext;
|
5
|
+
describe?: unknown;
|
6
|
+
}
|
7
|
+
declare type QueueConfig = {
|
8
|
+
queueName: string;
|
9
|
+
name: string;
|
10
|
+
reDriveFunctionArn?: string;
|
11
|
+
};
|
12
|
+
export declare function handler(event: Event): Promise<string | {
|
13
|
+
markdown: string;
|
14
|
+
}>;
|
15
|
+
export interface WidgetContext {
|
16
|
+
readonly dashboardName: string;
|
17
|
+
readonly widgetId: string;
|
18
|
+
readonly domain: string;
|
19
|
+
readonly accountId: string;
|
20
|
+
readonly locale: string;
|
21
|
+
readonly timezone: {
|
22
|
+
readonly label: string;
|
23
|
+
readonly offsetISO: string;
|
24
|
+
readonly offsetInMinutes: number;
|
25
|
+
};
|
26
|
+
readonly period: number;
|
27
|
+
readonly isAutoPeriod: true;
|
28
|
+
readonly timeRange: {
|
29
|
+
readonly mode: 'relative' | 'absolute';
|
30
|
+
readonly start: number;
|
31
|
+
readonly end: number;
|
32
|
+
readonly relativeStart: number;
|
33
|
+
readonly zoom: {
|
34
|
+
readonly start: number;
|
35
|
+
readonly end: number;
|
36
|
+
};
|
37
|
+
};
|
38
|
+
readonly theme: 'light' | 'dark';
|
39
|
+
readonly linkCharts: boolean;
|
40
|
+
readonly title: string;
|
41
|
+
readonly forms: {
|
42
|
+
readonly all: {
|
43
|
+
readonly [key: string]: string;
|
44
|
+
};
|
45
|
+
};
|
46
|
+
readonly params: {
|
47
|
+
queues: Record<string, QueueConfig>;
|
48
|
+
title?: string;
|
49
|
+
description?: string;
|
50
|
+
emptyQueueMessage?: string;
|
51
|
+
nonEmptyQueueMessage?: string;
|
52
|
+
};
|
53
|
+
readonly width: number;
|
54
|
+
readonly height: number;
|
55
|
+
}
|
56
|
+
export {};
|