construct-hub 0.3.313 → 0.3.316
Sign up to get free protection for your applications and to get access to all the features.
- 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": "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",
|
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 {};
|