@oneuptime/common 12.0.14 → 12.0.15

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 (195) hide show
  1. package/Models/DatabaseModels/GlobalOidc.ts +20 -0
  2. package/Models/DatabaseModels/GlobalSso.ts +20 -0
  3. package/Models/DatabaseModels/MarketingConversion.ts +19 -9
  4. package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +46 -0
  5. package/Models/DatabaseModels/StatusPage.ts +7 -4
  6. package/Server/API/StatusPageAPI.ts +29 -4
  7. package/Server/EnvironmentConfig.ts +7 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.ts +19 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.ts +40 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  11. package/Server/Middleware/SlackAuthorization.ts +43 -6
  12. package/Server/Middleware/UserAuthorization.ts +236 -26
  13. package/Server/Middleware/WhatsAppAuthorization.ts +43 -3
  14. package/Server/Services/GlobalOidcProjectService.ts +126 -1
  15. package/Server/Services/GlobalOidcService.ts +132 -0
  16. package/Server/Services/GlobalSsoProjectService.ts +126 -1
  17. package/Server/Services/GlobalSsoService.ts +132 -0
  18. package/Server/Services/UserEmailService.ts +4 -4
  19. package/Server/Utils/CronTab.ts +73 -0
  20. package/Server/Utils/GlobalSsoAuthorization.ts +175 -0
  21. package/Server/Utils/Marketing/ConversionUploadProvider.ts +35 -5
  22. package/Server/Utils/Marketing/Providers/GoogleAds.ts +1 -1
  23. package/Server/Utils/Marketing/Providers/LinkedIn.ts +1 -1
  24. package/Server/Utils/Marketing/Providers/Meta.ts +1 -1
  25. package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +1 -1
  26. package/Server/Utils/Marketing/Providers/Reddit.ts +1 -1
  27. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +48 -33
  28. package/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.ts +35 -30
  29. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +33 -28
  30. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +475 -47
  31. package/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.ts +35 -30
  32. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +34 -28
  33. package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +33 -28
  34. package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +69 -40
  35. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +35 -29
  36. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +8 -0
  37. package/Server/Utils/Monitor/MonitorResource.ts +161 -21
  38. package/Server/Utils/StatusPageContentSecurityPolicy.ts +56 -0
  39. package/Server/Utils/StatusPageCustomizationAccess.ts +117 -0
  40. package/Tests/Server/API/StatusPageCustomizationOrigin.test.ts +348 -0
  41. package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +9 -59
  42. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +227 -0
  43. package/Tests/Server/Middleware/SlackAuthorization.test.ts +149 -0
  44. package/Tests/Server/Middleware/UserAuthorization.test.ts +27 -13
  45. package/Tests/Server/Middleware/UserAuthorizationGlobalSsoGovernance.test.ts +1455 -0
  46. package/Tests/Server/Middleware/UserAuthorizationSSOProvider.test.ts +91 -2
  47. package/Tests/Server/Middleware/WhatsAppAuthorization.test.ts +233 -0
  48. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +21 -8
  49. package/Tests/Server/Services/GlobalSsoProviderAuthorization.test.ts +1242 -0
  50. package/Tests/Server/Services/MonitorResourceProbeAgreementHydration.test.ts +459 -0
  51. package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +10 -0
  52. package/Tests/Server/Types/AnalyticsDatabase/AggregateUtilBucketTimestamp.test.ts +416 -0
  53. package/Tests/Server/Types/Database/QueryHelperManyToManyAndEmptyValues.test.ts +551 -0
  54. package/Tests/Server/Types/Database/SelectUtil.test.ts +348 -0
  55. package/Tests/Server/Utils/CronTab.test.ts +116 -0
  56. package/Tests/Server/Utils/GlobalSSOToken.test.ts +581 -0
  57. package/Tests/Server/Utils/GlobalSsoAuthorization.test.ts +1401 -0
  58. package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +254 -0
  59. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +1 -1
  60. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaEvaluateOverTime.test.ts +508 -0
  61. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +30 -9
  62. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +987 -0
  63. package/Tests/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.test.ts +916 -0
  64. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +382 -0
  65. package/Tests/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.test.ts +1219 -0
  66. package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +86 -0
  67. package/Tests/Server/Utils/StatusPageContentSecurityPolicy.test.ts +136 -0
  68. package/Tests/Server/Utils/StatusPageCustomizationAccess.test.ts +223 -0
  69. package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +165 -0
  70. package/Tests/Types/NetworkDevice/NetworkDeviceLinkRuleScope.test.ts +215 -0
  71. package/Tests/UI/Components/Forms/FormSubmitAnalytics.test.tsx +206 -0
  72. package/Tests/UI/Utils/AIChatExport/ConversationMarkdown.test.ts +774 -0
  73. package/Tests/UI/Utils/AIChatExport/MarkdownBlocks.test.ts +791 -0
  74. package/Tests/UI/Utils/AIChatExport/MarkdownSafety.test.ts +544 -0
  75. package/Tests/Utils/Analytics.test.ts +187 -0
  76. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +80 -0
  77. package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +1291 -0
  78. package/Tests/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.test.ts +214 -0
  79. package/Types/Marketing/MarketingConversion.ts +12 -0
  80. package/Types/Monitor/CriteriaFilter.ts +9 -0
  81. package/Types/NetworkDevice/NetworkDeviceLinkRuleScope.ts +51 -0
  82. package/UI/Components/FormModal/BasicFormModal.tsx +12 -0
  83. package/UI/Components/Forms/BasicForm.tsx +13 -1
  84. package/UI/Components/Forms/BasicModelForm.tsx +2 -1
  85. package/UI/Components/Forms/ModelForm.tsx +10 -1
  86. package/UI/Components/Forms/Utils/FormAnalyticsName.ts +39 -0
  87. package/UI/Components/ModelFormModal/ModelFormModal.tsx +6 -1
  88. package/Utils/Analytics.ts +55 -2
  89. package/Utils/Monitor/MonitorMetricType.ts +40 -0
  90. package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +587 -37
  91. package/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.ts +115 -0
  92. package/build/dist/Models/DatabaseModels/GlobalOidc.js +21 -0
  93. package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
  94. package/build/dist/Models/DatabaseModels/GlobalSso.js +21 -0
  95. package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
  96. package/build/dist/Models/DatabaseModels/MarketingConversion.js +20 -10
  97. package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +1 -1
  98. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +47 -0
  99. package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -1
  100. package/build/dist/Models/DatabaseModels/StatusPage.js +4 -4
  101. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  102. package/build/dist/Server/API/StatusPageAPI.js +25 -4
  103. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  104. package/build/dist/Server/EnvironmentConfig.js +6 -0
  105. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js +12 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js.map +1 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js +29 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js.map +1 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  111. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  112. package/build/dist/Server/Middleware/SlackAuthorization.js +28 -4
  113. package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
  114. package/build/dist/Server/Middleware/UserAuthorization.js +197 -22
  115. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  116. package/build/dist/Server/Middleware/WhatsAppAuthorization.js +32 -2
  117. package/build/dist/Server/Middleware/WhatsAppAuthorization.js.map +1 -1
  118. package/build/dist/Server/Services/GlobalOidcProjectService.js +105 -0
  119. package/build/dist/Server/Services/GlobalOidcProjectService.js.map +1 -1
  120. package/build/dist/Server/Services/GlobalOidcService.js +127 -0
  121. package/build/dist/Server/Services/GlobalOidcService.js.map +1 -1
  122. package/build/dist/Server/Services/GlobalSsoProjectService.js +105 -0
  123. package/build/dist/Server/Services/GlobalSsoProjectService.js.map +1 -1
  124. package/build/dist/Server/Services/GlobalSsoService.js +127 -0
  125. package/build/dist/Server/Services/GlobalSsoService.js.map +1 -1
  126. package/build/dist/Server/Services/UserEmailService.js +4 -4
  127. package/build/dist/Server/Utils/CronTab.js +66 -0
  128. package/build/dist/Server/Utils/CronTab.js.map +1 -1
  129. package/build/dist/Server/Utils/GlobalSsoAuthorization.js +112 -0
  130. package/build/dist/Server/Utils/GlobalSsoAuthorization.js.map +1 -0
  131. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +17 -1
  132. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +1 -1
  133. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +1 -1
  134. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +1 -1
  135. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +1 -1
  136. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +1 -1
  137. package/build/dist/Server/Utils/Marketing/Providers/Meta.js +1 -1
  138. package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +1 -1
  139. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +1 -1
  140. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +1 -1
  141. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +1 -1
  142. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +1 -1
  143. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +37 -35
  144. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  145. package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js +21 -25
  146. package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js.map +1 -1
  147. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +20 -22
  148. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  149. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +370 -42
  150. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  151. package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js +21 -25
  152. package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js.map +1 -1
  153. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +21 -25
  154. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  155. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +21 -25
  156. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
  157. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +64 -52
  158. package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
  159. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +25 -29
  160. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  161. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +8 -0
  162. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  163. package/build/dist/Server/Utils/Monitor/MonitorResource.js +123 -23
  164. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  165. package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js +39 -0
  166. package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js.map +1 -0
  167. package/build/dist/Server/Utils/StatusPageCustomizationAccess.js +85 -0
  168. package/build/dist/Server/Utils/StatusPageCustomizationAccess.js.map +1 -0
  169. package/build/dist/Types/Marketing/MarketingConversion.js +10 -0
  170. package/build/dist/Types/Marketing/MarketingConversion.js.map +1 -1
  171. package/build/dist/Types/Monitor/CriteriaFilter.js +1 -0
  172. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  173. package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js +46 -0
  174. package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js.map +1 -0
  175. package/build/dist/UI/Components/FormModal/BasicFormModal.js +2 -1
  176. package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
  177. package/build/dist/UI/Components/Forms/BasicForm.js +10 -1
  178. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  179. package/build/dist/UI/Components/Forms/BasicModelForm.js +2 -1
  180. package/build/dist/UI/Components/Forms/BasicModelForm.js.map +1 -1
  181. package/build/dist/UI/Components/Forms/ModelForm.js +4 -1
  182. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  183. package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js +33 -0
  184. package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js.map +1 -0
  185. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js +2 -1
  186. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  187. package/build/dist/Utils/Analytics.js +50 -2
  188. package/build/dist/Utils/Analytics.js.map +1 -1
  189. package/build/dist/Utils/Monitor/MonitorMetricType.js +37 -0
  190. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  191. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +318 -37
  192. package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -1
  193. package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js +87 -0
  194. package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js.map +1 -0
  195. package/package.json +1 -1
@@ -107,3 +107,190 @@ describe("Analytics Class", () => {
107
107
  expect(posthog.capture).not.toHaveBeenCalled();
108
108
  });
109
109
  });
110
+
111
+ describe("Analytics.toGA4EventName", () => {
112
+ /*
113
+ * GA4 event names must start with a letter and contain only letters, digits
114
+ * and underscores, with a maximum length of 40 characters.
115
+ */
116
+ const GA4_EVENT_NAME_REGEX: RegExp = /^[a-z][a-z0-9_]*$/;
117
+
118
+ it.each([
119
+ // Names product code sends today that GA4 would otherwise reject.
120
+ ["Page View: Project > Home", "page_view_project_home"],
121
+ ["FORM SUBMIT: Register", "form_submit_register"],
122
+ ["accounts/login", "accounts_login"],
123
+ ["accounts/register", "accounts_register"],
124
+ ["dashboard/home", "dashboard_home"],
125
+ ["dashboard/ai-copilot", "dashboard_ai_copilot"],
126
+ ["dashboard/billing/plan-changed", "dashboard_billing_plan_changed"],
127
+ [
128
+ "dashboard/home/getting-started-task",
129
+ "dashboard_home_getting_started_task",
130
+ ],
131
+ [
132
+ "dashboard/home/getting-started-dismissed",
133
+ "dashboard_home_getting_started_dismissed",
134
+ ],
135
+ // Leading and trailing separators are dropped rather than kept.
136
+ [" /accounts/login/ ", "accounts_login"],
137
+ // A name with no leading letter gets a prefix.
138
+ ["404 page not found", "event_404_page_not_found"],
139
+ ["_leading_underscore", "leading_underscore"],
140
+ // A name with nothing usable in it falls back entirely.
141
+ ["***", "event"],
142
+ ["", "event"],
143
+ // Casing is normalised.
144
+ ["testEvent", "testevent"],
145
+ ])("normalises %j to %j", (input: string, expected: string) => {
146
+ expect(Analytics.toGA4EventName(input)).toBe(expected);
147
+ });
148
+
149
+ it("truncates to 40 characters without leaving a trailing underscore", () => {
150
+ const normalized: string = Analytics.toGA4EventName(
151
+ "Page View: Project > Settings > Notification > Email",
152
+ );
153
+
154
+ expect(normalized).toBe("page_view_project_settings_notification");
155
+ expect(normalized.length).toBeLessThanOrEqual(40);
156
+ });
157
+
158
+ it("truncates a name that is exactly at the boundary", () => {
159
+ const normalized: string = Analytics.toGA4EventName(
160
+ "Page View: Project > Settings > Notifications > Email",
161
+ );
162
+
163
+ expect(normalized).toBe("page_view_project_settings_notifications");
164
+ expect(normalized).toHaveLength(40);
165
+ });
166
+
167
+ it("leaves already GA4-compatible revenue event names untouched", () => {
168
+ for (const eventName of Object.values(RevenueEventName)) {
169
+ expect(Analytics.toGA4EventName(eventName)).toBe(eventName);
170
+ }
171
+ });
172
+
173
+ it("always produces a GA4-valid name", () => {
174
+ const inputs: Array<string> = [
175
+ "Page View: Project > Home",
176
+ "FORM SUBMIT: Register",
177
+ "accounts/login",
178
+ "404",
179
+ "___",
180
+ "!!!",
181
+ "",
182
+ " ",
183
+ "a".repeat(200),
184
+ "1".repeat(200),
185
+ "Page View: " + "Very Long Breadcrumb > ".repeat(10),
186
+ ];
187
+
188
+ for (const input of inputs) {
189
+ const normalized: string = Analytics.toGA4EventName(input);
190
+
191
+ expect(normalized).toMatch(GA4_EVENT_NAME_REGEX);
192
+ expect(normalized.length).toBeGreaterThan(0);
193
+ expect(normalized.length).toBeLessThanOrEqual(40);
194
+ // Normalising an already-normalised name must be a no-op.
195
+ expect(Analytics.toGA4EventName(normalized)).toBe(normalized);
196
+ }
197
+ });
198
+ });
199
+
200
+ describe("Analytics dataLayer (GA4) branch", () => {
201
+ let dataLayer: Array<JSONObject>;
202
+
203
+ beforeEach(() => {
204
+ dataLayer = [];
205
+ (window as any).dataLayer = dataLayer;
206
+ });
207
+
208
+ afterEach(() => {
209
+ delete (window as any).dataLayer;
210
+ jest.clearAllMocks();
211
+ });
212
+
213
+ it("pushes a normalised event name while PostHog keeps the original", () => {
214
+ const analytics: Analytics = new Analytics(apiHost, apiKey);
215
+
216
+ analytics.capture("Page View: Project > Home");
217
+
218
+ expect(posthog.capture).toHaveBeenCalledWith(
219
+ "Page View: Project > Home",
220
+ undefined,
221
+ );
222
+ expect(dataLayer).toHaveLength(1);
223
+ expect(dataLayer[0]).toEqual({
224
+ event: "page_view_project_home",
225
+ eventCategory: "analytics",
226
+ eventAction: "Page View: Project > Home",
227
+ });
228
+ });
229
+
230
+ it("pushes to the dataLayer even when PostHog is not initialized", () => {
231
+ const analytics: Analytics = new Analytics("", "");
232
+
233
+ analytics.capture("FORM SUBMIT: Register");
234
+
235
+ expect(posthog.capture).not.toHaveBeenCalled();
236
+ expect(dataLayer).toHaveLength(1);
237
+ expect(dataLayer[0]).toMatchObject({
238
+ event: "form_submit_register",
239
+ eventAction: "FORM SUBMIT: Register",
240
+ });
241
+ });
242
+
243
+ it("includes event properties in the pushed payload", () => {
244
+ const analytics: Analytics = new Analytics(apiHost, apiKey);
245
+
246
+ analytics.capture("dashboard/billing/plan-changed", {
247
+ plan_name: "growth",
248
+ is_paid_conversion: true,
249
+ });
250
+
251
+ expect(dataLayer[0]).toEqual({
252
+ event: "dashboard_billing_plan_changed",
253
+ eventCategory: "analytics",
254
+ eventAction: "dashboard/billing/plan-changed",
255
+ plan_name: "growth",
256
+ is_paid_conversion: true,
257
+ });
258
+ });
259
+
260
+ it("does not let event properties override the GA4 event name", () => {
261
+ const analytics: Analytics = new Analytics(apiHost, apiKey);
262
+
263
+ analytics.capture("accounts/login", { event: "Not A Valid GA4 Name" });
264
+
265
+ expect(dataLayer[0]).toMatchObject({ event: "accounts_login" });
266
+ });
267
+
268
+ it("pushes revenue event names unchanged", () => {
269
+ const analytics: Analytics = new Analytics(apiHost, apiKey);
270
+
271
+ analytics.captureRevenueEvent(RevenueEventName.WorkspaceCreated, {
272
+ funnel_stage: RevenueFunnelStage.Activation,
273
+ project_id: "project-123",
274
+ });
275
+
276
+ expect(dataLayer[0]).toEqual({
277
+ event: RevenueEventName.WorkspaceCreated,
278
+ eventCategory: "analytics",
279
+ eventAction: RevenueEventName.WorkspaceCreated,
280
+ funnel_stage: RevenueFunnelStage.Activation,
281
+ project_id: "project-123",
282
+ event_schema_version: REVENUE_EVENT_SCHEMA_VERSION,
283
+ });
284
+ });
285
+
286
+ it("does not throw when window.dataLayer is not defined", () => {
287
+ delete (window as any).dataLayer;
288
+
289
+ const analytics: Analytics = new Analytics(apiHost, apiKey);
290
+
291
+ expect(() => {
292
+ analytics.capture("accounts/login");
293
+ }).not.toThrow();
294
+ expect(posthog.capture).toHaveBeenCalledWith("accounts/login", undefined);
295
+ });
296
+ });
@@ -398,3 +398,83 @@ describe("invariant: every probeable monitor type displays some metric", () => {
398
398
  expect(typesWithoutMetrics).toEqual([]);
399
399
  });
400
400
  });
401
+
402
+ /*
403
+ * The null-returning variant exists because over-time evaluation asks "is
404
+ * there a series behind this CheckOn?" for every filter it sees. It used to
405
+ * ask by calling the throwing variant inside a try/catch that swallowed the
406
+ * error and quietly compared the instantaneous value instead - so "evaluate
407
+ * over time" was a silent no-op for every CheckOn missing from the map.
408
+ */
409
+ describe("getMonitorMetricTypeByCheckOnOrNull", () => {
410
+ test("returns the same series as the throwing variant for mapped CheckOns", () => {
411
+ const mapped: Array<CheckOn> = [
412
+ CheckOn.ResponseTime,
413
+ CheckOn.ResponseStatusCode,
414
+ CheckOn.IsOnline,
415
+ CheckOn.CPUUsagePercent,
416
+ CheckOn.MemoryUsagePercent,
417
+ CheckOn.DiskUsagePercent,
418
+ CheckOn.PacketLossPercent,
419
+ CheckOn.Jitter,
420
+ CheckOn.PortDnsLookupTime,
421
+ CheckOn.PortTcpConnectTime,
422
+ ];
423
+
424
+ for (const checkOn of mapped) {
425
+ expect(
426
+ MonitorMetricTypeUtil.getMonitorMetricTypeByCheckOnOrNull(checkOn),
427
+ ).toBe(MonitorMetricTypeUtil.getMonitorMeticTypeByCheckOn(checkOn));
428
+ }
429
+ });
430
+
431
+ /*
432
+ * DNS, SNMP and External Status Page probes write into the shared online
433
+ * and response-time series like every other probe check, and the criteria
434
+ * UI advertises all six of these as over-time capable - but none of them
435
+ * had a mapping, so the window could never be read.
436
+ */
437
+ test("maps the DNS / SNMP / status page online checks onto the online series", () => {
438
+ for (const checkOn of [
439
+ CheckOn.DnsIsOnline,
440
+ CheckOn.SnmpIsOnline,
441
+ CheckOn.ExternalStatusPageIsOnline,
442
+ ]) {
443
+ expect(
444
+ MonitorMetricTypeUtil.getMonitorMetricTypeByCheckOnOrNull(checkOn),
445
+ ).toBe(MonitorMetricType.IsOnline);
446
+ }
447
+ });
448
+
449
+ test("maps their response time checks onto the response time series", () => {
450
+ for (const checkOn of [
451
+ CheckOn.DnsResponseTime,
452
+ CheckOn.SnmpResponseTime,
453
+ CheckOn.ExternalStatusPageResponseTime,
454
+ ]) {
455
+ expect(
456
+ MonitorMetricTypeUtil.getMonitorMetricTypeByCheckOnOrNull(checkOn),
457
+ ).toBe(MonitorMetricType.ResponseTime);
458
+ }
459
+ });
460
+
461
+ test("returns null instead of throwing for CheckOns no series records", () => {
462
+ for (const checkOn of [
463
+ CheckOn.ResponseBody,
464
+ CheckOn.ResponseHeader,
465
+ CheckOn.IsRequestTimeout,
466
+ CheckOn.JavaScriptExpression,
467
+ CheckOn.IncomingRequest,
468
+ ]) {
469
+ expect(
470
+ MonitorMetricTypeUtil.getMonitorMetricTypeByCheckOnOrNull(checkOn),
471
+ ).toBeNull();
472
+ }
473
+ });
474
+
475
+ test("the throwing variant still throws for those CheckOns", () => {
476
+ expect(() => {
477
+ MonitorMetricTypeUtil.getMonitorMeticTypeByCheckOn(CheckOn.ResponseBody);
478
+ }).toThrow();
479
+ });
480
+ });