@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
@@ -541,13 +541,16 @@ function vitalsWith(
541
541
  value: (d: WebVitalDefinition) => number | null,
542
542
  ): Array<WebVital> {
543
543
  return WebVitalDefinitions.map((d: WebVitalDefinition): WebVital => {
544
+ const reported: number | null = value(d);
544
545
  return {
545
546
  key: d.key,
546
547
  label: d.label,
547
548
  description: d.description,
548
- value: value(d),
549
+ value: reported,
549
550
  unit: d.unit,
550
551
  thresholds: d.thresholds,
552
+ // As fetchWebVitals reports it: the first alias, once one has data.
553
+ metricName: reported === null ? null : d.names[0] || null,
551
554
  };
552
555
  });
553
556
  }
@@ -40,6 +40,7 @@ const countMock: MockFunction = getJestMockFunction();
40
40
  const fetchSpanMetricsMock: MockFunction = getJestMockFunction();
41
41
  const fetchSpanNameStatsMock: MockFunction = getJestMockFunction();
42
42
  const fetchWebVitalsMock: MockFunction = getJestMockFunction();
43
+ const fetchWebVitalByRouteMock: MockFunction = getJestMockFunction();
43
44
  const fetchSignalsMock: MockFunction = getJestMockFunction();
44
45
  const fetchSessionReplayListMock: MockFunction = getJestMockFunction();
45
46
  const lineChartMock: MockFunction = getJestMockFunction();
@@ -204,6 +205,9 @@ jest.mock(
204
205
  fetchWebVitals: (...args: Array<unknown>): unknown => {
205
206
  return fetchWebVitalsMock(...args);
206
207
  },
208
+ fetchWebVitalByRoute: (...args: Array<unknown>): unknown => {
209
+ return fetchWebVitalByRouteMock(...args);
210
+ },
207
211
  fetchLogAndExceptionSignals: (...args: Array<unknown>): unknown => {
208
212
  return fetchSignalsMock(...args);
209
213
  },
@@ -279,6 +283,7 @@ import {
279
283
  SpanNameStats,
280
284
  LogAndExceptionSignals,
281
285
  WebVital,
286
+ WebVitalByRoute,
282
287
  } from "../../../../App/FeatureSet/Dashboard/src/Components/TelemetryResource/telemetryMetrics";
283
288
  import PageComponentProps from "../../../../App/FeatureSet/Dashboard/src/Pages/PageComponentProps";
284
289
  import {
@@ -367,10 +372,25 @@ const VITALS: Array<WebVital> = WebVitalDefinitions.map(
367
372
  value: d.unit === "score" ? 0.05 : d.thresholds.warn / 2,
368
373
  unit: d.unit,
369
374
  thresholds: d.thresholds,
375
+ metricName: null,
370
376
  };
371
377
  },
372
378
  );
373
379
 
380
+ /* INP as instrumentation that emits web_vital.inp reports it. */
381
+ const VITALS_WITH_INP: Array<WebVital> = VITALS.map((v: WebVital): WebVital => {
382
+ return v.key === "inp"
383
+ ? { ...v, value: 260, metricName: "web_vital.inp" }
384
+ : v;
385
+ });
386
+
387
+ const NO_ROUTES: WebVitalByRoute = {
388
+ routeAttribute: null,
389
+ routes: [],
390
+ totalRoutes: null,
391
+ failed: false,
392
+ };
393
+
374
394
  function sessions(count: number): Array<{ sessionId: string }> {
375
395
  return Array.from({ length: count }, (_: unknown, i: number) => {
376
396
  return { sessionId: `s-${i}` };
@@ -419,6 +439,7 @@ interface Scenario {
419
439
  pageLoadStats?: Promise<SpanNameStats> | undefined;
420
440
  signals?: Promise<LogAndExceptionSignals> | undefined;
421
441
  vitals?: Promise<Array<WebVital>> | undefined;
442
+ inpByRoute?: Promise<WebVitalByRoute> | undefined;
422
443
  sessionList?: Promise<unknown> | undefined;
423
444
  }
424
445
 
@@ -444,6 +465,9 @@ function arrange(scenario: Scenario = {}): void {
444
465
  fetchWebVitalsMock.mockImplementation(() => {
445
466
  return scenario.vitals ?? Promise.resolve(VITALS);
446
467
  });
468
+ fetchWebVitalByRouteMock.mockImplementation(() => {
469
+ return scenario.inpByRoute ?? Promise.resolve(NO_ROUTES);
470
+ });
447
471
  fetchSessionReplayListMock.mockImplementation(() => {
448
472
  return (
449
473
  scenario.sessionList ??
@@ -560,6 +584,7 @@ beforeEach(() => {
560
584
  fetchSpanMetricsMock,
561
585
  fetchSpanNameStatsMock,
562
586
  fetchWebVitalsMock,
587
+ fetchWebVitalByRouteMock,
563
588
  fetchSignalsMock,
564
589
  fetchSessionReplayListMock,
565
590
  lineChartMock,
@@ -1193,3 +1218,105 @@ describe("RUM overview: refreshing", () => {
1193
1218
  expectTile("Page loads", "42", "3 failed");
1194
1219
  });
1195
1220
  });
1221
+
1222
+ /*
1223
+ * Issue #3975. The app-wide INP mixes every view of a single-page app
1224
+ * together; the per-route card is where the slow one shows up.
1225
+ */
1226
+ describe("RUM overview: INP by route", () => {
1227
+ test("is not shown, and not asked for, until INP has reported", async () => {
1228
+ arrange();
1229
+ await renderPage();
1230
+
1231
+ expect(fetchWebVitalByRouteMock).not.toHaveBeenCalled();
1232
+ expect(screen.queryByText("INP by route")).toBeNull();
1233
+ });
1234
+
1235
+ test("asks for INP per route under the name the vitals card found it by", async () => {
1236
+ arrange({ vitals: Promise.resolve(VITALS_WITH_INP) });
1237
+ await renderPage();
1238
+
1239
+ expect(fetchWebVitalByRouteMock).toHaveBeenCalledTimes(1);
1240
+
1241
+ const request: {
1242
+ primaryEntityId: ObjectID;
1243
+ metricName: string;
1244
+ start: Date;
1245
+ end: Date;
1246
+ } = fetchWebVitalByRouteMock.mock.calls[0]![0] as {
1247
+ primaryEntityId: ObjectID;
1248
+ metricName: string;
1249
+ start: Date;
1250
+ end: Date;
1251
+ };
1252
+
1253
+ expect(request.metricName).toBe("web_vital.inp");
1254
+ expect(request.primaryEntityId.toString()).toBe(MODEL_ID);
1255
+ expect(request.end.getTime()).toBeGreaterThan(request.start.getTime());
1256
+ });
1257
+
1258
+ test("lists the routes slowest first, each rated, and explains itself", async () => {
1259
+ arrange({
1260
+ vitals: Promise.resolve(VITALS_WITH_INP),
1261
+ inpByRoute: Promise.resolve({
1262
+ routeAttribute: "app.route",
1263
+ routes: [
1264
+ { route: "/products/:id", value: 620 },
1265
+ { route: "/cart", value: 240 },
1266
+ { route: "/", value: 90 },
1267
+ ],
1268
+ totalRoutes: 12,
1269
+ failed: false,
1270
+ }),
1271
+ });
1272
+ await renderPage();
1273
+
1274
+ expect(screen.getByText("INP by route")).toBeInTheDocument();
1275
+ expect(
1276
+ await tooltipTextOf(
1277
+ screen.getByRole("button", { name: "About INP by route" }),
1278
+ ),
1279
+ ).toBe(RUM_METRIC_DESCRIPTIONS.inpByRoute);
1280
+
1281
+ const rows: Array<HTMLElement> = screen.getAllByTestId(
1282
+ "web-vital-route-row",
1283
+ );
1284
+
1285
+ expect(
1286
+ rows.map((row: HTMLElement): string => {
1287
+ return row.textContent || "";
1288
+ }),
1289
+ ).toEqual([
1290
+ "/products/:id620 msPoor",
1291
+ "/cart240 msNeeds work",
1292
+ "/90 msGood",
1293
+ ]);
1294
+ expect(screen.getByText("(app.route)")).toBeInTheDocument();
1295
+ expect(screen.getByText(/The 3 slowest of 12/)).toBeInTheDocument();
1296
+ });
1297
+
1298
+ test("INP without a route attribute says how to add one", async () => {
1299
+ arrange({ vitals: Promise.resolve(VITALS_WITH_INP) });
1300
+ await renderPage();
1301
+
1302
+ expect(
1303
+ screen.getByText("INP is reported, but not per route"),
1304
+ ).toBeInTheDocument();
1305
+ expect(
1306
+ screen.getByRole("link", { name: "How to measure INP per route" }),
1307
+ ).toHaveAttribute("href", "/docs/rum/web-vitals#single-page-apps");
1308
+ });
1309
+
1310
+ test("a failed lookup says so rather than claiming there are no routes", async () => {
1311
+ arrange({
1312
+ vitals: Promise.resolve(VITALS_WITH_INP),
1313
+ inpByRoute: Promise.resolve({ ...NO_ROUTES, failed: true }),
1314
+ });
1315
+ await renderPage();
1316
+
1317
+ expect(
1318
+ screen.getByText("Could not load INP by route."),
1319
+ ).toBeInTheDocument();
1320
+ expect(screen.queryByText("INP is reported, but not per route")).toBeNull();
1321
+ });
1322
+ });