@oneuptime/common 14.0.5 → 14.0.7

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 (192) hide show
  1. package/Models/DatabaseModels/InventoryItem.ts +23 -12
  2. package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
  3. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  4. package/Server/Services/AlertService.ts +32 -36
  5. package/Server/Services/IncidentAlertService.ts +609 -2
  6. package/Server/Services/IncidentService.ts +134 -44
  7. package/Server/Services/InventoryItemService.ts +38 -0
  8. package/Server/Services/IoTDeviceCredentialService.ts +13 -4
  9. package/Server/Services/MetricService.ts +1 -1
  10. package/Server/Services/ScheduledMaintenanceService.ts +50 -46
  11. package/Server/Services/UserNotificationRuleService.ts +55 -53
  12. package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
  13. package/Server/Types/Database/QueryHelper.ts +54 -1
  14. package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
  15. package/Server/Utils/AI/Toolbox/ResourceTools.ts +12 -1
  16. package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
  17. package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
  18. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
  19. package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
  20. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
  21. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +4 -3
  22. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +3 -1
  23. package/Server/Utils/Response.ts +31 -0
  24. package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
  25. package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
  26. package/Server/Utils/Topology/TopologyRequest.ts +463 -0
  27. package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
  28. package/Server/Utils/Topology/TopologySql.ts +1024 -0
  29. package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
  30. package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
  31. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
  32. package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
  33. package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
  34. package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
  35. package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
  36. package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
  37. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
  38. package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
  39. package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
  40. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +292 -1
  41. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
  42. package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
  43. package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
  44. package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
  45. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
  46. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
  47. package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
  48. package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
  49. package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
  50. package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
  51. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
  52. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
  53. package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
  54. package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
  55. package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
  56. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
  57. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
  58. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
  59. package/Tests/Server/Services/DomainService.test.ts +525 -0
  60. package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
  61. package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
  62. package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
  63. package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
  64. package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
  65. package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
  66. package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
  67. package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
  68. package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
  69. package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
  70. package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
  71. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
  72. package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
  73. package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
  74. package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
  75. package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
  76. package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
  77. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
  78. package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
  79. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +96 -0
  80. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
  81. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
  82. package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
  83. package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
  84. package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
  85. package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
  86. package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
  87. package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
  88. package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
  89. package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
  90. package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
  91. package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
  92. package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
  93. package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
  94. package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
  95. package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
  96. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +8 -14
  97. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +4 -3
  98. package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +27 -6
  99. package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
  100. package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
  101. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
  102. package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
  103. package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
  104. package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
  105. package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
  106. package/Types/BaseDatabase/AggregationType.ts +7 -0
  107. package/Types/Dashboard/DashboardTemplates.ts +20 -7
  108. package/Types/Incident/IncidentAlertLink.ts +19 -0
  109. package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
  110. package/Types/Rum/WebVitals.ts +13 -0
  111. package/Types/Telemetry/InventoryLiveness.ts +26 -0
  112. package/Types/Topology/TopologyApi.ts +428 -0
  113. package/Types/Topology/TopologyTypeRules.ts +191 -0
  114. package/UI/Components/Input/Input.tsx +11 -0
  115. package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
  116. package/Utils/Incident/IncidentFromAlerts.ts +78 -0
  117. package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
  118. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  119. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
  120. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js.map +1 -0
  121. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  122. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  123. package/build/dist/Server/Services/AlertService.js +21 -33
  124. package/build/dist/Server/Services/AlertService.js.map +1 -1
  125. package/build/dist/Server/Services/IncidentAlertService.js +457 -3
  126. package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
  127. package/build/dist/Server/Services/IncidentService.js +96 -42
  128. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  129. package/build/dist/Server/Services/InventoryItemService.js +31 -0
  130. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  131. package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
  132. package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
  133. package/build/dist/Server/Services/MetricService.js +1 -1
  134. package/build/dist/Server/Services/ScheduledMaintenanceService.js +37 -40
  135. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  136. package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
  137. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  138. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
  139. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
  140. package/build/dist/Server/Types/Database/QueryHelper.js +47 -1
  141. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  142. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
  143. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
  144. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +12 -1
  145. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  146. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
  147. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
  148. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
  149. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
  150. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
  151. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
  152. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
  153. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
  154. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  155. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +1 -1
  156. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  157. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1 -1
  158. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  159. package/build/dist/Server/Utils/Response.js +30 -0
  160. package/build/dist/Server/Utils/Response.js.map +1 -1
  161. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
  162. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
  163. package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
  164. package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
  165. package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
  166. package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
  167. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
  168. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
  169. package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
  170. package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
  171. package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
  172. package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
  173. package/build/dist/Types/Dashboard/DashboardTemplates.js +20 -7
  174. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  175. package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
  176. package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
  177. package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
  178. package/build/dist/Types/Rum/WebVitals.js +12 -0
  179. package/build/dist/Types/Rum/WebVitals.js.map +1 -1
  180. package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
  181. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
  182. package/build/dist/Types/Topology/TopologyApi.js +55 -0
  183. package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
  184. package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
  185. package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
  186. package/build/dist/UI/Components/Input/Input.js +1 -1
  187. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  188. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
  189. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
  190. package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
  191. package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
  192. package/package.json +1 -1
@@ -72,6 +72,7 @@ import {
72
72
  fetchLogAndExceptionSignals,
73
73
  fetchSpanMetrics,
74
74
  fetchSpanNameStats,
75
+ fetchWebVitalByRoute,
75
76
  fetchWebVitals,
76
77
  formatBytes as reexportedFormatBytes,
77
78
  formatCompact as reexportedFormatCompact,
@@ -84,7 +85,9 @@ import {
84
85
  spanNameStatsFromTableRows,
85
86
  summarizeLogAndExceptionBuckets,
86
87
  TimePoint,
88
+ WEB_VITAL_ROUTE_LIMIT,
87
89
  WebVital,
90
+ WebVitalByRoute,
88
91
  } from "../../../../App/FeatureSet/Dashboard/src/Components/TelemetryResource/telemetryMetrics";
89
92
  import {
90
93
  formatBytes,
@@ -98,11 +101,13 @@ import URL from "../../../Types/API/URL";
98
101
  import { JSONObject } from "../../../Types/JSON";
99
102
  import ObjectID from "../../../Types/ObjectID";
100
103
  import AggregationType from "../../../Types/BaseDatabase/AggregationType";
104
+ import AggregationInterval from "../../../Types/BaseDatabase/AggregationInterval";
101
105
  import InBetween from "../../../Types/BaseDatabase/InBetween";
102
106
  import { SpanStatus } from "../../../Models/AnalyticsModels/Span";
103
107
  import {
104
108
  WebVitalDefinition,
105
109
  WebVitalDefinitions,
110
+ WebVitalRouteAttributeKeys,
106
111
  } from "../../../Types/Rum/WebVitals";
107
112
  import { APP_API_URL } from "../../../UI/Config";
108
113
 
@@ -126,10 +131,13 @@ interface AggregateRequest {
126
131
  aggregateBy: {
127
132
  query: Record<string, unknown>;
128
133
  aggregationType: AggregationType;
134
+ aggregationInterval?: AggregationInterval;
129
135
  aggregateColumnName: string;
130
136
  aggregationTimestampColumnName: string;
131
137
  startTimestamp: Date;
132
138
  endTimestamp: Date;
139
+ groupByAttributeKeys?: Array<string>;
140
+ topK?: { count: number; rankBy: string };
133
141
  };
134
142
  }
135
143
 
@@ -1072,6 +1080,7 @@ describe("fetchWebVitals", () => {
1072
1080
  value: null,
1073
1081
  unit: d.unit,
1074
1082
  thresholds: d.thresholds,
1083
+ metricName: null,
1075
1084
  });
1076
1085
  expect(v.description.length).toBeGreaterThan(20);
1077
1086
  });
@@ -1086,12 +1095,7 @@ describe("fetchWebVitals", () => {
1086
1095
  ];
1087
1096
 
1088
1097
  if (name === second) {
1089
- return {
1090
- data: [
1091
- { time: T0, value: 1000 },
1092
- { time: T1, value: 3000 },
1093
- ],
1094
- };
1098
+ return { data: [{ time: T0, value: 2000 }] };
1095
1099
  }
1096
1100
 
1097
1101
  return { data: [] };
@@ -1107,8 +1111,9 @@ describe("fetchWebVitals", () => {
1107
1111
  return v.key === "lcp";
1108
1112
  })!;
1109
1113
 
1110
- // The average of the per-interval averages.
1114
+ // The one whole-range mean, and the name it was found under.
1111
1115
  expect(lcp.value).toBe(2000);
1116
+ expect(lcp.metricName).toBe(second);
1112
1117
  expect(lcp.description).toBe(LCP.description);
1113
1118
 
1114
1119
  const asked: Array<string> = metricNames();
@@ -1119,7 +1124,13 @@ describe("fetchWebVitals", () => {
1119
1124
  }
1120
1125
  });
1121
1126
 
1122
- test("averages, scoped to the entity and window", async () => {
1127
+ /*
1128
+ * A mean of the whole range at once - not a mean of per-interval means,
1129
+ * which let a quiet interval count as much as a busy one. A mean rather
1130
+ * than a p75 because a histogram's percentile comes from its buckets,
1131
+ * and the OpenTelemetry defaults put every CLS value in (0, 5].
1132
+ */
1133
+ test("asks for one mean over the whole window, scoped to the entity", async () => {
1123
1134
  aggregateMock.mockResolvedValue({ data: [] });
1124
1135
 
1125
1136
  await fetchWebVitals({
@@ -1130,6 +1141,7 @@ describe("fetchWebVitals", () => {
1130
1141
 
1131
1142
  for (const r of aggregateRequests()) {
1132
1143
  expect(r.aggregateBy.aggregationType).toBe(AggregationType.Avg);
1144
+ expect(r.aggregateBy.aggregationInterval).toBe(AggregationInterval.Total);
1133
1145
  expect(String(r.aggregateBy.query["primaryEntityId"])).toBe(ENTITY_ID);
1134
1146
  expect(r.aggregateBy.startTimestamp).toBe(START);
1135
1147
  expect(r.aggregateBy.endTimestamp).toBe(END);
@@ -1137,6 +1149,154 @@ describe("fetchWebVitals", () => {
1137
1149
  });
1138
1150
  });
1139
1151
 
1152
+ describe("fetchWebVitalByRoute", () => {
1153
+ const INP_NAME: string = "web_vital.inp";
1154
+
1155
+ function ask(): Promise<WebVitalByRoute> {
1156
+ return fetchWebVitalByRoute({
1157
+ primaryEntityId: new ObjectID(ENTITY_ID),
1158
+ metricName: INP_NAME,
1159
+ start: START,
1160
+ end: END,
1161
+ });
1162
+ }
1163
+
1164
+ function routeRow(
1165
+ key: string,
1166
+ route: string | undefined,
1167
+ value: number,
1168
+ ): Record<string, unknown> {
1169
+ return {
1170
+ time: T0,
1171
+ value: value,
1172
+ attributes: route === undefined ? {} : { [key]: route },
1173
+ };
1174
+ }
1175
+
1176
+ test("asks for the mean of this metric per route over the whole window, top routes only", async () => {
1177
+ aggregateMock.mockResolvedValue({ data: [] });
1178
+
1179
+ await ask();
1180
+
1181
+ const [first] = aggregateRequests();
1182
+
1183
+ expect(first?.aggregateBy.query["name"]).toBe(INP_NAME);
1184
+ expect(String(first?.aggregateBy.query["primaryEntityId"])).toBe(ENTITY_ID);
1185
+ expect(String(first?.aggregateBy.query["projectId"])).toBe(PROJECT_ID);
1186
+ expect(first?.aggregateBy.aggregationType).toBe(AggregationType.Avg);
1187
+ expect(first?.aggregateBy.aggregationInterval).toBe(
1188
+ AggregationInterval.Total,
1189
+ );
1190
+ expect(first?.aggregateBy.groupByAttributeKeys).toEqual(["app.route"]);
1191
+ // One spare slot for the pooled rows that carry no route at all.
1192
+ expect(first?.aggregateBy.topK).toEqual({
1193
+ count: WEB_VITAL_ROUTE_LIMIT + 1,
1194
+ rankBy: "max",
1195
+ });
1196
+ });
1197
+
1198
+ test("returns the routes slowest first, leaving out rows without a route", async () => {
1199
+ aggregateMock.mockResolvedValue({
1200
+ data: [
1201
+ routeRow("app.route", "/cart", 240),
1202
+ routeRow("app.route", undefined, 900),
1203
+ routeRow("app.route", " ", 800),
1204
+ routeRow("app.route", "/products/:id", 620),
1205
+ ],
1206
+ totalGroups: 5,
1207
+ });
1208
+
1209
+ const result: WebVitalByRoute = await ask();
1210
+
1211
+ expect(result).toEqual({
1212
+ routeAttribute: "app.route",
1213
+ routes: [
1214
+ { route: "/products/:id", value: 620 },
1215
+ { route: "/cart", value: 240 },
1216
+ ],
1217
+ // Five groups, one of them the blank no-route group.
1218
+ totalRoutes: 4,
1219
+ failed: false,
1220
+ });
1221
+ });
1222
+
1223
+ test("falls back to url.template, OpenTelemetry's name for the route", async () => {
1224
+ aggregateMock.mockImplementation(async (...args: Array<unknown>) => {
1225
+ const keys: Array<string> =
1226
+ (args[0] as AggregateRequest).aggregateBy.groupByAttributeKeys || [];
1227
+
1228
+ if (keys[0] === "url.template") {
1229
+ return { data: [routeRow("url.template", "/orders/{id}", 310)] };
1230
+ }
1231
+
1232
+ return { data: [routeRow("app.route", undefined, 310)] };
1233
+ });
1234
+
1235
+ const result: WebVitalByRoute = await ask();
1236
+
1237
+ expect(
1238
+ aggregateRequests().map((r: AggregateRequest): unknown => {
1239
+ return r.aggregateBy.groupByAttributeKeys;
1240
+ }),
1241
+ ).toEqual(
1242
+ WebVitalRouteAttributeKeys.map((key: string): Array<string> => {
1243
+ return [key];
1244
+ }),
1245
+ );
1246
+ expect(result.routeAttribute).toBe("url.template");
1247
+ expect(result.routes).toEqual([{ route: "/orders/{id}", value: 310 }]);
1248
+ expect(result.totalRoutes).toBeNull();
1249
+ });
1250
+
1251
+ test("keeps at most the route limit", async () => {
1252
+ aggregateMock.mockResolvedValue({
1253
+ data: Array.from(
1254
+ { length: WEB_VITAL_ROUTE_LIMIT + 1 },
1255
+ (_: unknown, i: number) => {
1256
+ return routeRow("app.route", `/r${i}`, 100 + i);
1257
+ },
1258
+ ),
1259
+ });
1260
+
1261
+ const result: WebVitalByRoute = await ask();
1262
+
1263
+ expect(result.routes).toHaveLength(WEB_VITAL_ROUTE_LIMIT);
1264
+ expect(result.routes[0]?.value).toBe(100 + WEB_VITAL_ROUTE_LIMIT);
1265
+ });
1266
+
1267
+ test("no route under any attribute is an empty answer, not a failure", async () => {
1268
+ aggregateMock.mockResolvedValue({ data: [] });
1269
+
1270
+ expect(await ask()).toEqual({
1271
+ routeAttribute: null,
1272
+ routes: [],
1273
+ totalRoutes: null,
1274
+ failed: false,
1275
+ });
1276
+ expect(aggregateMock).toHaveBeenCalledTimes(
1277
+ WebVitalRouteAttributeKeys.length,
1278
+ );
1279
+ });
1280
+
1281
+ test("a failed lookup is marked failed rather than read as no routes", async () => {
1282
+ aggregateMock.mockRejectedValue(new Error("403"));
1283
+
1284
+ const result: WebVitalByRoute = await ask();
1285
+
1286
+ expect(result.failed).toBe(true);
1287
+ expect(result.routes).toEqual([]);
1288
+ });
1289
+
1290
+ test("no current project: returns empty without asking", async () => {
1291
+ projectIdMock.mockReturnValue(null);
1292
+
1293
+ const result: WebVitalByRoute = await ask();
1294
+
1295
+ expect(result.failed).toBe(false);
1296
+ expect(aggregateMock).not.toHaveBeenCalled();
1297
+ });
1298
+ });
1299
+
1140
1300
  describe("telemetryFormat", () => {
1141
1301
  test.each([
1142
1302
  [null, "—"],