@oneuptime/common 14.0.5 → 14.0.6

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 (66) hide show
  1. package/Server/Services/MetricService.ts +1 -1
  2. package/Server/Types/Database/QueryHelper.ts +54 -1
  3. package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
  4. package/Server/Utils/AI/Toolbox/ResourceTools.ts +12 -1
  5. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
  6. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
  7. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +4 -3
  8. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +3 -1
  9. package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
  10. package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
  11. package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
  12. package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
  13. package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
  14. package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
  15. package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
  16. package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
  17. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
  18. package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
  19. package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
  20. package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
  21. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
  22. package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
  23. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +96 -0
  24. package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
  25. package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
  26. package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
  27. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +8 -14
  28. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +4 -3
  29. package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +27 -6
  30. package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
  31. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
  32. package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
  33. package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
  34. package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
  35. package/Types/BaseDatabase/AggregationType.ts +7 -0
  36. package/Types/Dashboard/DashboardTemplates.ts +20 -7
  37. package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
  38. package/Types/Rum/WebVitals.ts +13 -0
  39. package/UI/Components/Input/Input.tsx +11 -0
  40. package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
  41. package/build/dist/Server/Services/MetricService.js +1 -1
  42. package/build/dist/Server/Types/Database/QueryHelper.js +47 -1
  43. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  44. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
  45. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
  46. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +12 -1
  47. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  48. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
  49. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
  50. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  51. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
  52. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  53. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1 -1
  54. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  55. package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
  56. package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
  57. package/build/dist/Types/Dashboard/DashboardTemplates.js +20 -7
  58. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  59. package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
  60. package/build/dist/Types/Rum/WebVitals.js +12 -0
  61. package/build/dist/Types/Rum/WebVitals.js.map +1 -1
  62. package/build/dist/UI/Components/Input/Input.js +1 -1
  63. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  64. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
  65. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
  66. package/package.json +1 -1
@@ -952,7 +952,7 @@ export class MetricService extends AnalyticsDatabaseService<Metric> {
952
952
  *
953
953
  * For non-percentile aggregations (Sum/Avg/Min/Max/Count) we delegate
954
954
  * to the base implementation. For percentile aggregations
955
- * (P50/P90/P95/P99) we build a subquery that fans each metric row out
955
+ * (P50/P75/P90/P95/P99) we build a subquery that fans each metric row out
956
956
  * into one or more `(midpoint, weight)` samples — derived from
957
957
  * histogram buckets when present — and then runs
958
958
  * `quantileExactWeighted` over the fanned-out distribution. This means
@@ -9,7 +9,10 @@ import { FindOperator, Raw } from "typeorm";
9
9
  import { FindWhereProperty } from "../../../Types/BaseDatabase/Query";
10
10
  import { toLikePattern } from "../../../Types/BaseDatabase/WildcardPattern";
11
11
  import CaptureSpan from "../../Utils/Telemetry/CaptureSpan";
12
- import buildJSONColumnQuery, { JSONColumnQuery } from "./JSONColumnQuery";
12
+ import buildJSONColumnQuery, {
13
+ JSONColumnQuery,
14
+ escapeLikePattern,
15
+ } from "./JSONColumnQuery";
13
16
 
14
17
  export type { FindOperator };
15
18
 
@@ -229,6 +232,56 @@ export default class QueryHelper {
229
232
  );
230
233
  }
231
234
 
235
+ /*
236
+ * Every word has to appear somewhere in the column, in any order.
237
+ *
238
+ * `search` is one substring, so "michigan 104822" misses a row called
239
+ * "Unit 104822 - Michigan Ave" even though both words are in it. Here each
240
+ * word gets its own ILIKE, AND-joined, which is how people recall a name
241
+ * they half remember.
242
+ *
243
+ * A word is matched literally: a `%` or `_` somebody typed is escaped
244
+ * rather than read as a wildcard.
245
+ *
246
+ * No words matches NOTHING. An AND over zero predicates is vacuously true,
247
+ * and "every row in the table" is never what an empty search box meant.
248
+ */
249
+ @CaptureSpan()
250
+ public static searchAllWords(words: Array<string>): FindWhereProperty<any> {
251
+ const rids: Array<string> = [];
252
+ const valuesObj: Dictionary<string> = {};
253
+
254
+ for (const word of words) {
255
+ const trimmed: string = (word || "").trim();
256
+ if (!trimmed) {
257
+ continue;
258
+ }
259
+ const rid: string = Text.generateRandomText(10);
260
+ rids.push(rid);
261
+ valuesObj[rid] = `%${escapeLikePattern(trimmed)}%`;
262
+ }
263
+
264
+ if (rids.length === 0) {
265
+ return Raw(() => {
266
+ return `TRUE = FALSE`; // this will always return false
267
+ }, {});
268
+ }
269
+
270
+ return Raw(
271
+ (alias: string) => {
272
+ const conditions: string = rids
273
+ .map((rid: string) => {
274
+ return `CAST(${alias} AS TEXT) ILIKE :${rid}`;
275
+ })
276
+ .join(" AND ");
277
+ return `(${conditions})`;
278
+ },
279
+ {
280
+ ...valuesObj,
281
+ },
282
+ );
283
+ }
284
+
232
285
  /**
233
286
  * Searches the provided entity property names with a single OR-joined ILIKE.
234
287
  *
@@ -49,7 +49,7 @@ const METRIC_READ_PERMISSIONS: Array<Permission> = [
49
49
  export const QueryMetricsTool: ObservabilityTool = {
50
50
  name: "query_metrics",
51
51
  description:
52
- "Aggregate a metric over time: Avg, Max, Min, Sum, Count or P50/P90/P95/P99 percentiles (histogram-aware). Requires the exact metric name — discover names via lookup_context. Monitor metrics use reserved names like oneuptime.monitor.response.time with the monitor's ID as entityId.",
52
+ "Aggregate a metric over time: Avg, Max, Min, Sum, Count or P50/P75/P90/P95/P99 percentiles (histogram-aware). Requires the exact metric name — discover names via lookup_context. Monitor metrics use reserved names like oneuptime.monitor.response.time with the monitor's ID as entityId.",
53
53
  inputSchema: {
54
54
  type: "object",
55
55
  properties: {
@@ -60,7 +60,18 @@ export const QueryMetricsTool: ObservabilityTool = {
60
60
  },
61
61
  aggregationType: {
62
62
  type: "string",
63
- enum: ["Avg", "Max", "Min", "Sum", "Count", "P50", "P90", "P95", "P99"],
63
+ enum: [
64
+ "Avg",
65
+ "Max",
66
+ "Min",
67
+ "Sum",
68
+ "Count",
69
+ "P50",
70
+ "P75",
71
+ "P90",
72
+ "P95",
73
+ "P99",
74
+ ],
64
75
  description: "How to aggregate (default Avg).",
65
76
  },
66
77
  entityId: {
@@ -673,7 +673,18 @@ export const QueryResourceTelemetryTool: ObservabilityTool = {
673
673
  },
674
674
  aggregationType: {
675
675
  type: "string",
676
- enum: ["Avg", "Min", "Max", "Sum", "Count", "P50", "P90", "P95", "P99"],
676
+ enum: [
677
+ "Avg",
678
+ "Min",
679
+ "Max",
680
+ "Sum",
681
+ "Count",
682
+ "P50",
683
+ "P75",
684
+ "P90",
685
+ "P95",
686
+ "P99",
687
+ ],
677
688
  description:
678
689
  "Metric aggregation (defaultAvg); counter sums are exported values, not derived rates.",
679
690
  },
@@ -1938,7 +1938,7 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
1938
1938
  * EXAMPLE:
1939
1939
  * SELECT sum(Metric.value) as avg_value, date_trunc('hour', toStartOfInterval(createdAt, INTERVAL 1 hour)) as createdAt
1940
1940
  *
1941
- * Percentile aggregations (P50/P90/P95/P99) compile to ClickHouse's
1941
+ * Percentile aggregations (P50/P75/P90/P95/P99) compile to ClickHouse's
1942
1942
  * `quantile(level)(column)`. This is the right thing for scalar
1943
1943
  * columns (Span.duration, Metric.value when the metric is a Sum or
1944
1944
  * Gauge, etc.). MetricService overrides this method when it has
@@ -280,6 +280,39 @@ export default class CompareCriteria {
280
280
  return threshold as number;
281
281
  }
282
282
 
283
+ /*
284
+ * A metric-value threshold as a number, fraction included.
285
+ *
286
+ * The criteria form saves what the user typed as a STRING, and
287
+ * convertToNumber's parseInt truncated it: "0.9" on a ratio metric was
288
+ * compared as 0 and fired on every non-zero sample, and "85.5" was
289
+ * compared as 85. Metric values are where a fractional threshold is the
290
+ * natural one to type (ratios, seconds, load averages), so they are
291
+ * parsed with parseFloat. Every other checkOn keeps convertToNumber.
292
+ *
293
+ * Anything that does not parse to a finite number is treated as missing,
294
+ * so the caller ignores the filter instead of comparing against NaN.
295
+ */
296
+ @CaptureSpan()
297
+ public static convertMetricThresholdToNumber(
298
+ threshold: string | number | undefined,
299
+ ): number | null {
300
+ if (threshold === undefined || threshold === null) {
301
+ return null;
302
+ }
303
+
304
+ const parsed: number =
305
+ typeof threshold === Typeof.String
306
+ ? parseFloat(threshold as string)
307
+ : (threshold as number);
308
+
309
+ if (typeof parsed !== Typeof.Number || !Number.isFinite(parsed)) {
310
+ return null;
311
+ }
312
+
313
+ return parsed;
314
+ }
315
+
283
316
  @CaptureSpan()
284
317
  public static checkEqualToOrNotEqualTo(data: {
285
318
  value: string | number;
@@ -180,9 +180,10 @@ export default class MetricMonitorCriteria {
180
180
  projectId: { toString(): string } | undefined;
181
181
  nativeUnitsByMetricName?: { [key: string]: string } | undefined;
182
182
  }): Promise<MetricSeriesEvaluationResult> {
183
- const rawThreshold: number | null = CompareCriteria.convertToNumber(
184
- input.criteriaFilter.value,
185
- );
183
+ const rawThreshold: number | null =
184
+ CompareCriteria.convertMetricThresholdToNumber(
185
+ input.criteriaFilter.value,
186
+ );
186
187
 
187
188
  const metricAlias: string =
188
189
  input.criteriaFilter.metricMonitorOptions?.metricAlias || "";
@@ -4659,7 +4659,9 @@ ${contextBlock}
4659
4659
  .map((f: CriteriaFilter) => {
4660
4660
  return {
4661
4661
  filter: f,
4662
- threshold: CompareCriteria.convertToNumber(f.value) as number,
4662
+ threshold: CompareCriteria.convertMetricThresholdToNumber(
4663
+ f.value,
4664
+ ) as number,
4663
4665
  };
4664
4666
  })
4665
4667
  .filter((f: { filter: CriteriaFilter; threshold: number | null }) => {