@oneuptime/common 10.4.11 → 10.4.12

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 (86) hide show
  1. package/Models/DatabaseModels/Alert.ts +332 -0
  2. package/Models/DatabaseModels/Incident.ts +332 -0
  3. package/Models/DatabaseModels/RunbookAgent.ts +16 -2
  4. package/Server/EnvironmentConfig.ts +16 -0
  5. package/Server/Infrastructure/ClickhouseConfig.ts +14 -0
  6. package/Server/Infrastructure/ClickhouseDatabase.ts +20 -3
  7. package/Server/Infrastructure/InMemoryTTLCache.ts +61 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.ts +253 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.ts +60 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  11. package/Server/Middleware/TelemetryIngest.ts +6 -38
  12. package/Server/Middleware/UserAuthorization.ts +1 -11
  13. package/Server/Services/AnalyticsDatabaseService.ts +33 -1
  14. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +6 -0
  15. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +1 -0
  16. package/Server/Services/OnCallDutyPolicyScheduleService.ts +17 -0
  17. package/Server/Services/TelemetryIngestionKeyService.ts +90 -1
  18. package/Tests/Server/Middleware/UserAuthorization.test.ts +0 -7
  19. package/Types/Dashboard/DashboardComponentType.ts +0 -1
  20. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +2 -0
  21. package/Types/Dashboard/DashboardComponents/DashboardTableComponent.ts +74 -1
  22. package/Types/Dashboard/DashboardTemplates.ts +164 -51
  23. package/Types/Monitor/MonitorType.ts +1 -1
  24. package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +36 -9
  25. package/Utils/Dashboard/Components/DashboardTableComponent.ts +80 -17
  26. package/Utils/Dashboard/Components/Index.ts +0 -7
  27. package/build/dist/Models/DatabaseModels/Alert.js +324 -0
  28. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  29. package/build/dist/Models/DatabaseModels/Incident.js +324 -0
  30. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  31. package/build/dist/Models/DatabaseModels/RunbookAgent.js +16 -2
  32. package/build/dist/Models/DatabaseModels/RunbookAgent.js.map +1 -1
  33. package/build/dist/Server/EnvironmentConfig.js +8 -0
  34. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  35. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +9 -1
  36. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  37. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +12 -3
  38. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
  39. package/build/dist/Server/Infrastructure/InMemoryTTLCache.js +49 -0
  40. package/build/dist/Server/Infrastructure/InMemoryTTLCache.js.map +1 -0
  41. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.js +100 -0
  42. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779302536475-AttachKubernetesAndDockerToIncidentAndAlert.js.map +1 -0
  43. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.js +28 -0
  44. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1779303924241-AttachServiceToIncidentAndAlert.js.map +1 -0
  45. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  46. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  47. package/build/dist/Server/Middleware/TelemetryIngest.js +2 -26
  48. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  49. package/build/dist/Server/Middleware/UserAuthorization.js +1 -7
  50. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  51. package/build/dist/Server/Services/AnalyticsDatabaseService.js +23 -2
  52. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  53. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +28 -24
  54. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  55. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +1 -1
  56. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  57. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +18 -2
  58. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  59. package/build/dist/Server/Services/TelemetryIngestionKeyService.js +83 -0
  60. package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
  61. package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js +0 -7
  62. package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js.map +1 -1
  63. package/build/dist/Types/Dashboard/DashboardComponentType.js +0 -1
  64. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  65. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +2 -0
  66. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  67. package/build/dist/Types/Dashboard/DashboardComponents/DashboardTableComponent.js +13 -1
  68. package/build/dist/Types/Dashboard/DashboardComponents/DashboardTableComponent.js.map +1 -1
  69. package/build/dist/Types/Dashboard/DashboardTemplates.js +142 -42
  70. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  71. package/build/dist/Types/Monitor/MonitorType.js +1 -1
  72. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  73. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +27 -7
  74. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
  75. package/build/dist/Utils/Dashboard/Components/DashboardTableComponent.js +68 -16
  76. package/build/dist/Utils/Dashboard/Components/DashboardTableComponent.js.map +1 -1
  77. package/build/dist/Utils/Dashboard/Components/Index.js +0 -4
  78. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  79. package/package.json +1 -2
  80. package/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.ts +0 -27
  81. package/Typings/elkjs.d.ts +0 -30
  82. package/Utils/Dashboard/Components/DashboardHostMetricChartComponent.ts +0 -132
  83. package/build/dist/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.js +0 -11
  84. package/build/dist/Types/Dashboard/DashboardComponents/DashboardHostMetricChartComponent.js.map +0 -1
  85. package/build/dist/Utils/Dashboard/Components/DashboardHostMetricChartComponent.js +0 -113
  86. package/build/dist/Utils/Dashboard/Components/DashboardHostMetricChartComponent.js.map +0 -1
@@ -5,13 +5,13 @@ import DashboardComponentType from "./DashboardComponentType";
5
5
  import DashboardChartType from "./Chart/ChartType";
6
6
  import ObjectID from "../ObjectID";
7
7
  import DashboardBaseComponent from "./DashboardComponents/DashboardBaseComponent";
8
+ import DashboardVariable, { DashboardVariableType } from "./DashboardVariable";
8
9
  import IconProp from "../Icon/IconProp";
9
10
  import MetricsAggregationType from "../Metrics/MetricsAggregationType";
10
11
  import IncidentMetricType from "../Incident/IncidentMetricType";
11
12
  import MonitorMetricType from "../Monitor/MonitorMetricType";
12
13
  import MetricDashboardMetricType from "../Metrics/MetricDashboardMetricType";
13
14
  import { DashboardValueTrendDirection } from "./DashboardComponents/DashboardValueComponent";
14
- import { HostMetricKind } from "./DashboardComponents/DashboardHostMetricChartComponent";
15
15
 
16
16
  /*
17
17
  * Trace / Exception / Profiles entries are intentionally not in this
@@ -88,6 +88,19 @@ interface MetricConfig {
88
88
  aggregationType: MetricsAggregationType;
89
89
  legend?: string;
90
90
  legendUnit?: string;
91
+ /*
92
+ * OpenTelemetry attribute keys to fan the query out across (e.g.
93
+ * ["host.name"] for one series per host). When set, the chart renders
94
+ * one series per unique value combination.
95
+ */
96
+ groupByAttributeKeys?: Array<string>;
97
+ /*
98
+ * Plot the per-second rate of change instead of the raw cumulative
99
+ * counter. Required for OTel cumulative counters such as
100
+ * `system.disk.io` and `system.network.io` so the chart shows I/O rate
101
+ * rather than bytes-since-boot.
102
+ */
103
+ transformAsRate?: boolean;
91
104
  }
92
105
 
93
106
  function buildMetricQueryConfig(config: MetricConfig): Record<string, unknown> {
@@ -104,8 +117,10 @@ function buildMetricQueryConfig(config: MetricConfig): Record<string, unknown> {
104
117
  metricName: config.metricName,
105
118
  aggegationType: config.aggregationType,
106
119
  },
107
- groupBy: undefined,
120
+ groupBy: config.groupByAttributeKeys ? { attributes: true } : undefined,
121
+ groupByAttributeKeys: config.groupByAttributeKeys,
108
122
  },
123
+ transformAsRate: config.transformAsRate,
109
124
  };
110
125
  }
111
126
 
@@ -383,6 +398,31 @@ function createKubernetesNodeListComponent(data: {
383
398
  };
384
399
  }
385
400
 
401
+ /*
402
+ * Template variables are pre-built TelemetryAttribute variables that
403
+ * ship with the template — the toolbar renders a "Cluster" /
404
+ * "Namespace" / "Host" picker without the user having to open the
405
+ * Variables modal. Resource attributes from the OTel collector are
406
+ * stored in ClickHouse under the `resource.` prefix (see the comment
407
+ * in MonitorAlert.ts), so the binding keys here mirror what the
408
+ * Kubernetes / Host detail pages already filter on.
409
+ */
410
+ function createTelemetryAttributeVariable(data: {
411
+ name: string;
412
+ label: string;
413
+ attributeKey: string;
414
+ isMultiSelect?: boolean;
415
+ }): DashboardVariable {
416
+ return {
417
+ id: ObjectID.generate().toString(),
418
+ name: data.name,
419
+ label: data.label,
420
+ type: DashboardVariableType.TelemetryAttribute,
421
+ attributeKey: data.attributeKey,
422
+ isMultiSelect: data.isMultiSelect ?? false,
423
+ };
424
+ }
425
+
386
426
  function createHostListComponent(data: {
387
427
  title: string;
388
428
  top: number;
@@ -412,33 +452,6 @@ function createHostListComponent(data: {
412
452
  };
413
453
  }
414
454
 
415
- function createHostMetricChartComponent(data: {
416
- title: string;
417
- description?: string;
418
- metricKind: HostMetricKind;
419
- top: number;
420
- left: number;
421
- width: number;
422
- height: number;
423
- }): DashboardBaseComponent {
424
- return {
425
- _type: ObjectType.DashboardComponent,
426
- componentType: DashboardComponentType.HostMetricChart,
427
- componentId: ObjectID.generate(),
428
- topInDashboardUnits: data.top,
429
- leftInDashboardUnits: data.left,
430
- widthInDashboardUnits: data.width,
431
- heightInDashboardUnits: data.height,
432
- minHeightInDashboardUnits: 3,
433
- minWidthInDashboardUnits: 6,
434
- arguments: {
435
- title: data.title,
436
- description: data.description,
437
- metricKind: data.metricKind,
438
- },
439
- };
440
- }
441
-
442
455
  // -- Dashboard configs --
443
456
 
444
457
  function createMonitorDashboardConfig(): DashboardViewConfig {
@@ -630,9 +643,25 @@ function createMonitorDashboardConfig(): DashboardViewConfig {
630
643
  }),
631
644
  ];
632
645
 
646
+ /*
647
+ * Monitor metrics are stored with bare attribute keys (`monitorName`,
648
+ * `probeName`) rather than the OTel `resource.*` prefix — see
649
+ * MonitorMetricUtil.buildAttributes — so the variable binds to the
650
+ * bare key. Multi-select lets users pin a small group of monitors.
651
+ */
652
+ const variables: Array<DashboardVariable> = [
653
+ createTelemetryAttributeVariable({
654
+ name: "monitor",
655
+ label: "Monitor",
656
+ attributeKey: "monitorName",
657
+ isMultiSelect: true,
658
+ }),
659
+ ];
660
+
633
661
  return {
634
662
  _type: ObjectType.DashboardViewConfig,
635
663
  components,
664
+ variables,
636
665
  heightInDashboardUnits: Math.max(DashboardSize.heightInDashboardUnits, 13),
637
666
  };
638
667
  }
@@ -1122,9 +1151,31 @@ function createKubernetesDashboardConfig(): DashboardViewConfig {
1122
1151
  }),
1123
1152
  ];
1124
1153
 
1154
+ /*
1155
+ * Pre-built variables let the user scope every widget on the dashboard
1156
+ * to a single cluster / namespace from the toolbar. Multi-select is on
1157
+ * for namespace so users can pick a couple of namespaces at once;
1158
+ * cluster stays single-select since the typical "compare two clusters"
1159
+ * workflow lives on the Compare page.
1160
+ */
1161
+ const variables: Array<DashboardVariable> = [
1162
+ createTelemetryAttributeVariable({
1163
+ name: "cluster",
1164
+ label: "Cluster",
1165
+ attributeKey: "resource.k8s.cluster.name",
1166
+ }),
1167
+ createTelemetryAttributeVariable({
1168
+ name: "namespace",
1169
+ label: "Namespace",
1170
+ attributeKey: "resource.k8s.namespace.name",
1171
+ isMultiSelect: true,
1172
+ }),
1173
+ ];
1174
+
1125
1175
  return {
1126
1176
  _type: ObjectType.DashboardViewConfig,
1127
1177
  components,
1178
+ variables,
1128
1179
  heightInDashboardUnits: Math.max(DashboardSize.heightInDashboardUnits, 21),
1129
1180
  };
1130
1181
  }
@@ -1442,9 +1493,25 @@ function createMetricsDashboardConfig(): DashboardViewConfig {
1442
1493
  }),
1443
1494
  ];
1444
1495
 
1496
+ /*
1497
+ * Most HTTP / runtime metrics on this dashboard carry the standard
1498
+ * OTel `resource.service.name` attribute, so scoping by service is
1499
+ * the most useful default. Multi-select keeps the cross-service view
1500
+ * available — e.g. compare API and worker on one chart.
1501
+ */
1502
+ const variables: Array<DashboardVariable> = [
1503
+ createTelemetryAttributeVariable({
1504
+ name: "service",
1505
+ label: "Service",
1506
+ attributeKey: "resource.service.name",
1507
+ isMultiSelect: true,
1508
+ }),
1509
+ ];
1510
+
1445
1511
  return {
1446
1512
  _type: ObjectType.DashboardViewConfig,
1447
1513
  components,
1514
+ variables,
1448
1515
  heightInDashboardUnits: Math.max(DashboardSize.heightInDashboardUnits, 24),
1449
1516
  };
1450
1517
  }
@@ -1453,11 +1520,10 @@ function createHostDashboardConfig(): DashboardViewConfig {
1453
1520
  /*
1454
1521
  * Layout notes:
1455
1522
  *
1456
- * - The HostMetricChart widget groups by `host.name` and plots one
1457
- * series per host (or a single series when filtered to one host).
1458
- * The OTel host receiver emits `system.cpu.utilization` and
1459
- * `system.memory.utilization` as [0, 1] ratios, which Value/Gauge
1460
- * widgets auto-scale to percent at render time (see
1523
+ * - Per-host charts fan out via `groupByAttributeKeys: ["host.name"]`
1524
+ * so a single chart renders one series per host. The OTel host
1525
+ * receiver emits `system.cpu.utilization` as a [0, 1] ratio, which
1526
+ * Value/Gauge widgets auto-scale to percent at render time (see
1461
1527
  * splitFormattedValue / isFractionScale).
1462
1528
  *
1463
1529
  * - The Value widget for "Avg Memory" uses `system.memory.usage`
@@ -1465,11 +1531,10 @@ function createHostDashboardConfig(): DashboardViewConfig {
1465
1531
  * `system.memory.utilization` is not always emitted by default OTel
1466
1532
  * host metrics.
1467
1533
  *
1468
- * - Disk and Network I/O Value widgets aggregate the raw byte counters
1469
- * with Sum. HostMetricChart turns these into per-second rates for
1470
- * the chart view; the Value tile shows the unrated total so users
1471
- * reading "Disk I/O" against the time window get an absolute byte
1472
- * figure rather than a noisy snapshot.
1534
+ * - Disk and Network I/O charts set `transformAsRate: true` so the
1535
+ * cumulative byte counters render as per-second rates rather than
1536
+ * bytes-since-boot. The matching Value tiles in row 1 keep the
1537
+ * unrated Sum so users see an absolute byte figure over the window.
1473
1538
  */
1474
1539
  const components: Array<DashboardBaseComponent> = [
1475
1540
  // Row 0: Title
@@ -1529,21 +1594,31 @@ function createHostDashboardConfig(): DashboardViewConfig {
1529
1594
  }),
1530
1595
 
1531
1596
  // Row 2-4: Per-host CPU and Memory charts
1532
- createHostMetricChartComponent({
1597
+ createChartComponent({
1533
1598
  title: "CPU Utilization by Host",
1534
- metricKind: HostMetricKind.CpuUtilization,
1599
+ chartType: DashboardChartType.Line,
1535
1600
  top: 2,
1536
1601
  left: 0,
1537
1602
  width: 6,
1538
1603
  height: 3,
1604
+ metricConfig: {
1605
+ metricName: MetricDashboardMetricType.SystemCpuUtilization,
1606
+ aggregationType: MetricsAggregationType.Avg,
1607
+ groupByAttributeKeys: ["host.name"],
1608
+ },
1539
1609
  }),
1540
- createHostMetricChartComponent({
1610
+ createChartComponent({
1541
1611
  title: "Memory Usage by Host",
1542
- metricKind: HostMetricKind.MemoryUsage,
1612
+ chartType: DashboardChartType.Line,
1543
1613
  top: 2,
1544
1614
  left: 6,
1545
1615
  width: 6,
1546
1616
  height: 3,
1617
+ metricConfig: {
1618
+ metricName: MetricDashboardMetricType.SystemMemoryUsage,
1619
+ aggregationType: MetricsAggregationType.Avg,
1620
+ groupByAttributeKeys: ["host.name"],
1621
+ },
1547
1622
  }),
1548
1623
 
1549
1624
  // Row 5: Section header
@@ -1600,13 +1675,18 @@ function createHostDashboardConfig(): DashboardViewConfig {
1600
1675
  aggregationType: MetricsAggregationType.Avg,
1601
1676
  },
1602
1677
  }),
1603
- createHostMetricChartComponent({
1678
+ createChartComponent({
1604
1679
  title: "Filesystem Usage by Host",
1605
- metricKind: HostMetricKind.Filesystem,
1680
+ chartType: DashboardChartType.Line,
1606
1681
  top: 11,
1607
1682
  left: 4,
1608
1683
  width: 8,
1609
1684
  height: 3,
1685
+ metricConfig: {
1686
+ metricName: "system.filesystem.usage",
1687
+ aggregationType: MetricsAggregationType.Avg,
1688
+ groupByAttributeKeys: ["host.name"],
1689
+ },
1610
1690
  }),
1611
1691
 
1612
1692
  // Row 14: Section header
@@ -1620,21 +1700,33 @@ function createHostDashboardConfig(): DashboardViewConfig {
1620
1700
  }),
1621
1701
 
1622
1702
  // Row 15-17: Disk and network I/O rates per host
1623
- createHostMetricChartComponent({
1703
+ createChartComponent({
1624
1704
  title: "Disk I/O by Host",
1625
- metricKind: HostMetricKind.DiskIo,
1705
+ chartType: DashboardChartType.Line,
1626
1706
  top: 15,
1627
1707
  left: 0,
1628
1708
  width: 6,
1629
1709
  height: 3,
1710
+ metricConfig: {
1711
+ metricName: MetricDashboardMetricType.SystemDiskIo,
1712
+ aggregationType: MetricsAggregationType.Sum,
1713
+ groupByAttributeKeys: ["host.name"],
1714
+ transformAsRate: true,
1715
+ },
1630
1716
  }),
1631
- createHostMetricChartComponent({
1717
+ createChartComponent({
1632
1718
  title: "Network I/O by Host",
1633
- metricKind: HostMetricKind.NetworkIo,
1719
+ chartType: DashboardChartType.Line,
1634
1720
  top: 15,
1635
1721
  left: 6,
1636
1722
  width: 6,
1637
1723
  height: 3,
1724
+ metricConfig: {
1725
+ metricName: MetricDashboardMetricType.SystemNetworkIo,
1726
+ aggregationType: MetricsAggregationType.Sum,
1727
+ groupByAttributeKeys: ["host.name"],
1728
+ transformAsRate: true,
1729
+ },
1638
1730
  }),
1639
1731
 
1640
1732
  // Row 18: Section header
@@ -1648,13 +1740,18 @@ function createHostDashboardConfig(): DashboardViewConfig {
1648
1740
  }),
1649
1741
 
1650
1742
  // Row 19-21: Process count chart and recent logs
1651
- createHostMetricChartComponent({
1743
+ createChartComponent({
1652
1744
  title: "Process Count by Host",
1653
- metricKind: HostMetricKind.ProcessCount,
1745
+ chartType: DashboardChartType.Line,
1654
1746
  top: 19,
1655
1747
  left: 0,
1656
1748
  width: 6,
1657
1749
  height: 3,
1750
+ metricConfig: {
1751
+ metricName: "system.processes.count",
1752
+ aggregationType: MetricsAggregationType.Avg,
1753
+ groupByAttributeKeys: ["host.name"],
1754
+ },
1658
1755
  }),
1659
1756
  createLogStreamComponent({
1660
1757
  title: "Recent Logs",
@@ -1665,9 +1762,25 @@ function createHostDashboardConfig(): DashboardViewConfig {
1665
1762
  }),
1666
1763
  ];
1667
1764
 
1765
+ /*
1766
+ * Per-host scoping: the variable defaults to multi-select because the
1767
+ * dashboard's by-host charts already split rendering per host; the
1768
+ * selector lets users narrow to a subset (e.g. just two prod hosts)
1769
+ * without losing the per-host breakdown.
1770
+ */
1771
+ const variables: Array<DashboardVariable> = [
1772
+ createTelemetryAttributeVariable({
1773
+ name: "host",
1774
+ label: "Host",
1775
+ attributeKey: "resource.host.name",
1776
+ isMultiSelect: true,
1777
+ }),
1778
+ ];
1779
+
1668
1780
  return {
1669
1781
  _type: ObjectType.DashboardViewConfig,
1670
1782
  components,
1783
+ variables,
1671
1784
  heightInDashboardUnits: Math.max(DashboardSize.heightInDashboardUnits, 22),
1672
1785
  };
1673
1786
  }
@@ -83,7 +83,7 @@ export class MonitorTypeHelper {
83
83
  },
84
84
  {
85
85
  label: "Infrastructure",
86
- monitorTypes: [MonitorType.Server, MonitorType.SNMP],
86
+ monitorTypes: [MonitorType.SNMP],
87
87
  },
88
88
  {
89
89
  label: "Kubernetes",
@@ -27,22 +27,49 @@ export const OVERRIDE_META_KEY: string = "_override";
27
27
 
28
28
  export default class UserOverrideUtil {
29
29
  /**
30
- * Returns true when this override should be applied on top of the schedule
31
- * events. Global overrides always apply; policy-scoped overrides apply to
32
- * any schedule, since a schedule's calendar shows coverage information for
33
- * every user who could potentially be paged through it.
30
+ * Decides whether an override should affect resolution for a given policy
31
+ * context. Global overrides (no policy id) always apply. Policy-scoped
32
+ * overrides only apply when their policy matches the caller's policy.
33
+ * When the caller has no policy context, only global overrides apply.
34
34
  */
35
- public static isOverrideApplicable(_override: UserOverrideRecord): boolean {
36
- return true;
35
+ public static isOverrideApplicable(
36
+ override: UserOverrideRecord,
37
+ currentOnCallDutyPolicyId?: string | null | undefined,
38
+ ): boolean {
39
+ if (!override.onCallDutyPolicyId) {
40
+ return true;
41
+ }
42
+
43
+ if (!currentOnCallDutyPolicyId) {
44
+ return false;
45
+ }
46
+
47
+ return override.onCallDutyPolicyId === currentOnCallDutyPolicyId;
37
48
  }
38
49
 
39
50
  public static applyOverridesToEvents(data: {
40
51
  events: Array<CalendarEvent>;
41
52
  overrides: Array<UserOverrideRecord>;
53
+ currentOnCallDutyPolicyId?: string | null | undefined;
42
54
  }): Array<CalendarEvent> {
43
- const applicable: Array<UserOverrideRecord> = data.overrides.filter(
44
- UserOverrideUtil.isOverrideApplicable,
45
- );
55
+ const applicable: Array<UserOverrideRecord> = data.overrides
56
+ .filter((o: UserOverrideRecord) => {
57
+ return UserOverrideUtil.isOverrideApplicable(
58
+ o,
59
+ data.currentOnCallDutyPolicyId,
60
+ );
61
+ })
62
+ /*
63
+ * Apply policy-specific overrides before globals so that when both
64
+ * target the same user/window, the policy-specific substitution wins.
65
+ * splitEventByOverride only matches on the original user id, so the
66
+ * first override that consumes a segment claims it.
67
+ */
68
+ .sort((a: UserOverrideRecord, b: UserOverrideRecord) => {
69
+ const aPolicyScoped: number = a.onCallDutyPolicyId ? 0 : 1;
70
+ const bPolicyScoped: number = b.onCallDutyPolicyId ? 0 : 1;
71
+ return aPolicyScoped - bPolicyScoped;
72
+ });
46
73
 
47
74
  if (applicable.length === 0) {
48
75
  return data.events;
@@ -1,5 +1,9 @@
1
- import DashboardTableComponent from "../../../Types/Dashboard/DashboardComponents/DashboardTableComponent";
1
+ import DashboardTableComponent, {
2
+ TableColumnKind,
3
+ TableReduce,
4
+ } from "../../../Types/Dashboard/DashboardComponents/DashboardTableComponent";
2
5
  import { ObjectType } from "../../../Types/JSON";
6
+ import MetricsAggregationType from "../../../Types/Metrics/MetricsAggregationType";
3
7
  import ObjectID from "../../../Types/ObjectID";
4
8
  import DashboardBaseComponentUtil from "./DashboardBaseComponent";
5
9
  import {
@@ -9,15 +13,16 @@ import {
9
13
  } from "../../../Types/Dashboard/DashboardComponents/ComponentArgument";
10
14
  import DashboardComponentType from "../../../Types/Dashboard/DashboardComponentType";
11
15
 
12
- const DataSourceSection: ComponentArgumentSection = {
13
- name: "Data Source",
14
- description: "Configure which metrics to display in the table",
16
+ const DataSection: ComponentArgumentSection = {
17
+ name: "Data",
18
+ description:
19
+ "Pick how rows are grouped, then add columns for metrics and formulas",
15
20
  order: 1,
16
21
  };
17
22
 
18
23
  const DisplaySection: ComponentArgumentSection = {
19
24
  name: "Display Options",
20
- description: "Customize the table appearance",
25
+ description: "Customize how the table renders",
21
26
  order: 2,
22
27
  defaultCollapsed: true,
23
28
  };
@@ -35,13 +40,22 @@ export default class DashboardTableComponentUtil extends DashboardBaseComponentU
35
40
  minHeightInDashboardUnits: 3,
36
41
  minWidthInDashboardUnits: 4,
37
42
  arguments: {
38
- metricQueryConfig: {
39
- metricQueryData: {
40
- filterData: {},
41
- groupBy: undefined,
43
+ groupByAttributes: [],
44
+ columns: [
45
+ {
46
+ id: ObjectID.generate().toString(),
47
+ variable: "a",
48
+ header: "Value",
49
+ kind: TableColumnKind.Metric,
50
+ showAsColumn: true,
51
+ metricName: undefined,
52
+ aggregation: MetricsAggregationType.Avg,
53
+ decimals: 2,
42
54
  },
43
- },
44
- maxRows: 20,
55
+ ],
56
+ maxRows: 25,
57
+ reduce: TableReduce.Last,
58
+ decimals: 2,
45
59
  },
46
60
  };
47
61
  }
@@ -54,12 +68,23 @@ export default class DashboardTableComponentUtil extends DashboardBaseComponentU
54
68
  > = [];
55
69
 
56
70
  componentArguments.push({
57
- name: "Metric Query",
58
- description: "Select the metrics to display in the table",
71
+ name: "Group By Attributes",
72
+ description:
73
+ "Each unique combination of these attribute values becomes one row. Customize the column header for each picked attribute below. Leave empty for a time-bucketed table.",
74
+ required: false,
75
+ type: ComponentInputType.TableGroupBy,
76
+ id: "groupByAttributes",
77
+ section: DataSection,
78
+ });
79
+
80
+ componentArguments.push({
81
+ name: "Metrics & Columns",
82
+ description:
83
+ "Define metrics (data sources) and formulas. Each is auto-assigned a variable letter (a, b, c…) — formulas reference these. Toggle 'Show as column' off for metrics that should only feed formulas without appearing in the table.",
59
84
  required: true,
60
- type: ComponentInputType.MetricsQueryConfig,
61
- id: "metricQueryConfig",
62
- section: DataSourceSection,
85
+ type: ComponentInputType.TableColumns,
86
+ id: "columns",
87
+ section: DataSection,
63
88
  });
64
89
 
65
90
  componentArguments.push({
@@ -71,13 +96,51 @@ export default class DashboardTableComponentUtil extends DashboardBaseComponentU
71
96
  section: DisplaySection,
72
97
  });
73
98
 
99
+ componentArguments.push({
100
+ name: "Description",
101
+ description: "Subtitle shown below the title",
102
+ required: false,
103
+ type: ComponentInputType.LongText,
104
+ id: "tableDescription",
105
+ section: DisplaySection,
106
+ });
107
+
108
+ componentArguments.push({
109
+ name: "Reduce across time",
110
+ description:
111
+ "When Group By is set, how to collapse the time series into one value per row",
112
+ required: false,
113
+ type: ComponentInputType.Dropdown,
114
+ id: "reduce",
115
+ placeholder: TableReduce.Last,
116
+ section: DisplaySection,
117
+ dropdownOptions: [
118
+ { label: "Last value", value: TableReduce.Last },
119
+ { label: "Average", value: TableReduce.Avg },
120
+ { label: "Sum", value: TableReduce.Sum },
121
+ { label: "Min", value: TableReduce.Min },
122
+ { label: "Max", value: TableReduce.Max },
123
+ ],
124
+ });
125
+
126
+ componentArguments.push({
127
+ name: "Decimals",
128
+ description:
129
+ "Default decimals for value columns (overridable per column)",
130
+ required: false,
131
+ type: ComponentInputType.Number,
132
+ id: "decimals",
133
+ placeholder: "2",
134
+ section: DisplaySection,
135
+ });
136
+
74
137
  componentArguments.push({
75
138
  name: "Max Rows",
76
139
  description: "Maximum number of rows to show",
77
140
  required: false,
78
141
  type: ComponentInputType.Number,
79
142
  id: "maxRows",
80
- placeholder: "20",
143
+ placeholder: "25",
81
144
  section: DisplaySection,
82
145
  });
83
146
 
@@ -11,7 +11,6 @@ import DashboardDockerNetworkListComponentUtil from "./DashboardDockerNetworkLis
11
11
  import DashboardDockerVolumeListComponentUtil from "./DashboardDockerVolumeListComponent";
12
12
  import DashboardGaugeComponentUtil from "./DashboardGaugeComponent";
13
13
  import DashboardHostListComponentUtil from "./DashboardHostListComponent";
14
- import DashboardHostMetricChartComponentUtil from "./DashboardHostMetricChartComponent";
15
14
  import DashboardIncidentListComponentUtil from "./DashboardIncidentListComponent";
16
15
  import DashboardKubernetesCronJobListComponentUtil from "./DashboardKubernetesCronJobListComponent";
17
16
  import DashboardKubernetesDaemonSetListComponentUtil from "./DashboardKubernetesDaemonSetListComponent";
@@ -187,12 +186,6 @@ export default class DashboardComponentsUtil {
187
186
  >;
188
187
  }
189
188
 
190
- if (dashboardComponentType === DashboardComponentType.HostMetricChart) {
191
- return DashboardHostMetricChartComponentUtil.getComponentConfigArguments() as Array<
192
- ComponentArgument<DashboardBaseComponent>
193
- >;
194
- }
195
-
196
189
  throw new BadDataException(
197
190
  `Unknown dashboard component type: ${dashboardComponentType}`,
198
191
  );