@oneuptime/common 10.0.65 → 10.0.66

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 (210) hide show
  1. package/Models/DatabaseModels/DockerHostOwnerTeam.ts +464 -0
  2. package/Models/DatabaseModels/DockerHostOwnerUser.ts +463 -0
  3. package/Models/DatabaseModels/Index.ts +24 -0
  4. package/Models/DatabaseModels/KubernetesClusterOwnerTeam.ts +464 -0
  5. package/Models/DatabaseModels/KubernetesClusterOwnerUser.ts +463 -0
  6. package/Models/DatabaseModels/KubernetesResource.ts +548 -0
  7. package/Models/DatabaseModels/MetricPipelineRule.ts +804 -0
  8. package/Models/DatabaseModels/MetricRecordingRule.ts +470 -0
  9. package/Models/DatabaseModels/Monitor.ts +2 -0
  10. package/Models/DatabaseModels/Project.ts +53 -0
  11. package/Models/DatabaseModels/Service.ts +79 -0
  12. package/Models/DatabaseModels/TraceDropFilter.ts +508 -0
  13. package/Models/DatabaseModels/TracePipeline.ts +436 -0
  14. package/Models/DatabaseModels/TracePipelineProcessor.ts +454 -0
  15. package/Models/DatabaseModels/TraceRecordingRule.ts +470 -0
  16. package/Models/DatabaseModels/TraceScrubRule.ts +546 -0
  17. package/Server/API/KubernetesResourceAPI.ts +129 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.ts +399 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.ts +205 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.ts +335 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.ts +29 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.ts +53 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -1
  24. package/Server/Services/DockerHostOwnerTeamService.ts +10 -0
  25. package/Server/Services/DockerHostOwnerUserService.ts +10 -0
  26. package/Server/Services/KubernetesClusterOwnerTeamService.ts +10 -0
  27. package/Server/Services/KubernetesClusterOwnerUserService.ts +10 -0
  28. package/Server/Services/KubernetesResourceService.ts +351 -0
  29. package/Server/Services/MetricPipelineRuleService.ts +10 -0
  30. package/Server/Services/MetricRecordingRuleService.ts +10 -0
  31. package/Server/Services/TraceDropFilterService.ts +10 -0
  32. package/Server/Services/TracePipelineProcessorService.ts +10 -0
  33. package/Server/Services/TracePipelineService.ts +10 -0
  34. package/Server/Services/TraceRecordingRuleService.ts +10 -0
  35. package/Server/Services/TraceScrubRuleService.ts +10 -0
  36. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +71 -9
  37. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +483 -75
  38. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +379 -6
  39. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +502 -0
  40. package/Tests/Utils/MetricUnitUtil.test.ts +216 -0
  41. package/Tests/Utils/Metrics/MetricFormulaEvaluator.test.ts +269 -0
  42. package/Tests/Utils/Metrics/MetricResultUnitConverter.test.ts +231 -0
  43. package/Tests/Utils/RecordingRuleExpression.test.ts +177 -0
  44. package/Types/Kubernetes/KubernetesInventoryExtractor.ts +327 -0
  45. package/Types/Kubernetes/KubernetesObjectParser.ts +1949 -0
  46. package/Types/Metrics/MetricDownsamplingRetentionDays.ts +49 -0
  47. package/Types/Metrics/MetricFormulaConfigData.ts +4 -0
  48. package/Types/Metrics/MetricPipelineRuleFilterCondition.ts +136 -0
  49. package/Types/Metrics/MetricPipelineRuleType.ts +27 -0
  50. package/Types/Metrics/RecordingRuleDefinition.ts +180 -0
  51. package/Types/Monitor/CriteriaFilter.ts +43 -0
  52. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +70 -0
  53. package/Types/Permission.ts +520 -0
  54. package/Types/Trace/TraceAggregationType.ts +17 -0
  55. package/Types/Trace/TraceDropFilterAction.ts +6 -0
  56. package/Types/Trace/TracePipelineProcessorType.ts +56 -0
  57. package/Types/Trace/TraceRecordingRuleDefinition.ts +218 -0
  58. package/Types/Trace/TraceScrubAction.ts +7 -0
  59. package/Types/Trace/TraceScrubField.ts +8 -0
  60. package/Types/Trace/TraceScrubPatternType.ts +10 -0
  61. package/UI/Components/CardSelect/CardSelect.tsx +9 -1
  62. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +6 -10
  63. package/UI/Components/Forms/Fields/FormField.tsx +1 -0
  64. package/UI/Components/Forms/Types/Field.ts +1 -0
  65. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +57 -0
  66. package/UI/Components/Page/Page.tsx +6 -0
  67. package/Utils/MetricUnitUtil.ts +289 -0
  68. package/Utils/Metrics/MetricFormulaEvaluator.ts +610 -0
  69. package/Utils/Metrics/MetricResultUnitConverter.ts +91 -0
  70. package/Utils/Metrics/RecordingRuleExpression.ts +359 -0
  71. package/Utils/ValueFormatter.ts +137 -13
  72. package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js +480 -0
  73. package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js.map +1 -0
  74. package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js +479 -0
  75. package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js.map +1 -0
  76. package/build/dist/Models/DatabaseModels/Index.js +24 -0
  77. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  78. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js +480 -0
  79. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js.map +1 -0
  80. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js +479 -0
  81. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js.map +1 -0
  82. package/build/dist/Models/DatabaseModels/KubernetesResource.js +590 -0
  83. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -0
  84. package/build/dist/Models/DatabaseModels/MetricPipelineRule.js +836 -0
  85. package/build/dist/Models/DatabaseModels/MetricPipelineRule.js.map +1 -0
  86. package/build/dist/Models/DatabaseModels/MetricRecordingRule.js +497 -0
  87. package/build/dist/Models/DatabaseModels/MetricRecordingRule.js.map +1 -0
  88. package/build/dist/Models/DatabaseModels/Monitor.js +2 -0
  89. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  90. package/build/dist/Models/DatabaseModels/Project.js +53 -0
  91. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  92. package/build/dist/Models/DatabaseModels/Service.js +79 -0
  93. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  94. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +536 -0
  95. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -0
  96. package/build/dist/Models/DatabaseModels/TracePipeline.js +462 -0
  97. package/build/dist/Models/DatabaseModels/TracePipeline.js.map +1 -0
  98. package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js +476 -0
  99. package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js.map +1 -0
  100. package/build/dist/Models/DatabaseModels/TraceRecordingRule.js +497 -0
  101. package/build/dist/Models/DatabaseModels/TraceRecordingRule.js.map +1 -0
  102. package/build/dist/Models/DatabaseModels/TraceScrubRule.js +575 -0
  103. package/build/dist/Models/DatabaseModels/TraceScrubRule.js.map +1 -0
  104. package/build/dist/Server/API/KubernetesResourceAPI.js +98 -0
  105. package/build/dist/Server/API/KubernetesResourceAPI.js.map +1 -0
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js +144 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js.map +1 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js +82 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js.map +1 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js +118 -0
  111. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js.map +1 -0
  112. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js +16 -0
  113. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js.map +1 -0
  114. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js +24 -0
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js.map +1 -0
  116. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  117. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  118. package/build/dist/Server/Services/DockerHostOwnerTeamService.js +9 -0
  119. package/build/dist/Server/Services/DockerHostOwnerTeamService.js.map +1 -0
  120. package/build/dist/Server/Services/DockerHostOwnerUserService.js +9 -0
  121. package/build/dist/Server/Services/DockerHostOwnerUserService.js.map +1 -0
  122. package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js +9 -0
  123. package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js.map +1 -0
  124. package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js +9 -0
  125. package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js.map +1 -0
  126. package/build/dist/Server/Services/KubernetesResourceService.js +237 -0
  127. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -0
  128. package/build/dist/Server/Services/MetricPipelineRuleService.js +9 -0
  129. package/build/dist/Server/Services/MetricPipelineRuleService.js.map +1 -0
  130. package/build/dist/Server/Services/MetricRecordingRuleService.js +9 -0
  131. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -0
  132. package/build/dist/Server/Services/TraceDropFilterService.js +9 -0
  133. package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -0
  134. package/build/dist/Server/Services/TracePipelineProcessorService.js +9 -0
  135. package/build/dist/Server/Services/TracePipelineProcessorService.js.map +1 -0
  136. package/build/dist/Server/Services/TracePipelineService.js +9 -0
  137. package/build/dist/Server/Services/TracePipelineService.js.map +1 -0
  138. package/build/dist/Server/Services/TraceRecordingRuleService.js +9 -0
  139. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -0
  140. package/build/dist/Server/Services/TraceScrubRuleService.js +9 -0
  141. package/build/dist/Server/Services/TraceScrubRuleService.js.map +1 -0
  142. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +56 -9
  143. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  144. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +335 -53
  145. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  146. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +277 -5
  147. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  148. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +407 -0
  149. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -0
  150. package/build/dist/Tests/Utils/MetricUnitUtil.test.js +159 -0
  151. package/build/dist/Tests/Utils/MetricUnitUtil.test.js.map +1 -0
  152. package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js +224 -0
  153. package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js.map +1 -0
  154. package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js +180 -0
  155. package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js.map +1 -0
  156. package/build/dist/Tests/Utils/RecordingRuleExpression.test.js +142 -0
  157. package/build/dist/Tests/Utils/RecordingRuleExpression.test.js.map +1 -0
  158. package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js +200 -0
  159. package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js.map +1 -0
  160. package/build/dist/Types/Kubernetes/KubernetesObjectParser.js +1205 -0
  161. package/build/dist/Types/Kubernetes/KubernetesObjectParser.js.map +1 -0
  162. package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js +32 -0
  163. package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js.map +1 -0
  164. package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js +103 -0
  165. package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js.map +1 -0
  166. package/build/dist/Types/Metrics/MetricPipelineRuleType.js +27 -0
  167. package/build/dist/Types/Metrics/MetricPipelineRuleType.js.map +1 -0
  168. package/build/dist/Types/Metrics/RecordingRuleDefinition.js +110 -0
  169. package/build/dist/Types/Metrics/RecordingRuleDefinition.js.map +1 -0
  170. package/build/dist/Types/Monitor/CriteriaFilter.js +22 -0
  171. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  172. package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js +2 -0
  173. package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js.map +1 -0
  174. package/build/dist/Types/Permission.js +454 -0
  175. package/build/dist/Types/Permission.js.map +1 -1
  176. package/build/dist/Types/Trace/TraceAggregationType.js +18 -0
  177. package/build/dist/Types/Trace/TraceAggregationType.js.map +1 -0
  178. package/build/dist/Types/Trace/TraceDropFilterAction.js +7 -0
  179. package/build/dist/Types/Trace/TraceDropFilterAction.js.map +1 -0
  180. package/build/dist/Types/Trace/TracePipelineProcessorType.js +10 -0
  181. package/build/dist/Types/Trace/TracePipelineProcessorType.js.map +1 -0
  182. package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js +145 -0
  183. package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js.map +1 -0
  184. package/build/dist/Types/Trace/TraceScrubAction.js +8 -0
  185. package/build/dist/Types/Trace/TraceScrubAction.js.map +1 -0
  186. package/build/dist/Types/Trace/TraceScrubField.js +9 -0
  187. package/build/dist/Types/Trace/TraceScrubField.js.map +1 -0
  188. package/build/dist/Types/Trace/TraceScrubPatternType.js +11 -0
  189. package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -0
  190. package/build/dist/UI/Components/CardSelect/CardSelect.js +3 -1
  191. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  192. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -9
  193. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  194. package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
  195. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  196. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +30 -0
  197. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  198. package/build/dist/UI/Components/Page/Page.js +1 -0
  199. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  200. package/build/dist/Utils/MetricUnitUtil.js +232 -0
  201. package/build/dist/Utils/MetricUnitUtil.js.map +1 -0
  202. package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js +453 -0
  203. package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js.map +1 -0
  204. package/build/dist/Utils/Metrics/MetricResultUnitConverter.js +61 -0
  205. package/build/dist/Utils/Metrics/MetricResultUnitConverter.js.map +1 -0
  206. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +298 -0
  207. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -0
  208. package/build/dist/Utils/ValueFormatter.js +123 -13
  209. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  210. package/package.json +1 -1
@@ -2,16 +2,26 @@ import AggregateModel from "../../../../Types/BaseDatabase/AggregatedModel";
2
2
  import AggregatedResult from "../../../../Types/BaseDatabase/AggregatedResult";
3
3
  import MetricFormulaConfigData from "../../../../Types/Metrics/MetricFormulaConfigData";
4
4
  import MetricQueryConfigData from "../../../../Types/Metrics/MetricQueryConfigData";
5
+ import MetricsAggregationType from "../../../../Types/Metrics/MetricsAggregationType";
5
6
  import MetricMonitorResponse from "../../../../Types/Monitor/MetricMonitor/MetricMonitorResponse";
7
+ import MetricCriteriaContext, {
8
+ MetricComponent,
9
+ MetricComponentValue,
10
+ } from "../../../../Types/Monitor/MetricMonitor/MetricCriteriaContext";
6
11
  import MonitorStep from "../../../../Types/Monitor/MonitorStep";
12
+ import { JSONObject } from "../../../../Types/JSON";
7
13
  import DataToProcess from "../DataToProcess";
8
14
  import CompareCriteria from "./CompareCriteria";
9
15
  import {
10
16
  CheckOn,
11
17
  CriteriaFilter,
12
18
  EvaluateOverTimeType,
19
+ FilterType,
20
+ NoDataPolicy,
13
21
  } from "../../../../Types/Monitor/CriteriaFilter";
14
22
  import CaptureSpan from "../../Telemetry/CaptureSpan";
23
+ import MetricUnitUtil from "../../../../Utils/MetricUnitUtil";
24
+ import MetricFormulaEvaluator from "../../../../Utils/Metrics/MetricFormulaEvaluator";
15
25
 
16
26
  export default class MetricMonitorCriteria {
17
27
  @CaptureSpan()
@@ -30,100 +40,498 @@ export default class MetricMonitorCriteria {
30
40
  EvaluateOverTimeType.AnyValue;
31
41
  }
32
42
 
33
- let threshold: number | string | undefined | null =
34
- input.criteriaFilter.value;
43
+ if (input.criteriaFilter.checkOn !== CheckOn.MetricValue) {
44
+ return null;
45
+ }
46
+
47
+ const rawThreshold: number | null = CompareCriteria.convertToNumber(
48
+ input.criteriaFilter.value,
49
+ );
50
+
51
+ const metricAlias: string =
52
+ input.criteriaFilter.metricMonitorOptions?.metricAlias || "";
35
53
 
36
- if (input.criteriaFilter.checkOn === CheckOn.MetricValue) {
37
- threshold = CompareCriteria.convertToNumber(threshold);
54
+ const metricResponse: MetricMonitorResponse =
55
+ input.dataToProcess as MetricMonitorResponse;
56
+ const metricAggregatedResult: Array<AggregatedResult> =
57
+ metricResponse.metricResult || [];
38
58
 
39
- const metricAggregaredResult: Array<AggregatedResult> =
40
- (input.dataToProcess as MetricMonitorResponse).metricResult || [];
59
+ const queryConfigs: Array<MetricQueryConfigData> =
60
+ input.monitorStep.data?.metricMonitor?.metricViewConfig?.queryConfigs ||
61
+ [];
62
+ const formulaConfigs: Array<MetricFormulaConfigData> =
63
+ input.monitorStep.data?.metricMonitor?.metricViewConfig?.formulaConfigs ||
64
+ [];
41
65
 
42
- const metricAlias: string =
43
- input.criteriaFilter.metricMonitorOptions?.metricAlias || "";
66
+ /*
67
+ * Resolve which query/formula the alias refers to. Use explicit index
68
+ * checks (not `findIndex() || -1`, which incorrectly falls back to -1
69
+ * when the first element matches).
70
+ */
71
+ let matchedQuery: MetricQueryConfigData | null = null;
72
+ let matchedFormula: MetricFormulaConfigData | null = null;
73
+ let aliasIndex: number = -1;
44
74
 
45
- // Pick based on the alias, or if there's no alias, pick the first one
75
+ if (metricAlias) {
76
+ const qIdx: number = queryConfigs.findIndex(
77
+ (q: MetricQueryConfigData) => {
78
+ return q.metricAliasData?.metricVariable === metricAlias;
79
+ },
80
+ );
46
81
 
47
- let aliasIndex: number =
48
- input.monitorStep.data?.metricMonitor?.metricViewConfig?.queryConfigs.findIndex(
49
- (queryConfig: MetricQueryConfigData) => {
50
- return queryConfig.metricAliasData?.metricVariable === metricAlias;
82
+ if (qIdx >= 0) {
83
+ matchedQuery = queryConfigs[qIdx] || null;
84
+ aliasIndex = qIdx;
85
+ } else {
86
+ const fIdx: number = formulaConfigs.findIndex(
87
+ (f: MetricFormulaConfigData) => {
88
+ return f.metricAliasData?.metricVariable === metricAlias;
51
89
  },
52
- ) || -1;
53
-
54
- if (aliasIndex < 0) {
55
- // then try to find in formula
56
- let formulaIndex: number =
57
- input.monitorStep.data?.metricMonitor?.metricViewConfig?.formulaConfigs.findIndex(
58
- (formulaConfig: MetricFormulaConfigData) => {
59
- return (
60
- formulaConfig.metricAliasData?.metricVariable === metricAlias
61
- );
62
- },
63
- ) || -1;
64
-
65
- if (formulaIndex >= 0) {
66
- // add number of queries to the index
67
- formulaIndex =
68
- formulaIndex +
69
- (input.monitorStep.data?.metricMonitor?.metricViewConfig
70
- ?.queryConfigs.length || 0);
71
- aliasIndex = formulaIndex;
90
+ );
91
+
92
+ if (fIdx >= 0) {
93
+ matchedFormula = formulaConfigs[fIdx] || null;
94
+ aliasIndex = queryConfigs.length + fIdx;
72
95
  }
73
96
  }
74
- const aggregatedResult: AggregatedResult | undefined =
75
- metricAggregaredResult &&
76
- metricAggregaredResult.length >= aliasIndex - 1 &&
77
- aliasIndex >= 0
78
- ? metricAggregaredResult[aliasIndex]
79
- : metricAggregaredResult[0] || undefined;
80
-
81
- if (metricAlias) {
82
- // find the index of the alias in the dataToProcess.
83
- const indexOfAlias: number = (
84
- input.dataToProcess as MetricMonitorResponse
85
- ).metricViewConfig.queryConfigs.findIndex(
86
- (queryConfig: MetricQueryConfigData) => {
87
- return queryConfig.metricAliasData?.metricVariable === metricAlias;
88
- },
89
- );
97
+ }
90
98
 
91
- // now get the aggregated result for that alias
92
- if (indexOfAlias !== -1) {
93
- const aggregatedResultForAlias: AggregatedResult | undefined =
94
- metricAggregaredResult[indexOfAlias];
95
- if (aggregatedResultForAlias) {
96
- const numbers: Array<number> = aggregatedResultForAlias.data.map(
97
- (data: AggregateModel) => {
98
- return data.value;
99
- },
100
- );
99
+ /*
100
+ * If no alias was configured or it didn't match anything, fall back to
101
+ * the first aggregated result / query for a best-effort comparison.
102
+ */
103
+ const aggregatedResult: AggregatedResult | undefined =
104
+ aliasIndex >= 0
105
+ ? metricAggregatedResult[aliasIndex]
106
+ : metricAggregatedResult[0];
107
+
108
+ if (!matchedQuery && !matchedFormula && queryConfigs[0]) {
109
+ matchedQuery = queryConfigs[0];
110
+ }
111
+
112
+ /*
113
+ * Build the metric context regardless of whether the threshold breaches,
114
+ * so the filter message can still reference the metric if needed.
115
+ */
116
+ const metricContext: MetricCriteriaContext =
117
+ MetricMonitorCriteria.buildContext({
118
+ matchedQuery,
119
+ matchedFormula,
120
+ metricAlias,
121
+ criteriaFilter: input.criteriaFilter,
122
+ queryConfigs,
123
+ formulaConfigs,
124
+ });
125
+
126
+ input.criteriaFilter.metricCriteriaContext = metricContext;
127
+
128
+ if (rawThreshold === null) {
129
+ return null;
130
+ }
131
+
132
+ /*
133
+ * Sample values arrive already converted into the query's configured
134
+ * display unit (legendUnit) — the upstream fetch step uses
135
+ * MetricResultUnitConverter to normalize OpenTelemetry's native unit
136
+ * into whatever the user picked. So `sampleUnit` here is that
137
+ * legendUnit, and `thresholdUnit` is what the user typed into the
138
+ * criteria threshold. When they differ we convert sample values from
139
+ * legendUnit → thresholdUnit before comparing, so the alert message
140
+ * reads in the unit the user actually chose for the threshold.
141
+ */
142
+ const sampleUnit: string | undefined = metricContext.unit || undefined;
143
+ const thresholdUnit: string | undefined =
144
+ input.criteriaFilter.metricMonitorOptions?.thresholdUnit || sampleUnit;
145
+
146
+ const displayUnit: string | undefined = thresholdUnit;
147
+
148
+ /*
149
+ * Threshold is entered in thresholdUnit; keep the numeric value as-is
150
+ * for comparison in that same unit.
151
+ */
152
+ const threshold: number = rawThreshold;
153
+
154
+ const convertToDisplayUnit: (value: number) => number = (
155
+ value: number,
156
+ ): number => {
157
+ if (!sampleUnit || !displayUnit || sampleUnit === displayUnit) {
158
+ return value;
159
+ }
160
+ return MetricUnitUtil.convertToMetricUnit({
161
+ value,
162
+ fromUnit: sampleUnit,
163
+ metricUnit: displayUnit,
164
+ });
165
+ };
166
+
167
+ metricContext.unit = displayUnit || null;
168
+
169
+ const samples: Array<AggregateModel> =
170
+ (aggregatedResult && aggregatedResult.data) || [];
171
+
172
+ /*
173
+ * Respect the configured no-data policy. Without this guard, the
174
+ * evaluator silently treats missing data as a value of 0 and can
175
+ * trigger incidents for monitors that simply haven't received data.
176
+ */
177
+ if (samples.length === 0) {
178
+ const policy: NoDataPolicy =
179
+ input.criteriaFilter.metricMonitorOptions?.onNoDataPolicy ||
180
+ NoDataPolicy.Ignore;
101
181
 
102
- return CompareCriteria.compareCriteriaNumbers({
103
- value: numbers && numbers.length > 0 ? numbers : 0,
104
- threshold: threshold as number,
105
- criteriaFilter: input.criteriaFilter,
106
- });
107
- }
182
+ if (policy === NoDataPolicy.Ignore) {
183
+ return null;
184
+ }
185
+
186
+ if (policy === NoDataPolicy.Trigger) {
187
+ return `No data received for ${metricContext.metricName} in the evaluation window — triggering per no-data policy.`;
188
+ }
189
+
190
+ // TreatAsZero: fall through to the comparator with value 0.
191
+ }
192
+
193
+ const numbersInDisplayUnit: Array<number> = samples.map(
194
+ (d: AggregateModel) => {
195
+ return convertToDisplayUnit(d.value);
196
+ },
197
+ );
198
+
199
+ const comparisonMessage: string | null =
200
+ CompareCriteria.compareCriteriaNumbers({
201
+ value: numbersInDisplayUnit.length > 0 ? numbersInDisplayUnit : 0,
202
+ threshold: threshold,
203
+ criteriaFilter: input.criteriaFilter,
204
+ metricDisplayName: metricContext.metricName,
205
+ unit: displayUnit,
206
+ });
207
+
208
+ if (!comparisonMessage) {
209
+ return null;
210
+ }
211
+
212
+ /*
213
+ * Identify every sample that breached so the root cause can render a
214
+ * timestamp/value table instead of a giant one-line list, and keep a
215
+ * total count so we can say "N of M samples breached". Values are
216
+ * stored in the display unit so they match the comparison message.
217
+ */
218
+ const breachingSamples: Array<{
219
+ value: number;
220
+ timestamp: Date;
221
+ attributes: JSONObject;
222
+ componentValues?: Array<MetricComponentValue>;
223
+ }> = [];
224
+
225
+ /*
226
+ * For formulas, precompute an index from ISO-timestamp → value for
227
+ * every component series so we can show what `a` and `b` were at
228
+ * the moment the formula breached, without re-walking the arrays
229
+ * inside the per-sample loop.
230
+ */
231
+ const componentValueLookup: Map<string, Map<string, number>> | null =
232
+ matchedFormula
233
+ ? MetricMonitorCriteria.buildComponentValueLookup({
234
+ components: metricContext.components || [],
235
+ queryConfigs,
236
+ formulaConfigs,
237
+ metricAggregatedResult,
238
+ })
239
+ : null;
240
+
241
+ for (const sample of samples) {
242
+ const convertedValue: number = convertToDisplayUnit(sample.value);
243
+ const breaches: boolean = MetricMonitorCriteria.sampleBreaches(
244
+ convertedValue,
245
+ threshold,
246
+ input.criteriaFilter.filterType,
247
+ );
248
+ if (breaches) {
249
+ const entry: {
250
+ value: number;
251
+ timestamp: Date;
252
+ attributes: JSONObject;
253
+ componentValues?: Array<MetricComponentValue>;
254
+ } = {
255
+ value: convertedValue,
256
+ timestamp: sample.timestamp,
257
+ attributes: MetricMonitorCriteria.extractLabelAttributes(sample),
258
+ };
259
+
260
+ if (componentValueLookup && metricContext.components) {
261
+ entry.componentValues = MetricMonitorCriteria.resolveComponentValues({
262
+ timestamp: sample.timestamp,
263
+ components: metricContext.components,
264
+ lookup: componentValueLookup,
265
+ });
108
266
  }
267
+
268
+ breachingSamples.push(entry);
109
269
  }
270
+ }
271
+
272
+ metricContext.totalSamplesInWindow = samples.length;
273
+
274
+ if (breachingSamples.length > 0) {
275
+ metricContext.breachingSample = breachingSamples[0];
276
+ metricContext.breachingSamples = breachingSamples;
277
+ }
278
+
279
+ return comparisonMessage;
280
+ }
281
+
282
+ private static buildComponentValueLookup(input: {
283
+ components: Array<MetricComponent>;
284
+ queryConfigs: Array<MetricQueryConfigData>;
285
+ formulaConfigs: Array<MetricFormulaConfigData>;
286
+ metricAggregatedResult: Array<AggregatedResult>;
287
+ }): Map<string, Map<string, number>> {
288
+ // Map of alias -> (isoTimestamp -> value)
289
+ const lookup: Map<string, Map<string, number>> = new Map();
290
+
291
+ for (const component of input.components) {
292
+ const queryIdx: number = input.queryConfigs.findIndex(
293
+ (q: MetricQueryConfigData) => {
294
+ return (
295
+ (q.metricAliasData?.metricVariable || "").toLowerCase() ===
296
+ component.alias
297
+ );
298
+ },
299
+ );
110
300
 
111
- // if there's no alias then this is the default case
112
- if (aggregatedResult) {
113
- const numbers: Array<number> = aggregatedResult.data.map(
114
- (data: AggregateModel) => {
115
- return data.value;
301
+ let resultIndex: number = -1;
302
+ if (queryIdx >= 0) {
303
+ resultIndex = queryIdx;
304
+ } else {
305
+ const formulaIdx: number = input.formulaConfigs.findIndex(
306
+ (fc: MetricFormulaConfigData) => {
307
+ return (
308
+ (fc.metricAliasData?.metricVariable || "").toLowerCase() ===
309
+ component.alias
310
+ );
116
311
  },
117
312
  );
313
+ if (formulaIdx >= 0) {
314
+ resultIndex = input.queryConfigs.length + formulaIdx;
315
+ }
316
+ }
317
+
318
+ if (resultIndex < 0) {
319
+ continue;
320
+ }
321
+
322
+ const series: AggregatedResult | undefined =
323
+ input.metricAggregatedResult[resultIndex];
324
+ if (!series) {
325
+ continue;
326
+ }
327
+
328
+ const timestampMap: Map<string, number> = new Map();
329
+ for (const row of series.data) {
330
+ const iso: string = MetricMonitorCriteria.toIsoTimestamp(row.timestamp);
331
+ timestampMap.set(iso, row.value);
332
+ }
333
+ lookup.set(component.alias, timestampMap);
334
+ }
335
+
336
+ return lookup;
337
+ }
338
+
339
+ private static resolveComponentValues(input: {
340
+ timestamp: Date | string;
341
+ components: Array<MetricComponent>;
342
+ lookup: Map<string, Map<string, number>>;
343
+ }): Array<MetricComponentValue> {
344
+ const iso: string = MetricMonitorCriteria.toIsoTimestamp(input.timestamp);
345
+ return input.components.map((component: MetricComponent) => {
346
+ const series: Map<string, number> | undefined = input.lookup.get(
347
+ component.alias,
348
+ );
349
+ const value: number | undefined = series ? series.get(iso) : undefined;
350
+ return {
351
+ alias: component.alias,
352
+ value: typeof value === "number" ? value : null,
353
+ };
354
+ });
355
+ }
356
+
357
+ private static toIsoTimestamp(value: Date | string): string {
358
+ const date: Date = value instanceof Date ? value : new Date(value);
359
+ return isNaN(date.getTime()) ? String(value) : date.toISOString();
360
+ }
361
+
362
+ private static sampleBreaches(
363
+ value: number,
364
+ threshold: number,
365
+ filterType: FilterType | undefined,
366
+ ): boolean {
367
+ switch (filterType) {
368
+ case FilterType.GreaterThan:
369
+ return value > threshold;
370
+ case FilterType.GreaterThanOrEqualTo:
371
+ return value >= threshold;
372
+ case FilterType.LessThan:
373
+ return value < threshold;
374
+ case FilterType.LessThanOrEqualTo:
375
+ return value <= threshold;
376
+ case FilterType.EqualTo:
377
+ return value === threshold;
378
+ case FilterType.NotEqualTo:
379
+ return value !== threshold;
380
+ default:
381
+ return false;
382
+ }
383
+ }
384
+
385
+ private static extractLabelAttributes(sample: AggregateModel): JSONObject {
386
+ /*
387
+ * AggregatedModel has a string index signature that holds group-by
388
+ * attributes alongside `timestamp` and `value`. Strip the known keys
389
+ * to get the label dictionary.
390
+ */
391
+ const labels: JSONObject = {};
392
+ for (const key of Object.keys(sample)) {
393
+ if (key === "timestamp" || key === "value") {
394
+ continue;
395
+ }
396
+ const v: unknown = (sample as unknown as JSONObject)[key];
397
+ if (v === undefined || v === null) {
398
+ continue;
399
+ }
400
+ labels[key] = v as JSONObject[string];
401
+ }
402
+ return labels;
403
+ }
404
+
405
+ private static buildContext(input: {
406
+ matchedQuery: MetricQueryConfigData | null;
407
+ matchedFormula: MetricFormulaConfigData | null;
408
+ metricAlias: string;
409
+ criteriaFilter: CriteriaFilter;
410
+ queryConfigs: Array<MetricQueryConfigData>;
411
+ formulaConfigs: Array<MetricFormulaConfigData>;
412
+ }): MetricCriteriaContext {
413
+ const q: MetricQueryConfigData | null = input.matchedQuery;
414
+ const f: MetricFormulaConfigData | null = input.matchedFormula;
415
+
416
+ const metricName: string =
417
+ (q?.metricQueryData?.filterData?.metricName as string | undefined) ||
418
+ f?.metricFormulaData?.metricFormula ||
419
+ q?.metricAliasData?.title ||
420
+ f?.metricAliasData?.title ||
421
+ "Metric";
422
+
423
+ const unit: string | null =
424
+ (q?.metricAliasData?.legendUnit as string | undefined) ||
425
+ (f?.metricAliasData?.legendUnit as string | undefined) ||
426
+ null;
427
+
428
+ const aggregationType: MetricsAggregationType | null =
429
+ (q?.metricQueryData?.filterData?.aggegationType as
430
+ | MetricsAggregationType
431
+ | undefined) || null;
432
+
433
+ const filterAttributes: JSONObject =
434
+ (q?.metricQueryData?.filterData?.attributes as JSONObject | undefined) ||
435
+ {};
436
+
437
+ const groupBy: Array<string> = q?.metricQueryData?.groupBy
438
+ ? Object.keys(q.metricQueryData.groupBy as Record<string, unknown>)
439
+ : [];
440
+
441
+ const components: Array<MetricComponent> | undefined = f
442
+ ? MetricMonitorCriteria.buildFormulaComponents({
443
+ formulaConfig: f,
444
+ queryConfigs: input.queryConfigs,
445
+ formulaConfigs: input.formulaConfigs,
446
+ })
447
+ : undefined;
448
+
449
+ return {
450
+ metricName,
451
+ alias: input.metricAlias,
452
+ unit,
453
+ aggregationType,
454
+ isFormula: Boolean(f),
455
+ formulaExpression: f?.metricFormulaData?.metricFormula,
456
+ filterAttributes,
457
+ groupBy,
458
+ timeWindowMinutes:
459
+ input.criteriaFilter.evaluateOverTimeOptions?.timeValueInMinutes,
460
+ ...(components && components.length > 0 ? { components } : {}),
461
+ };
462
+ }
463
+
464
+ /**
465
+ * Resolve the variables the formula references to their source
466
+ * query/formula definitions so the root cause can label each
467
+ * component column with its metric name and native unit.
468
+ */
469
+ private static buildFormulaComponents(input: {
470
+ formulaConfig: MetricFormulaConfigData;
471
+ queryConfigs: Array<MetricQueryConfigData>;
472
+ formulaConfigs: Array<MetricFormulaConfigData>;
473
+ }): Array<MetricComponent> {
474
+ const formula: string =
475
+ input.formulaConfig.metricFormulaData?.metricFormula || "";
476
+ const referenced: Array<string> =
477
+ MetricFormulaEvaluator.getReferencedVariables(formula);
478
+
479
+ const components: Array<MetricComponent> = [];
480
+ const seen: Set<string> = new Set<string>();
481
+
482
+ for (const alias of referenced) {
483
+ const normalizedAlias: string = alias.toLowerCase();
484
+ if (seen.has(normalizedAlias)) {
485
+ continue;
486
+ }
487
+ seen.add(normalizedAlias);
488
+
489
+ const queryMatch: MetricQueryConfigData | undefined =
490
+ input.queryConfigs.find((q: MetricQueryConfigData) => {
491
+ return (
492
+ (q.metricAliasData?.metricVariable || "").toLowerCase() ===
493
+ normalizedAlias
494
+ );
495
+ });
496
+
497
+ if (queryMatch) {
498
+ const name: string =
499
+ (queryMatch.metricQueryData?.filterData?.metricName as
500
+ | string
501
+ | undefined) ||
502
+ queryMatch.metricAliasData?.title ||
503
+ normalizedAlias;
504
+ components.push({
505
+ alias: normalizedAlias,
506
+ name,
507
+ unit: queryMatch.metricAliasData?.legendUnit || null,
508
+ isFormula: false,
509
+ });
510
+ continue;
511
+ }
512
+
513
+ const formulaMatch: MetricFormulaConfigData | undefined =
514
+ input.formulaConfigs.find((fc: MetricFormulaConfigData) => {
515
+ return (
516
+ (fc.metricAliasData?.metricVariable || "").toLowerCase() ===
517
+ normalizedAlias
518
+ );
519
+ });
118
520
 
119
- return CompareCriteria.compareCriteriaNumbers({
120
- value: numbers && numbers.length > 0 ? numbers : 0,
121
- threshold: threshold as number,
122
- criteriaFilter: input.criteriaFilter,
521
+ if (formulaMatch) {
522
+ const name: string =
523
+ formulaMatch.metricAliasData?.title ||
524
+ formulaMatch.metricFormulaData?.metricFormula ||
525
+ normalizedAlias;
526
+ components.push({
527
+ alias: normalizedAlias,
528
+ name,
529
+ unit: formulaMatch.metricAliasData?.legendUnit || null,
530
+ isFormula: true,
123
531
  });
124
532
  }
125
533
  }
126
534
 
127
- return null;
535
+ return components;
128
536
  }
129
537
  }