@oneuptime/common 14.0.2 → 14.0.3
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/Models/DatabaseModels/AlertFeed.ts +2 -0
- package/Models/DatabaseModels/IncidentAlert.ts +540 -0
- package/Models/DatabaseModels/IncidentFeed.ts +2 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/Project.ts +60 -0
- package/Models/DatabaseModels/RumApplication.ts +52 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/IncidentAlertService.ts +1811 -0
- package/Server/Services/IncidentService.ts +210 -10
- package/Server/Services/IncidentStateTimelineService.ts +24 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
- package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
- package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
- package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
- package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
- package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
- package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
- package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
- package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
- package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
- package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +38 -18
- package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
- package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
- package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
- package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
- package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
- package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
- package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
- package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
- package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
- package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
- package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
- package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
- package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
- package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
- package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
- package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
- package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
- package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
- package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
- package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
- package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
- package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
- package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
- package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
- package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
- package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
- package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
- package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
- package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
- package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
- package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
- package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
- package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
- package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
- package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
- package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
- package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
- package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
- package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
- package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
- package/Tests/Types/Rum/WebVitals.test.ts +173 -0
- package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
- package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
- package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
- package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
- package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
- package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
- package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
- package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
- package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
- package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
- package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/UI/Utils/PermissionGate.test.ts +76 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
- package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
- package/Types/Incident/IncidentAlertLink.ts +38 -0
- package/Types/Permission.ts +48 -0
- package/Types/Rum/SessionReplay.ts +25 -6
- package/Types/Rum/WebVitals.ts +52 -0
- package/UI/Components/Detail/FieldLabel.tsx +8 -0
- package/UI/Components/InfoCard/InfoCard.tsx +47 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +95 -17
- package/UI/Components/ModelTable/Column.ts +5 -0
- package/UI/Components/Table/TableHeader.tsx +47 -2
- package/UI/Components/Table/Types/Column.ts +7 -0
- package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
- package/UI/Utils/PermissionGate.ts +11 -2
- package/Utils/Incident/IncidentFromAlerts.ts +516 -0
- package/Utils/Rum/SessionTraceState.ts +212 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
- package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +62 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/IncidentAlertService.js +1370 -0
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +164 -12
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +21 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +25 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
- package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +66 -14
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +18 -2
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
- package/build/dist/UI/Utils/PermissionGate.js +4 -2
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
- package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
- package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
- package/package.json +1 -1
|
@@ -111,6 +111,10 @@ import IncidentAIContextBuilder, {
|
|
|
111
111
|
AIGenerationContext,
|
|
112
112
|
IncidentContextData,
|
|
113
113
|
} from "../Utils/AI/IncidentAIContextBuilder";
|
|
114
|
+
import IncidentAlertService, {
|
|
115
|
+
LinkAlertsToIncidentResult,
|
|
116
|
+
} from "./IncidentAlertService";
|
|
117
|
+
import { INCIDENT_ALERT_IDS_TO_LINK_KEY } from "../../Types/Incident/IncidentAlertLink";
|
|
114
118
|
|
|
115
119
|
// key is incidentId for this dictionary.
|
|
116
120
|
type UpdateCarryForward = Dictionary<{
|
|
@@ -120,6 +124,15 @@ type UpdateCarryForward = Dictionary<{
|
|
|
120
124
|
newMonitorChangeStatusIdTo: ObjectID | undefined;
|
|
121
125
|
}>;
|
|
122
126
|
|
|
127
|
+
/*
|
|
128
|
+
* What onBeforeCreate hands to onCreateSuccess. Null unless the incident is
|
|
129
|
+
* being declared from alerts.
|
|
130
|
+
*/
|
|
131
|
+
type IncidentCreateCarryForward = {
|
|
132
|
+
// Validated, deduplicated alert ids to link once the incident exists.
|
|
133
|
+
alertIdsToLink: Array<ObjectID>;
|
|
134
|
+
} | null;
|
|
135
|
+
|
|
123
136
|
type IncidentUpdatePayload = {
|
|
124
137
|
postmortemNote?: string | null;
|
|
125
138
|
title?: string | null;
|
|
@@ -783,6 +796,30 @@ export class Service extends DatabaseService<Model> {
|
|
|
783
796
|
const projectId: ObjectID =
|
|
784
797
|
createBy.props.tenantId || createBy.data.projectId!;
|
|
785
798
|
|
|
799
|
+
/*
|
|
800
|
+
* Declaring the incident from alerts. The alert ids are checked here,
|
|
801
|
+
* before the incident number is taken and before anything is written, so
|
|
802
|
+
* a bad id (a typo, another project's alert, an alert the caller cannot
|
|
803
|
+
* see, too many alerts) rejects the whole request instead of leaving an
|
|
804
|
+
* incident behind that is missing some of its alerts. The alerts are
|
|
805
|
+
* linked in onCreateSuccess, once the incident exists.
|
|
806
|
+
*/
|
|
807
|
+
let carryForward: IncidentCreateCarryForward = null;
|
|
808
|
+
|
|
809
|
+
const alertIdsToLink: unknown =
|
|
810
|
+
createBy.miscDataProps?.[INCIDENT_ALERT_IDS_TO_LINK_KEY];
|
|
811
|
+
|
|
812
|
+
if (alertIdsToLink !== undefined && alertIdsToLink !== null) {
|
|
813
|
+
carryForward = {
|
|
814
|
+
alertIdsToLink:
|
|
815
|
+
await IncidentAlertService.validateAlertIdsForNewIncident({
|
|
816
|
+
projectId: projectId,
|
|
817
|
+
alertIds: alertIdsToLink,
|
|
818
|
+
props: createBy.props,
|
|
819
|
+
}),
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
786
823
|
if (!createBy.data.declaredAt) {
|
|
787
824
|
createBy.data.declaredAt = OneUptimeDate.getCurrentDate();
|
|
788
825
|
} else {
|
|
@@ -1179,7 +1216,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
1179
1216
|
|
|
1180
1217
|
return {
|
|
1181
1218
|
createBy,
|
|
1182
|
-
carryForward:
|
|
1219
|
+
carryForward: carryForward,
|
|
1183
1220
|
};
|
|
1184
1221
|
}
|
|
1185
1222
|
|
|
@@ -1267,14 +1304,16 @@ export class Service extends DatabaseService<Model> {
|
|
|
1267
1304
|
*/
|
|
1268
1305
|
let aiInvestigationEnqueued: boolean = false;
|
|
1269
1306
|
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1307
|
+
/*
|
|
1308
|
+
* Apply privacy rules BEFORE workspace operations so the workspace
|
|
1309
|
+
* channel is created with the correct privacy setting. This may set
|
|
1310
|
+
* createdItem.isPrivate=true in memory. Kept as its own promise (it never
|
|
1311
|
+
* rejects) so declaring from alerts can wait for it before linking: the
|
|
1312
|
+
* links' feed entries leave out a private incident's title, so they must
|
|
1313
|
+
* see the privacy the rules settle on.
|
|
1314
|
+
*/
|
|
1315
|
+
const privacyRulesApplied: Promise<void> = Promise.resolve().then(
|
|
1316
|
+
async () => {
|
|
1278
1317
|
try {
|
|
1279
1318
|
await IncidentPrivacyRuleEngineService.applyRulesToIncident(
|
|
1280
1319
|
createdItem,
|
|
@@ -1288,7 +1327,11 @@ export class Service extends DatabaseService<Model> {
|
|
|
1288
1327
|
} as LogAttributes,
|
|
1289
1328
|
);
|
|
1290
1329
|
}
|
|
1291
|
-
}
|
|
1330
|
+
},
|
|
1331
|
+
);
|
|
1332
|
+
|
|
1333
|
+
// Execute operations sequentially with error handling
|
|
1334
|
+
privacyRulesApplied
|
|
1292
1335
|
.then(async () => {
|
|
1293
1336
|
try {
|
|
1294
1337
|
if (createdItem.projectId && createdItem.id) {
|
|
@@ -1324,6 +1367,67 @@ export class Service extends DatabaseService<Model> {
|
|
|
1324
1367
|
return Promise.resolve();
|
|
1325
1368
|
}
|
|
1326
1369
|
})
|
|
1370
|
+
.then(async () => {
|
|
1371
|
+
/*
|
|
1372
|
+
* Declared from alerts: announce the alerts once, here - after
|
|
1373
|
+
* "Incident Created" and after the incident's Slack / Microsoft
|
|
1374
|
+
* Teams channels exist - rather than once per alert as each link is
|
|
1375
|
+
* written (see linkAlertsDeclaredWithIncident). Uses the validated
|
|
1376
|
+
* ids, so it does not wait for the links.
|
|
1377
|
+
*/
|
|
1378
|
+
try {
|
|
1379
|
+
const alertIds: Array<ObjectID> =
|
|
1380
|
+
this.getAlertIdsDeclaredWith(onCreate);
|
|
1381
|
+
|
|
1382
|
+
if (alertIds.length > 0) {
|
|
1383
|
+
await IncidentAlertService.createDeclaredFromAlertsFeedItem({
|
|
1384
|
+
projectId: createdItem.projectId!,
|
|
1385
|
+
incidentId: createdItem.id!,
|
|
1386
|
+
alertIds: alertIds,
|
|
1387
|
+
actorUserId: this.getDeclaringUserId(onCreate, createdItem),
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
} catch (error) {
|
|
1391
|
+
logger.error(
|
|
1392
|
+
`Announcing the alerts an incident was declared from failed in IncidentService.onCreateSuccess: ${error}`,
|
|
1393
|
+
{
|
|
1394
|
+
projectId: createdItem.projectId?.toString(),
|
|
1395
|
+
incidentId: createdItem.id?.toString(),
|
|
1396
|
+
} as LogAttributes,
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
})
|
|
1400
|
+
.then(async () => {
|
|
1401
|
+
/*
|
|
1402
|
+
* A private incident declared from alerts - private from the form or
|
|
1403
|
+
* made private by a privacy rule above - gets the alerts' owners as
|
|
1404
|
+
* its owners, so the people who could see the alerts can see the
|
|
1405
|
+
* incident. Here rather than before the request returns: the owners'
|
|
1406
|
+
* own hooks invite them to the incident's Slack / Microsoft Teams
|
|
1407
|
+
* channels, which exist only from the workspace step above, and their
|
|
1408
|
+
* "owner added" entries belong after "Incident Created".
|
|
1409
|
+
*/
|
|
1410
|
+
try {
|
|
1411
|
+
const alertIds: Array<ObjectID> =
|
|
1412
|
+
this.getAlertIdsDeclaredWith(onCreate);
|
|
1413
|
+
|
|
1414
|
+
if (alertIds.length > 0 && createdItem.isPrivate === true) {
|
|
1415
|
+
await IncidentAlertService.copyAlertOwnersToIncident({
|
|
1416
|
+
projectId: createdItem.projectId!,
|
|
1417
|
+
incidentId: createdItem.id!,
|
|
1418
|
+
alertIds: alertIds,
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
logger.error(
|
|
1423
|
+
`Adding the owners of the alerts a private incident was declared from failed in IncidentService.onCreateSuccess: ${error}`,
|
|
1424
|
+
{
|
|
1425
|
+
projectId: createdItem.projectId?.toString(),
|
|
1426
|
+
incidentId: createdItem.id?.toString(),
|
|
1427
|
+
} as LogAttributes,
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
})
|
|
1327
1431
|
.then(async () => {
|
|
1328
1432
|
try {
|
|
1329
1433
|
return await this.handleIncidentStateChangeAsync(createdItem);
|
|
@@ -1623,9 +1727,105 @@ export class Service extends DatabaseService<Model> {
|
|
|
1623
1727
|
);
|
|
1624
1728
|
});
|
|
1625
1729
|
|
|
1730
|
+
await this.linkAlertsDeclaredWithIncident(
|
|
1731
|
+
onCreate,
|
|
1732
|
+
createdItem,
|
|
1733
|
+
privacyRulesApplied,
|
|
1734
|
+
);
|
|
1735
|
+
|
|
1626
1736
|
return createdItem;
|
|
1627
1737
|
}
|
|
1628
1738
|
|
|
1739
|
+
// The validated alert ids an incident is being declared from, if any.
|
|
1740
|
+
private getAlertIdsDeclaredWith(onCreate: OnCreate<Model>): Array<ObjectID> {
|
|
1741
|
+
const carryForward: IncidentCreateCarryForward =
|
|
1742
|
+
(onCreate.carryForward as IncidentCreateCarryForward) || null;
|
|
1743
|
+
|
|
1744
|
+
return carryForward?.alertIdsToLink || [];
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
/*
|
|
1748
|
+
* Who declared the incident, as "Linked by" on the links and the actor of
|
|
1749
|
+
* their feed entries. A user is recorded as themselves and an API key as
|
|
1750
|
+
* nobody: Incident.createdByUserId is writable by the create payload, and
|
|
1751
|
+
* an API key must not be able to name somebody else as the one who linked
|
|
1752
|
+
* the alerts (IncidentAlertService.onBeforeCreate applies the same rule to
|
|
1753
|
+
* a link created directly). Only an internal root caller is trusted to
|
|
1754
|
+
* name the incident's creator.
|
|
1755
|
+
*/
|
|
1756
|
+
private getDeclaringUserId(
|
|
1757
|
+
onCreate: OnCreate<Model>,
|
|
1758
|
+
createdItem: Model,
|
|
1759
|
+
): ObjectID | undefined {
|
|
1760
|
+
if (onCreate.createBy.props.isRoot) {
|
|
1761
|
+
return createdItem.createdByUserId || undefined;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
return onCreate.createBy.props.userId || undefined;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
/*
|
|
1768
|
+
* The incident was declared from alerts (see onBeforeCreate): link them now
|
|
1769
|
+
* that it exists. Awaited, unlike the chain above, so the incident page the
|
|
1770
|
+
* user lands on already lists its alerts. Written as root because the
|
|
1771
|
+
* caller's right to link them was checked before the incident was created;
|
|
1772
|
+
* the user who declared the incident is recorded as the one who linked
|
|
1773
|
+
* them. The links write no incident-side feed entries: the chain above
|
|
1774
|
+
* posts one "Declared from N alerts" entry instead. A link that fails is
|
|
1775
|
+
* logged and never fails the incident.
|
|
1776
|
+
*
|
|
1777
|
+
* The privacy rules run first: each link's entry on its alert leaves out
|
|
1778
|
+
* the incident's title when the incident is private, and a rule can make
|
|
1779
|
+
* it private after it was saved. (The alerts' owners are added to a private
|
|
1780
|
+
* incident later in the chain, once its workspace channels exist.)
|
|
1781
|
+
*/
|
|
1782
|
+
@CaptureSpan()
|
|
1783
|
+
private async linkAlertsDeclaredWithIncident(
|
|
1784
|
+
onCreate: OnCreate<Model>,
|
|
1785
|
+
createdItem: Model,
|
|
1786
|
+
privacyRulesApplied: Promise<void>,
|
|
1787
|
+
): Promise<void> {
|
|
1788
|
+
const alertIds: Array<ObjectID> = this.getAlertIdsDeclaredWith(onCreate);
|
|
1789
|
+
|
|
1790
|
+
if (alertIds.length === 0 || !createdItem.projectId || !createdItem.id) {
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
await privacyRulesApplied;
|
|
1795
|
+
|
|
1796
|
+
try {
|
|
1797
|
+
const result: LinkAlertsToIncidentResult =
|
|
1798
|
+
await IncidentAlertService.linkAlertsToIncident({
|
|
1799
|
+
projectId: createdItem.projectId,
|
|
1800
|
+
incidentId: createdItem.id,
|
|
1801
|
+
alertIds: alertIds,
|
|
1802
|
+
createdByUserId: this.getDeclaringUserId(onCreate, createdItem),
|
|
1803
|
+
declaredWithIncident: true,
|
|
1804
|
+
props: {
|
|
1805
|
+
isRoot: true,
|
|
1806
|
+
},
|
|
1807
|
+
});
|
|
1808
|
+
|
|
1809
|
+
if (result.failed.length > 0) {
|
|
1810
|
+
logger.error(
|
|
1811
|
+
`${result.failed.length} of ${alertIds.length} alerts could not be linked to the incident they were declared with.`,
|
|
1812
|
+
{
|
|
1813
|
+
projectId: createdItem.projectId.toString(),
|
|
1814
|
+
incidentId: createdItem.id.toString(),
|
|
1815
|
+
} as LogAttributes,
|
|
1816
|
+
);
|
|
1817
|
+
}
|
|
1818
|
+
} catch (error) {
|
|
1819
|
+
logger.error(
|
|
1820
|
+
`Linking the alerts an incident was declared from failed in IncidentService.onCreateSuccess: ${error}`,
|
|
1821
|
+
{
|
|
1822
|
+
projectId: createdItem.projectId.toString(),
|
|
1823
|
+
incidentId: createdItem.id.toString(),
|
|
1824
|
+
} as LogAttributes,
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1629
1829
|
@CaptureSpan()
|
|
1630
1830
|
private async handleIncidentWorkspaceOperationsAsync(
|
|
1631
1831
|
createdItem: Model,
|
|
@@ -37,6 +37,7 @@ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
|
37
37
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
38
38
|
import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
|
|
39
39
|
import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
|
|
40
|
+
import IncidentAlertService from "./IncidentAlertService";
|
|
40
41
|
|
|
41
42
|
export class Service extends DatabaseService<IncidentStateTimeline> {
|
|
42
43
|
public constructor() {
|
|
@@ -485,6 +486,29 @@ export class Service extends DatabaseService<IncidentStateTimeline> {
|
|
|
485
486
|
}
|
|
486
487
|
}
|
|
487
488
|
|
|
489
|
+
/*
|
|
490
|
+
* Carry the incident's new state over to its linked alerts, when the
|
|
491
|
+
* project has opted in (acknowledge and/or resolve them with the
|
|
492
|
+
* incident). Only for the incident's current state - a back-dated row has
|
|
493
|
+
* an endsAt - and after the mutex is released, since it writes alert
|
|
494
|
+
* timelines. Fire-and-forget: it never fails or delays the state change.
|
|
495
|
+
*/
|
|
496
|
+
if (!createdItem.endsAt && createdItem.projectId) {
|
|
497
|
+
IncidentAlertService.cascadeIncidentStateToLinkedAlerts({
|
|
498
|
+
projectId: createdItem.projectId,
|
|
499
|
+
incidentId: createdItem.incidentId,
|
|
500
|
+
incidentStateId: createdItem.incidentStateId,
|
|
501
|
+
}).catch((error: Error) => {
|
|
502
|
+
logger.error(
|
|
503
|
+
`Error while carrying the incident state over to linked alerts: ${error}`,
|
|
504
|
+
{
|
|
505
|
+
projectId: createdItem.projectId?.toString(),
|
|
506
|
+
incidentId: createdItem.incidentId?.toString(),
|
|
507
|
+
} as LogAttributes,
|
|
508
|
+
);
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
488
512
|
const stateName: string = incidentState?.name || "";
|
|
489
513
|
let stateEmoji: string = "➡️";
|
|
490
514
|
|
package/Server/Services/Index.ts
CHANGED
|
@@ -361,6 +361,7 @@ import IncidentEpisodeLabelRuleService from "./IncidentEpisodeLabelRuleService";
|
|
|
361
361
|
import IncidentEpisodeFeedService from "./IncidentEpisodeFeedService";
|
|
362
362
|
import IncidentEpisodeInternalNoteService from "./IncidentEpisodeInternalNoteService";
|
|
363
363
|
import IncidentEpisodeMemberService from "./IncidentEpisodeMemberService";
|
|
364
|
+
import IncidentAlertService from "./IncidentAlertService";
|
|
364
365
|
import IncidentEpisodeRoleMemberService from "./IncidentEpisodeRoleMemberService";
|
|
365
366
|
import IncidentEpisodeOwnerTeamService from "./IncidentEpisodeOwnerTeamService";
|
|
366
367
|
import IncidentEpisodeOwnerRuleService from "./IncidentEpisodeOwnerRuleService";
|
|
@@ -797,6 +798,7 @@ const services: Array<BaseService> = [
|
|
|
797
798
|
IncidentEpisodeFeedService,
|
|
798
799
|
IncidentEpisodeInternalNoteService,
|
|
799
800
|
IncidentEpisodeMemberService,
|
|
801
|
+
IncidentAlertService,
|
|
800
802
|
IncidentEpisodeRoleMemberService,
|
|
801
803
|
IncidentEpisodeOwnerTeamService,
|
|
802
804
|
IncidentEpisodeOwnerRuleService,
|
|
@@ -90,6 +90,7 @@ const RUM_APPLICATION_POLICY_COLUMNS: ReadonlyArray<string> = [
|
|
|
90
90
|
"sessionReplayMonthlyBudgetInGB",
|
|
91
91
|
"sessionReplayIgnoreErrorPatterns",
|
|
92
92
|
"sessionReplayTracePropagationOrigins",
|
|
93
|
+
"sessionReplaySameOriginTracePropagation",
|
|
93
94
|
"sessionReplayLcpBudgetMs",
|
|
94
95
|
"sessionReplayLongTaskBudgetMs",
|
|
95
96
|
"sessionReplaySlowRequestBudgetMs",
|
|
@@ -159,11 +160,24 @@ export interface SessionReplayGatePolicy {
|
|
|
159
160
|
ignoreErrorPatterns: Array<string>;
|
|
160
161
|
|
|
161
162
|
/*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
163
|
+
* CROSS-origin APIs the recorder may inject a W3C traceparent header
|
|
164
|
+
* into (traceparent only, never the session id). Empty = no cross-origin
|
|
165
|
+
* injection; the empty default is the CORS safety mechanism. The page's
|
|
166
|
+
* own origin is governed by sameOriginTracePropagation, not by this list.
|
|
164
167
|
*/
|
|
165
168
|
tracePropagationOrigins: Array<string>;
|
|
166
169
|
|
|
170
|
+
/*
|
|
171
|
+
* Whether the recorder adds traceparent plus a tracestate member carrying
|
|
172
|
+
* the replay session id to requests to the page's OWN origin while a
|
|
173
|
+
* session uploads, so the backend telemetry they cause links to the
|
|
174
|
+
* recording with no customer code. A same-origin request is never
|
|
175
|
+
* preflighted, so this defaults ON; the switch exists to turn it off
|
|
176
|
+
* without a customer redeploy (a same-origin request that redirects to
|
|
177
|
+
* another origin does need that origin to allow the headers).
|
|
178
|
+
*/
|
|
179
|
+
sameOriginTracePropagation: boolean;
|
|
180
|
+
|
|
167
181
|
/* Performance capture budgets in milliseconds; 0 disables each. */
|
|
168
182
|
lcpBudgetMs: number;
|
|
169
183
|
longTaskBudgetMs: number;
|
|
@@ -514,6 +528,14 @@ export default class SessionReplayGateCache {
|
|
|
514
528
|
tracePropagationOrigins: this.readStringArray(
|
|
515
529
|
appView["sessionReplayTracePropagationOrigins"],
|
|
516
530
|
),
|
|
531
|
+
/*
|
|
532
|
+
* Anything but an explicit false reads as on: the column defaults
|
|
533
|
+
* to true, so an absent or null value is the model default, like
|
|
534
|
+
* the consent / trigger / sample fallbacks above. Only an operator
|
|
535
|
+
* who switched it off gets it off.
|
|
536
|
+
*/
|
|
537
|
+
sameOriginTracePropagation:
|
|
538
|
+
appView["sessionReplaySameOriginTracePropagation"] !== false,
|
|
517
539
|
lcpBudgetMs: this.readNumber(appView["sessionReplayLcpBudgetMs"], 0),
|
|
518
540
|
longTaskBudgetMs: this.readNumber(
|
|
519
541
|
appView["sessionReplayLongTaskBudgetMs"],
|