@oneuptime/common 10.0.43 → 10.0.45

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.
Files changed (120) hide show
  1. package/Models/DatabaseModels/Workflow.ts +29 -0
  2. package/Server/API/StatusPageAPI.ts +48 -0
  3. package/Server/EnvironmentConfig.ts +5 -8
  4. package/Server/Infrastructure/Postgres/SchemaMigrations/1774559064920-MigrationName.ts +22 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  6. package/Server/Services/AlertService.ts +45 -0
  7. package/Server/Services/IncidentService.ts +81 -13
  8. package/Server/Services/LogAggregationService.ts +1 -0
  9. package/Server/Services/WorkflowService.ts +28 -1
  10. package/Server/Types/Workflow/Components/Webhook.ts +28 -5
  11. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +29 -13
  12. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +163 -26
  13. package/Server/Utils/Monitor/MonitorMetricUtil.ts +92 -0
  14. package/Server/Utils/Profiling.ts +101 -0
  15. package/Server/Utils/VM/VMRunner.ts +88 -0
  16. package/Types/Dashboard/DashboardTemplates.ts +1149 -0
  17. package/Types/Exception/ExceptionMetricType.ts +15 -0
  18. package/Types/IsolatedVM/ReturnResult.ts +3 -0
  19. package/Types/Metrics/MetricDashboardMetricType.ts +28 -0
  20. package/Types/Metrics/MetricsQuery.ts +2 -1
  21. package/Types/Monitor/CustomCodeMonitor/CapturedMetric.ts +7 -0
  22. package/Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse.ts +2 -0
  23. package/Types/Monitor/UptimeBarTooltipIncident.ts +21 -0
  24. package/Types/Profile/ProfileMetricType.ts +16 -0
  25. package/Types/Span/SpanMetricType.ts +17 -0
  26. package/UI/Components/Charts/Area/AreaChart.tsx +40 -33
  27. package/UI/Components/Charts/Bar/BarChart.tsx +37 -30
  28. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +196 -51
  29. package/UI/Components/Charts/ChartGroup/NoDataMessage.tsx +13 -0
  30. package/UI/Components/Charts/Line/LineChart.tsx +39 -32
  31. package/UI/Components/Forms/BasicForm.tsx +1 -1
  32. package/UI/Components/Graphs/DayUptimeGraph.tsx +88 -35
  33. package/UI/Components/Graphs/UptimeBarTooltip.tsx +547 -0
  34. package/UI/Components/MonitorGraphs/Uptime.tsx +7 -0
  35. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +225 -0
  36. package/UI/Components/RadioButtons/GroupRadioButtons.tsx +1 -0
  37. package/UI/Components/Tooltip/Tooltip.tsx +29 -4
  38. package/UI/Components/Workflow/ComponentSettingsModal.tsx +2 -0
  39. package/UI/Components/Workflow/DocumentationViewer.tsx +5 -0
  40. package/UI/Components/Workflow/Workflow.tsx +2 -0
  41. package/Utils/Alerts/AlertMetricType.ts +98 -0
  42. package/Utils/Incident/IncidentMetricType.ts +130 -0
  43. package/build/dist/Models/DatabaseModels/Workflow.js +30 -0
  44. package/build/dist/Models/DatabaseModels/Workflow.js.map +1 -1
  45. package/build/dist/Server/API/StatusPageAPI.js +42 -0
  46. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  47. package/build/dist/Server/EnvironmentConfig.js +2 -2
  48. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  49. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774559064920-MigrationName.js +14 -0
  50. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774559064920-MigrationName.js.map +1 -0
  51. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  52. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  53. package/build/dist/Server/Services/AlertService.js +34 -0
  54. package/build/dist/Server/Services/AlertService.js.map +1 -1
  55. package/build/dist/Server/Services/IncidentService.js +52 -9
  56. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  57. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  58. package/build/dist/Server/Services/WorkflowService.js +25 -0
  59. package/build/dist/Server/Services/WorkflowService.js.map +1 -1
  60. package/build/dist/Server/Types/Workflow/Components/Webhook.js +23 -5
  61. package/build/dist/Server/Types/Workflow/Components/Webhook.js.map +1 -1
  62. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +21 -7
  63. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  64. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +120 -21
  65. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  66. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +68 -1
  67. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  68. package/build/dist/Server/Utils/Profiling.js +80 -0
  69. package/build/dist/Server/Utils/Profiling.js.map +1 -0
  70. package/build/dist/Server/Utils/VM/VMRunner.js +68 -0
  71. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  72. package/build/dist/Types/Dashboard/DashboardTemplates.js +1095 -0
  73. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  74. package/build/dist/Types/Exception/ExceptionMetricType.js +16 -0
  75. package/build/dist/Types/Exception/ExceptionMetricType.js.map +1 -0
  76. package/build/dist/Types/Metrics/MetricDashboardMetricType.js +26 -0
  77. package/build/dist/Types/Metrics/MetricDashboardMetricType.js.map +1 -0
  78. package/build/dist/Types/Monitor/CustomCodeMonitor/CapturedMetric.js +2 -0
  79. package/build/dist/Types/Monitor/CustomCodeMonitor/CapturedMetric.js.map +1 -0
  80. package/build/dist/Types/Monitor/UptimeBarTooltipIncident.js +2 -0
  81. package/build/dist/Types/Monitor/UptimeBarTooltipIncident.js.map +1 -0
  82. package/build/dist/Types/Profile/ProfileMetricType.js +17 -0
  83. package/build/dist/Types/Profile/ProfileMetricType.js.map +1 -0
  84. package/build/dist/Types/Span/SpanMetricType.js +18 -0
  85. package/build/dist/Types/Span/SpanMetricType.js.map +1 -0
  86. package/build/dist/UI/Components/Charts/Area/AreaChart.js +21 -16
  87. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  88. package/build/dist/UI/Components/Charts/Bar/BarChart.js +20 -15
  89. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  90. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +73 -15
  91. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  92. package/build/dist/UI/Components/Charts/ChartGroup/NoDataMessage.js +7 -0
  93. package/build/dist/UI/Components/Charts/ChartGroup/NoDataMessage.js.map +1 -0
  94. package/build/dist/UI/Components/Charts/Line/LineChart.js +20 -15
  95. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  96. package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
  97. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  98. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +46 -20
  99. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  100. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +303 -0
  101. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -0
  102. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +1 -1
  103. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  104. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +118 -0
  105. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -0
  106. package/build/dist/UI/Components/RadioButtons/GroupRadioButtons.js +1 -1
  107. package/build/dist/UI/Components/RadioButtons/GroupRadioButtons.js.map +1 -1
  108. package/build/dist/UI/Components/Tooltip/Tooltip.js +13 -3
  109. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  110. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +1 -1
  111. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
  112. package/build/dist/UI/Components/Workflow/DocumentationViewer.js +1 -0
  113. package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
  114. package/build/dist/UI/Components/Workflow/Workflow.js +1 -1
  115. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  116. package/build/dist/Utils/Alerts/AlertMetricType.js +84 -0
  117. package/build/dist/Utils/Alerts/AlertMetricType.js.map +1 -0
  118. package/build/dist/Utils/Incident/IncidentMetricType.js +114 -0
  119. package/build/dist/Utils/Incident/IncidentMetricType.js.map +1 -0
  120. package/package.json +3 -2
@@ -527,6 +527,35 @@ export default class Workflow extends BaseModel {
527
527
  })
528
528
  public triggerArguments?: JSONObject = undefined;
529
529
 
530
+ @ColumnAccessControl({
531
+ create: [],
532
+ read: [
533
+ Permission.ProjectOwner,
534
+ Permission.ProjectAdmin,
535
+ Permission.ProjectMember,
536
+ Permission.ReadWorkflow,
537
+ Permission.ReadAllProjectResources,
538
+ ],
539
+ update: [
540
+ Permission.ProjectOwner,
541
+ Permission.ProjectAdmin,
542
+ Permission.EditWorkflow,
543
+ ],
544
+ })
545
+ @TableColumn({
546
+ isDefaultValueColumn: false,
547
+ required: false,
548
+ type: TableColumnType.LongText,
549
+ title: "Webhook Secret Key",
550
+ description:
551
+ "Secret key used to trigger this workflow via webhook. Use this instead of the workflow ID for security.",
552
+ })
553
+ @Column({
554
+ type: ColumnType.LongText,
555
+ nullable: true,
556
+ })
557
+ public webhookSecretKey?: string = undefined;
558
+
530
559
  // This is a BullMQ job key that is used to schedule job for this workflow. This is used internally to remove existing job.
531
560
  @ColumnAccessControl({
532
561
  create: [],
@@ -2169,6 +2169,50 @@ export default class StatusPageAPI extends BaseAPI<
2169
2169
  },
2170
2170
  });
2171
2171
 
2172
+ /*
2173
+ * Fetch all incidents (active + resolved) in the timeline date range
2174
+ * for the uptime bar tooltip and click-through
2175
+ */
2176
+ let timelineIncidents: Array<Incident> = [];
2177
+ if (
2178
+ monitorsOnStatusPage.length > 0 &&
2179
+ statusPage.showIncidentsOnStatusPage
2180
+ ) {
2181
+ timelineIncidents = await IncidentService.findBy({
2182
+ query: {
2183
+ monitors: monitorsOnStatusPage as any,
2184
+ declaredAt: QueryHelper.inBetween(startDate, endDate),
2185
+ isVisibleOnStatusPage: true,
2186
+ projectId: statusPage.projectId!,
2187
+ },
2188
+ select: {
2189
+ _id: true,
2190
+ title: true,
2191
+ declaredAt: true,
2192
+ incidentSeverity: {
2193
+ name: true,
2194
+ color: true,
2195
+ },
2196
+ currentIncidentState: {
2197
+ _id: true,
2198
+ name: true,
2199
+ color: true,
2200
+ },
2201
+ monitors: {
2202
+ _id: true,
2203
+ },
2204
+ },
2205
+ sort: {
2206
+ declaredAt: SortOrder.Descending,
2207
+ },
2208
+ skip: 0,
2209
+ limit: LIMIT_PER_PROJECT,
2210
+ props: {
2211
+ isRoot: true,
2212
+ },
2213
+ });
2214
+ }
2215
+
2172
2216
  const overallStatus: MonitorStatus | null =
2173
2217
  StatusPageService.getOverallMonitorStatus({
2174
2218
  statusPageResources,
@@ -2251,6 +2295,10 @@ export default class StatusPageAPI extends BaseAPI<
2251
2295
  monitorGroupCurrentStatuses,
2252
2296
  ),
2253
2297
  monitorsInGroup: JSONFunctions.serialize(monitorsInGroup),
2298
+ timelineIncidents: BaseModel.toJSONArray(
2299
+ timelineIncidents,
2300
+ Incident,
2301
+ ),
2254
2302
  };
2255
2303
 
2256
2304
  return Response.sendJsonObjectResponse(req, res, response);
@@ -176,15 +176,9 @@ export const AppApiHostname: Hostname = Hostname.fromString(
176
176
  }`,
177
177
  );
178
178
 
179
- export const OpenTelemetryIngestHostname: Hostname = Hostname.fromString(
180
- `${process.env["SERVER_TELEMETRY_HOSTNAME"] || "localhost"}:${
181
- process.env["TELEMETRY_PORT"] || 80
182
- }`,
183
- );
184
-
185
179
  export const WorkerHostname: Hostname = Hostname.fromString(
186
- `${process.env["SERVER_WORKER_HOSTNAME"] || "localhost"}:${
187
- process.env["WORKER_PORT"] || 80
180
+ `${process.env["SERVER_APP_HOSTNAME"] || "localhost"}:${
181
+ process.env["APP_PORT"] || 80
188
182
  }`,
189
183
  );
190
184
 
@@ -406,6 +400,9 @@ export const IpWhitelist: string = process.env["IP_WHITELIST"] || "";
406
400
  export const DisableTelemetry: boolean =
407
401
  process.env["DISABLE_TELEMETRY"] === "true";
408
402
 
403
+ export const EnableProfiling: boolean =
404
+ process.env["ENABLE_PROFILING"] === "true";
405
+
409
406
  export const IsEnterpriseEdition: boolean =
410
407
  process.env["IS_ENTERPRISE_EDITION"] === "true";
411
408
 
@@ -0,0 +1,22 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class MigrationName1774559064920 implements MigrationInterface {
4
+ public name = "MigrationName1774559064920";
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(
8
+ `ALTER TABLE "Workflow" ADD "webhookSecretKey" text`,
9
+ );
10
+
11
+ // Set secret key to existing workflow ID so current webhook URLs keep working.
12
+ await queryRunner.query(
13
+ `UPDATE "Workflow" SET "webhookSecretKey" = "_id"::text WHERE "webhookSecretKey" IS NULL`,
14
+ );
15
+ }
16
+
17
+ public async down(queryRunner: QueryRunner): Promise<void> {
18
+ await queryRunner.query(
19
+ `ALTER TABLE "Workflow" DROP COLUMN "webhookSecretKey"`,
20
+ );
21
+ }
22
+ }
@@ -274,6 +274,7 @@ import { MigrationName1774524742177 } from "./1774524742177-MigrationName";
274
274
  import { MigrationName1774524742178 } from "./1774524742178-MigrationName";
275
275
  import { MigrationName1774524742179 } from "./1774524742179-MigrationName";
276
276
  import { MigrationName1774559064919 } from "./1774559064919-MigrationName";
277
+ import { MigrationName1774559064920 } from "./1774559064920-MigrationName";
277
278
 
278
279
  export default [
279
280
  InitialMigration,
@@ -552,4 +553,5 @@ export default [
552
553
  MigrationName1774524742178,
553
554
  MigrationName1774524742179,
554
555
  MigrationName1774559064919,
556
+ MigrationName1774559064920,
555
557
  ];
@@ -31,6 +31,8 @@ import { IsBillingEnabled } from "../EnvironmentConfig";
31
31
  import logger from "../Utils/Logger";
32
32
  import TelemetryUtil from "../Utils/Telemetry/Telemetry";
33
33
  import MetricService from "./MetricService";
34
+ import GlobalConfigService from "./GlobalConfigService";
35
+ import GlobalConfig from "../../Models/DatabaseModels/GlobalConfig";
34
36
  import OneUptimeDate from "../../Types/Date";
35
37
  import Metric, {
36
38
  MetricPointType,
@@ -1060,6 +1062,39 @@ ${alertSeverity.name}
1060
1062
  });
1061
1063
  }
1062
1064
 
1065
+ private static readonly DEFAULT_METRIC_RETENTION_DAYS: number = 180;
1066
+
1067
+ private async getMetricRetentionDays(): Promise<number> {
1068
+ try {
1069
+ const globalConfig: GlobalConfig | null =
1070
+ await GlobalConfigService.findOneBy({
1071
+ query: {
1072
+ _id: ObjectID.getZeroObjectID().toString(),
1073
+ },
1074
+ props: {
1075
+ isRoot: true,
1076
+ },
1077
+ select: {
1078
+ monitorMetricRetentionInDays: true,
1079
+ },
1080
+ });
1081
+
1082
+ if (
1083
+ globalConfig &&
1084
+ globalConfig.monitorMetricRetentionInDays !== undefined &&
1085
+ globalConfig.monitorMetricRetentionInDays !== null &&
1086
+ globalConfig.monitorMetricRetentionInDays > 0
1087
+ ) {
1088
+ return globalConfig.monitorMetricRetentionInDays;
1089
+ }
1090
+ } catch (error) {
1091
+ logger.error("Error fetching metric retention config, using default:");
1092
+ logger.error(error);
1093
+ }
1094
+
1095
+ return Service.DEFAULT_METRIC_RETENTION_DAYS;
1096
+ }
1097
+
1063
1098
  @CaptureSpan()
1064
1099
  public async refreshAlertMetrics(data: { alertId: ObjectID }): Promise<void> {
1065
1100
  const alert: Model | null = await this.findOneById({
@@ -1130,6 +1165,12 @@ ${alertSeverity.name}
1130
1165
  const itemsToSave: Array<Metric> = [];
1131
1166
  const metricTypesMap: Dictionary<MetricType> = {};
1132
1167
 
1168
+ const metricRetentionDays: number = await this.getMetricRetentionDays();
1169
+ const alertMetricRetentionDate: Date = OneUptimeDate.addRemoveDays(
1170
+ OneUptimeDate.getCurrentDate(),
1171
+ metricRetentionDays,
1172
+ );
1173
+
1133
1174
  // now we need to create new metrics for this alert - TimeToAcknowledge, TimeToResolve, AlertCount, AlertDuration
1134
1175
  const alertStartsAt: Date =
1135
1176
  firstAlertStateTimeline?.startsAt ||
@@ -1160,6 +1201,7 @@ ${alertSeverity.name}
1160
1201
  alertCountMetric.time,
1161
1202
  );
1162
1203
  alertCountMetric.metricPointType = MetricPointType.Sum;
1204
+ alertCountMetric.retentionDate = alertMetricRetentionDate;
1163
1205
 
1164
1206
  itemsToSave.push(alertCountMetric);
1165
1207
 
@@ -1214,6 +1256,7 @@ ${alertSeverity.name}
1214
1256
  timeToAcknowledgeMetric.time,
1215
1257
  );
1216
1258
  timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
1259
+ timeToAcknowledgeMetric.retentionDate = alertMetricRetentionDate;
1217
1260
 
1218
1261
  itemsToSave.push(timeToAcknowledgeMetric);
1219
1262
 
@@ -1270,6 +1313,7 @@ ${alertSeverity.name}
1270
1313
  timeToResolveMetric.time,
1271
1314
  );
1272
1315
  timeToResolveMetric.metricPointType = MetricPointType.Sum;
1316
+ timeToResolveMetric.retentionDate = alertMetricRetentionDate;
1273
1317
 
1274
1318
  itemsToSave.push(timeToResolveMetric);
1275
1319
 
@@ -1319,6 +1363,7 @@ ${alertSeverity.name}
1319
1363
  alertDurationMetric.time,
1320
1364
  );
1321
1365
  alertDurationMetric.metricPointType = MetricPointType.Sum;
1366
+ alertDurationMetric.retentionDate = alertMetricRetentionDate;
1322
1367
 
1323
1368
  itemsToSave.push(alertDurationMetric);
1324
1369
 
@@ -36,6 +36,8 @@ import MonitorStatusTimeline from "../../Models/DatabaseModels/MonitorStatusTime
36
36
  import User from "../../Models/DatabaseModels/User";
37
37
  import { IsBillingEnabled } from "../EnvironmentConfig";
38
38
  import MetricService from "./MetricService";
39
+ import GlobalConfigService from "./GlobalConfigService";
40
+ import GlobalConfig from "../../Models/DatabaseModels/GlobalConfig";
39
41
  import IncidentMetricType from "../../Types/Incident/IncidentMetricType";
40
42
  import Metric, {
41
43
  MetricPointType,
@@ -1396,6 +1398,12 @@ ${incident.remediationNotes || "No remediation notes provided."}
1396
1398
  postmortemMetric.time,
1397
1399
  );
1398
1400
  postmortemMetric.metricPointType = MetricPointType.Sum;
1401
+ const postmortemRetentionDays: number =
1402
+ await this.getMetricRetentionDays();
1403
+ postmortemMetric.retentionDate = OneUptimeDate.addRemoveDays(
1404
+ OneUptimeDate.getCurrentDate(),
1405
+ postmortemRetentionDays,
1406
+ );
1399
1407
 
1400
1408
  await MetricService.create({
1401
1409
  data: postmortemMetric,
@@ -1583,6 +1591,12 @@ ${incidentSeverity.name}
1583
1591
  severityChangeMetric.time,
1584
1592
  );
1585
1593
  severityChangeMetric.metricPointType = MetricPointType.Sum;
1594
+ const severityRetentionDays: number =
1595
+ await this.getMetricRetentionDays();
1596
+ severityChangeMetric.retentionDate = OneUptimeDate.addRemoveDays(
1597
+ OneUptimeDate.getCurrentDate(),
1598
+ severityRetentionDays,
1599
+ );
1586
1600
 
1587
1601
  await MetricService.create({
1588
1602
  data: severityChangeMetric,
@@ -2075,6 +2089,39 @@ ${incidentSeverity.name}
2075
2089
  });
2076
2090
  }
2077
2091
 
2092
+ private static readonly DEFAULT_METRIC_RETENTION_DAYS: number = 180;
2093
+
2094
+ private async getMetricRetentionDays(): Promise<number> {
2095
+ try {
2096
+ const globalConfig: GlobalConfig | null =
2097
+ await GlobalConfigService.findOneBy({
2098
+ query: {
2099
+ _id: ObjectID.getZeroObjectID().toString(),
2100
+ },
2101
+ props: {
2102
+ isRoot: true,
2103
+ },
2104
+ select: {
2105
+ monitorMetricRetentionInDays: true,
2106
+ },
2107
+ });
2108
+
2109
+ if (
2110
+ globalConfig &&
2111
+ globalConfig.monitorMetricRetentionInDays !== undefined &&
2112
+ globalConfig.monitorMetricRetentionInDays !== null &&
2113
+ globalConfig.monitorMetricRetentionInDays > 0
2114
+ ) {
2115
+ return globalConfig.monitorMetricRetentionInDays;
2116
+ }
2117
+ } catch (error) {
2118
+ logger.error("Error fetching metric retention config, using default:");
2119
+ logger.error(error);
2120
+ }
2121
+
2122
+ return Service.DEFAULT_METRIC_RETENTION_DAYS;
2123
+ }
2124
+
2078
2125
  @CaptureSpan()
2079
2126
  public async refreshIncidentMetrics(data: {
2080
2127
  incidentId: ObjectID;
@@ -2223,6 +2270,12 @@ ${incidentSeverity.name}
2223
2270
 
2224
2271
  const itemsToSave: Array<Metric> = [];
2225
2272
 
2273
+ const metricRetentionDays: number = await this.getMetricRetentionDays();
2274
+ const incidentMetricRetentionDate: Date = OneUptimeDate.addRemoveDays(
2275
+ OneUptimeDate.getCurrentDate(),
2276
+ metricRetentionDays,
2277
+ );
2278
+
2226
2279
  // now we need to create new metrics for this incident - TimeToAcknowledge, TimeToResolve, IncidentCount, IncidentDuration
2227
2280
 
2228
2281
  const incidentStartsAt: Date =
@@ -2233,24 +2286,34 @@ ${incidentSeverity.name}
2233
2286
 
2234
2287
  const metricTypesMap: Dictionary<MetricType> = {};
2235
2288
 
2236
- // common attributes shared by all incident metrics
2289
+ /*
2290
+ * common attributes shared by all incident metrics
2291
+ * All values must be strings for ClickHouse Map(String, String) storage.
2292
+ * Arrays are joined as comma-separated strings.
2293
+ */
2237
2294
  const baseMetricAttributes: JSONObject = {
2238
2295
  incidentId: data.incidentId.toString(),
2239
2296
  projectId: incident.projectId.toString(),
2240
- monitorIds:
2241
- incident.monitors?.map((monitor: Monitor) => {
2242
- return monitor._id?.toString();
2243
- }) || [],
2244
- monitorNames:
2245
- incident.monitors?.map((monitor: Monitor) => {
2246
- return monitor.name?.toString();
2247
- }) || [],
2297
+ monitorIds: (
2298
+ incident.monitors
2299
+ ?.map((monitor: Monitor) => {
2300
+ return monitor._id?.toString();
2301
+ })
2302
+ .filter(Boolean) || []
2303
+ ).join(", "),
2304
+ monitorNames: (
2305
+ incident.monitors
2306
+ ?.map((monitor: Monitor) => {
2307
+ return monitor.name?.toString();
2308
+ })
2309
+ .filter(Boolean) || []
2310
+ ).join(", "),
2248
2311
  incidentSeverityId: incident.incidentSeverity?._id?.toString(),
2249
2312
  incidentSeverityName: incident.incidentSeverity?.name?.toString(),
2250
- ownerUserIds: ownerUserIds,
2251
- ownerUserNames: ownerUserNames,
2252
- ownerTeamIds: ownerTeamIds,
2253
- ownerTeamNames: ownerTeamNames,
2313
+ ownerUserIds: ownerUserIds.join(", "),
2314
+ ownerUserNames: ownerUserNames.join(", "),
2315
+ ownerTeamIds: ownerTeamIds.join(", "),
2316
+ ownerTeamNames: ownerTeamNames.join(", "),
2254
2317
  };
2255
2318
 
2256
2319
  const incidentCountMetric: Metric = new Metric();
@@ -2270,6 +2333,7 @@ ${incidentSeverity.name}
2270
2333
  incidentCountMetric.time,
2271
2334
  );
2272
2335
  incidentCountMetric.metricPointType = MetricPointType.Sum;
2336
+ incidentCountMetric.retentionDate = incidentMetricRetentionDate;
2273
2337
 
2274
2338
  itemsToSave.push(incidentCountMetric);
2275
2339
 
@@ -2321,6 +2385,7 @@ ${incidentSeverity.name}
2321
2385
  timeToAcknowledgeMetric.time,
2322
2386
  );
2323
2387
  timeToAcknowledgeMetric.metricPointType = MetricPointType.Sum;
2388
+ timeToAcknowledgeMetric.retentionDate = incidentMetricRetentionDate;
2324
2389
 
2325
2390
  itemsToSave.push(timeToAcknowledgeMetric);
2326
2391
 
@@ -2374,6 +2439,7 @@ ${incidentSeverity.name}
2374
2439
  timeToResolveMetric.time,
2375
2440
  );
2376
2441
  timeToResolveMetric.metricPointType = MetricPointType.Sum;
2442
+ timeToResolveMetric.retentionDate = incidentMetricRetentionDate;
2377
2443
 
2378
2444
  itemsToSave.push(timeToResolveMetric);
2379
2445
 
@@ -2422,6 +2488,7 @@ ${incidentSeverity.name}
2422
2488
  incidentDurationMetric.time,
2423
2489
  );
2424
2490
  incidentDurationMetric.metricPointType = MetricPointType.Sum;
2491
+ incidentDurationMetric.retentionDate = incidentMetricRetentionDate;
2425
2492
 
2426
2493
  itemsToSave.push(incidentDurationMetric);
2427
2494
 
@@ -2474,6 +2541,7 @@ ${incidentSeverity.name}
2474
2541
  timeInStateMetric.time,
2475
2542
  );
2476
2543
  timeInStateMetric.metricPointType = MetricPointType.Sum;
2544
+ timeInStateMetric.retentionDate = incidentMetricRetentionDate;
2477
2545
 
2478
2546
  itemsToSave.push(timeInStateMetric);
2479
2547
  }
@@ -676,6 +676,7 @@ export class LogAggregationService {
676
676
  bodySearchText?: string | undefined;
677
677
  traceIds?: Array<string> | undefined;
678
678
  spanIds?: Array<string> | undefined;
679
+ attributes?: Record<string, string> | undefined;
679
680
  }): Promise<Array<JSONObject>> {
680
681
  const maxLimit: number = Math.min(request.limit || 10000, 10000);
681
682
 
@@ -1,6 +1,6 @@
1
1
  import { WorkflowHostname } from "../EnvironmentConfig";
2
2
  import ClusterKeyAuthorization from "../Middleware/ClusterKeyAuthorization";
3
- import { OnUpdate } from "../Types/Database/Hooks";
3
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
4
4
  import DatabaseService from "./DatabaseService";
5
5
  import EmptyResponseData from "../../Types/API/EmptyResponse";
6
6
  import Protocol from "../../Types/API/Protocol";
@@ -17,12 +17,39 @@ import {
17
17
  import API from "../../Utils/API";
18
18
  import Model from "../../Models/DatabaseModels/Workflow";
19
19
  import logger from "../Utils/Logger";
20
+ import UUID from "../../Utils/UUID";
20
21
 
21
22
  export class Service extends DatabaseService<Model> {
22
23
  public constructor() {
23
24
  super(Model);
24
25
  }
25
26
 
27
+ @CaptureSpan()
28
+ protected override async onCreateSuccess(
29
+ _onCreate: OnCreate<Model>,
30
+ createdItem: Model,
31
+ ): Promise<Model> {
32
+ // Auto-generate webhook secret key for new workflows.
33
+ if (!createdItem.webhookSecretKey && createdItem._id) {
34
+ const secretKey: string = UUID.generate();
35
+
36
+ await this.updateOneById({
37
+ id: new ObjectID(createdItem._id),
38
+ data: {
39
+ webhookSecretKey: secretKey,
40
+ } as any,
41
+ props: {
42
+ isRoot: true,
43
+ ignoreHooks: true,
44
+ },
45
+ });
46
+
47
+ createdItem.webhookSecretKey = secretKey;
48
+ }
49
+
50
+ return createdItem;
51
+ }
52
+
26
53
  @CaptureSpan()
27
54
  protected override async onUpdateSuccess(
28
55
  onUpdate: OnUpdate<Model>,
@@ -13,6 +13,8 @@ import ComponentMetadata, { Port } from "../../../../Types/Workflow/Component";
13
13
  import ComponentID from "../../../../Types/Workflow/ComponentID";
14
14
  import WebhookComponents from "../../../../Types/Workflow/Components/Webhook";
15
15
  import CaptureSpan from "../../../Utils/Telemetry/CaptureSpan";
16
+ import WorkflowService from "../../../Services/WorkflowService";
17
+ import Workflow from "../../../../Models/DatabaseModels/Workflow";
16
18
 
17
19
  export default class WebhookTrigger extends TriggerCode {
18
20
  public constructor() {
@@ -54,7 +56,7 @@ export default class WebhookTrigger extends TriggerCode {
54
56
  @CaptureSpan()
55
57
  public override async init(props: InitProps): Promise<void> {
56
58
  props.router.get(
57
- `/trigger/:workflowId`,
59
+ `/trigger/:secretkey`,
58
60
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
59
61
  try {
60
62
  await this.initTrigger(req, res, props);
@@ -65,7 +67,7 @@ export default class WebhookTrigger extends TriggerCode {
65
67
  );
66
68
 
67
69
  props.router.post(
68
- `/trigger/:workflowId`,
70
+ `/trigger/:secretkey`,
69
71
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
70
72
  try {
71
73
  await this.initTrigger(req, res, props);
@@ -82,12 +84,33 @@ export default class WebhookTrigger extends TriggerCode {
82
84
  res: ExpressResponse,
83
85
  props: InitProps,
84
86
  ): Promise<void> {
85
- /// Run Graph.
87
+ const secretKey: string = req.params["secretkey"] as string;
86
88
 
87
- // check if this workflow has the trigger enabled.
89
+ if (!secretKey) {
90
+ throw new BadDataException("Secret key is required to trigger workflow.");
91
+ }
92
+
93
+ // Look up the workflow by webhook secret key.
94
+ const workflow: Workflow | null = await WorkflowService.findOneBy({
95
+ query: {
96
+ webhookSecretKey: secretKey,
97
+ },
98
+ select: {
99
+ _id: true,
100
+ },
101
+ props: {
102
+ isRoot: true,
103
+ },
104
+ });
105
+
106
+ if (!workflow || !workflow._id) {
107
+ throw new BadDataException(
108
+ "Workflow not found for the provided secret key.",
109
+ );
110
+ }
88
111
 
89
112
  const executeWorkflow: ExecuteWorkflowType = {
90
- workflowId: new ObjectID(req.params["workflowId"] as string),
113
+ workflowId: new ObjectID(workflow._id),
91
114
  returnValues: {
92
115
  "request-headers": req.headers,
93
116
  "request-params": req.query,
@@ -208,6 +208,12 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
208
208
  )}')`;
209
209
  }
210
210
 
211
+ if (column.type === TableColumnType.DateTime64 && value instanceof Date) {
212
+ value = `parseDateTimeBestEffortOrNull('${OneUptimeDate.toClickhouseDateTime64(
213
+ value as Date,
214
+ )}')`;
215
+ }
216
+
211
217
  if (column.type === TableColumnType.Number) {
212
218
  if (typeof value === "string") {
213
219
  value = parseInt(value);
@@ -419,23 +425,33 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
419
425
  tableColumn.type === TableColumnType.MapStringString &&
420
426
  typeof value === "object"
421
427
  ) {
422
- const mapValue: Record<string, string> = value as Record<
423
- string,
424
- string
425
- >;
428
+ const mapValue: Record<string, string | Search<string>> =
429
+ value as Record<string, string | Search<string>>;
426
430
  for (const mapKey in mapValue) {
427
431
  if (mapValue[mapKey] === undefined) {
428
432
  continue;
429
433
  }
430
- whereStatement.append(
431
- SQL`AND ${key}[${{
432
- value: mapKey,
433
- type: TableColumnType.Text,
434
- }}] = ${{
435
- value: mapValue[mapKey] as string,
436
- type: TableColumnType.Text,
437
- }}`,
438
- );
434
+ if (mapValue[mapKey] instanceof Search) {
435
+ whereStatement.append(
436
+ SQL`AND ${key}[${{
437
+ value: mapKey,
438
+ type: TableColumnType.Text,
439
+ }}] ILIKE ${{
440
+ value: mapValue[mapKey] as Search<string>,
441
+ type: TableColumnType.Text,
442
+ }}`,
443
+ );
444
+ } else {
445
+ whereStatement.append(
446
+ SQL`AND ${key}[${{
447
+ value: mapKey,
448
+ type: TableColumnType.Text,
449
+ }}] = ${{
450
+ value: mapValue[mapKey] as string,
451
+ type: TableColumnType.Text,
452
+ }}`,
453
+ );
454
+ }
439
455
  }
440
456
  } else if (
441
457
  (tableColumn.type === TableColumnType.JSON ||