@oneuptime/common 12.0.19 → 12.0.21

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 (124) hide show
  1. package/Models/AnalyticsModels/ChangeEvent.ts +331 -0
  2. package/Models/AnalyticsModels/Index.ts +2 -0
  3. package/Server/API/EnterpriseLicenseAPI.ts +94 -19
  4. package/Server/EnvironmentConfig.ts +15 -5
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +42 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +0 -2
  7. package/Server/Services/ChangeEventService.ts +11 -0
  8. package/Server/Services/EnterpriseLicenseService.ts +3 -3
  9. package/Server/Services/Index.ts +2 -0
  10. package/Server/Services/ProjectService.ts +104 -0
  11. package/Server/Utils/Attribution.ts +11 -11
  12. package/Server/Utils/Marketing/MarketingEventWebhook.ts +4 -5
  13. package/Server/Utils/Response.ts +2 -2
  14. package/Server/Utils/StartServer.ts +2 -2
  15. package/Server/Utils/Telemetry/ChangeEventRow.ts +228 -0
  16. package/Server/Views/Partials/AnalyticsConsent.ejs +441 -0
  17. package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +465 -0
  18. package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +366 -0
  19. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +408 -0
  20. package/Tests/App/Dashboard/EventOverlayHook.test.tsx +249 -0
  21. package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +474 -0
  22. package/Tests/App/Dashboard/InvestigationArtifacts.test.tsx +315 -0
  23. package/Tests/App/Dashboard/InvestigationDrawer.test.tsx +258 -0
  24. package/Tests/App/Dashboard/MetricChartsCompare.test.tsx +171 -0
  25. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +368 -0
  26. package/Tests/App/Dashboard/MetricViewCompare.test.tsx +233 -0
  27. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +763 -0
  28. package/Tests/Server/Infrastructure/Postgres/AddUserSlackAndMicrosoftTeamsMigration.test.ts +236 -0
  29. package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +138 -0
  30. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +516 -0
  31. package/Tests/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.test.ts +224 -0
  32. package/Tests/Server/Utils/Attribution.test.ts +4 -5
  33. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +2 -2
  34. package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.test.ts +300 -0
  35. package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +53 -0
  36. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +241 -0
  37. package/Tests/UI/Components/Charts/ChartGhostSeries.test.tsx +103 -0
  38. package/Tests/UI/Components/Charts/ChartTooltipSorted.test.tsx +129 -0
  39. package/Tests/UI/Components/Charts/TooltipEntries.test.ts +191 -0
  40. package/Tests/UI/Components/Detail/DetailFieldErrors.test.tsx +347 -0
  41. package/Tests/UI/Components/Forms/BasicFormStepValidation.test.tsx +392 -0
  42. package/Tests/UI/Components/Forms/ValidationFormSteps.test.ts +549 -0
  43. package/Tests/Utils/EnterpriseLicenseSync.test.ts +562 -0
  44. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +13 -2
  45. package/Types/Analytics/RevenueEvent.ts +1 -0
  46. package/Types/AnalyticsDatabase/AnalyticsTableName.ts +7 -0
  47. package/Types/Marketing/Attribution.ts +9 -15
  48. package/Types/Marketing/MarketingEvent.ts +19 -12
  49. package/UI/Components/Charts/Area/AreaChart.tsx +23 -1
  50. package/UI/Components/Charts/Bar/BarChart.tsx +8 -1
  51. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +16 -1
  52. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +208 -97
  53. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +26 -3
  54. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +232 -118
  55. package/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.ts +120 -0
  56. package/UI/Components/Charts/Line/LineChart.tsx +23 -1
  57. package/UI/Components/Detail/Detail.tsx +18 -1
  58. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +244 -0
  59. package/Utils/Metrics/MetricExplorerUrl.ts +119 -5
  60. package/Utils/Telemetry/CrossSignalScope.ts +19 -10
  61. package/build/dist/Models/AnalyticsModels/ChangeEvent.js +298 -0
  62. package/build/dist/Models/AnalyticsModels/ChangeEvent.js.map +1 -0
  63. package/build/dist/Models/AnalyticsModels/Index.js +2 -0
  64. package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
  65. package/build/dist/Server/API/EnterpriseLicenseAPI.js +66 -12
  66. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  67. package/build/dist/Server/EnvironmentConfig.js +14 -5
  68. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  69. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +39 -0
  70. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -1
  71. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +0 -2
  72. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  73. package/build/dist/Server/Services/ChangeEventService.js +9 -0
  74. package/build/dist/Server/Services/ChangeEventService.js.map +1 -0
  75. package/build/dist/Server/Services/EnterpriseLicenseService.js +3 -3
  76. package/build/dist/Server/Services/Index.js +2 -0
  77. package/build/dist/Server/Services/Index.js.map +1 -1
  78. package/build/dist/Server/Services/ProjectService.js +88 -0
  79. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  80. package/build/dist/Server/Utils/Attribution.js +6 -6
  81. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +4 -5
  82. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -1
  83. package/build/dist/Server/Utils/Response.js +2 -2
  84. package/build/dist/Server/Utils/Response.js.map +1 -1
  85. package/build/dist/Server/Utils/StartServer.js +2 -2
  86. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  87. package/build/dist/Server/Utils/Telemetry/ChangeEventRow.js +150 -0
  88. package/build/dist/Server/Utils/Telemetry/ChangeEventRow.js.map +1 -0
  89. package/build/dist/Types/Analytics/RevenueEvent.js +1 -0
  90. package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -1
  91. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +7 -0
  92. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
  93. package/build/dist/Types/Marketing/Attribution.js +9 -15
  94. package/build/dist/Types/Marketing/Attribution.js.map +1 -1
  95. package/build/dist/Types/Marketing/MarketingEvent.js +12 -4
  96. package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -1
  97. package/build/dist/UI/Components/Charts/Area/AreaChart.js +10 -2
  98. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  99. package/build/dist/UI/Components/Charts/Bar/BarChart.js +9 -1
  100. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  101. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +9 -1
  102. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  103. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +114 -51
  104. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  105. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +35 -23
  106. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  107. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +122 -55
  108. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  109. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js +86 -0
  110. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js.map +1 -0
  111. package/build/dist/UI/Components/Charts/Line/LineChart.js +10 -2
  112. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  113. package/build/dist/UI/Components/Detail/Detail.js +17 -1
  114. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  115. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +174 -0
  116. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +1 -0
  117. package/build/dist/Utils/Metrics/MetricExplorerUrl.js +81 -0
  118. package/build/dist/Utils/Metrics/MetricExplorerUrl.js.map +1 -1
  119. package/build/dist/Utils/Telemetry/CrossSignalScope.js +14 -8
  120. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  121. package/package.json +1 -1
  122. package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +0 -117
  123. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +0 -106
  124. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +0 -1
@@ -0,0 +1,233 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import { cleanup, render, waitFor } from "@testing-library/react";
11
+ import * as React from "react";
12
+ import getJestMockFunction, { MockFunction } from "../../MockType";
13
+
14
+ /*
15
+ * Compare-to-previous-period: MetricView must fetch the SAME queries a
16
+ * second time over the window shifted back by exactly one window width,
17
+ * hand both result sets to MetricCharts, and degrade to live-only when
18
+ * the shifted fetch fails.
19
+ */
20
+
21
+ const fetchResultsMock: MockFunction = getJestMockFunction();
22
+ const metricChartsMock: MockFunction = getJestMockFunction();
23
+
24
+ jest.mock(
25
+ "../../../../App/FeatureSet/Dashboard/src/Components/Metrics/Utils/Metrics",
26
+ () => {
27
+ return {
28
+ __esModule: true,
29
+ default: {
30
+ fetchResults: (...args: Array<any>) => {
31
+ return fetchResultsMock(...args);
32
+ },
33
+ getMetricTypes: () => {
34
+ return Promise.resolve([]);
35
+ },
36
+ loadAllMetricsTypes: () => {
37
+ return Promise.resolve({
38
+ metricTypes: [],
39
+ telemetryServices: [],
40
+ });
41
+ },
42
+ },
43
+ };
44
+ },
45
+ );
46
+
47
+ jest.mock(
48
+ "../../../../App/FeatureSet/Dashboard/src/Components/Metrics/MetricCharts",
49
+ () => {
50
+ return {
51
+ __esModule: true,
52
+ default: (props: Record<string, unknown>): React.ReactElement => {
53
+ metricChartsMock(props);
54
+ return React.createElement("div", {
55
+ "data-testid": "metric-charts",
56
+ });
57
+ },
58
+ };
59
+ },
60
+ );
61
+
62
+ import MetricView from "../../../../App/FeatureSet/Dashboard/src/Components/Metrics/MetricView";
63
+ import InBetween from "../../../Types/BaseDatabase/InBetween";
64
+ import MetricsAggregationType from "../../../Types/Metrics/MetricsAggregationType";
65
+ import MetricViewData from "../../../Types/Metrics/MetricViewData";
66
+
67
+ const WINDOW_START: Date = new Date("2026-08-20T10:00:00.000Z");
68
+ const WINDOW_END: Date = new Date("2026-08-20T11:00:00.000Z");
69
+ const WINDOW_MS: number = WINDOW_END.getTime() - WINDOW_START.getTime();
70
+
71
+ function buildData(): MetricViewData {
72
+ return {
73
+ queryConfigs: [
74
+ {
75
+ metricAliasData: { metricVariable: "a" },
76
+ metricQueryData: {
77
+ filterData: {
78
+ metricName: "cpu.usage",
79
+ attributes: {},
80
+ aggegationType: MetricsAggregationType.Avg,
81
+ },
82
+ },
83
+ },
84
+ ],
85
+ formulaConfigs: [],
86
+ startAndEndDate: new InBetween<Date>(WINDOW_START, WINDOW_END),
87
+ } as unknown as MetricViewData;
88
+ }
89
+
90
+ interface CapturedFetch {
91
+ metricViewData: MetricViewData;
92
+ }
93
+
94
+ beforeEach(() => {
95
+ fetchResultsMock.mockReset();
96
+ metricChartsMock.mockReset();
97
+ fetchResultsMock.mockReturnValue(
98
+ Promise.resolve([{ data: [], truncated: false }]),
99
+ );
100
+ });
101
+
102
+ afterEach(() => {
103
+ cleanup();
104
+ });
105
+
106
+ describe("MetricView compare with previous period", () => {
107
+ test("fetches the shifted window alongside the live one and hands both down", async () => {
108
+ render(
109
+ <MetricView
110
+ data={buildData()}
111
+ hideQueryElements={true}
112
+ hideStartAndEndDate={true}
113
+ compareWithPreviousPeriod={true}
114
+ onChange={() => {
115
+ // not exercised
116
+ }}
117
+ />,
118
+ );
119
+
120
+ await waitFor(() => {
121
+ expect(fetchResultsMock.mock.calls.length).toBeGreaterThanOrEqual(2);
122
+ });
123
+
124
+ const windows: Array<[number, number]> = fetchResultsMock.mock.calls.map(
125
+ (call: Array<unknown>): [number, number] => {
126
+ const args: CapturedFetch = call[0] as CapturedFetch;
127
+ return [
128
+ args.metricViewData.startAndEndDate!.startValue.getTime(),
129
+ args.metricViewData.startAndEndDate!.endValue.getTime(),
130
+ ];
131
+ },
132
+ );
133
+
134
+ /*
135
+ * One live window and one shifted back by EXACTLY the window width
136
+ * (bucket-congruent modulo the shift). Alignment may floor the live
137
+ * start onto the bucket grid, so assert the relationship between the
138
+ * two fetches rather than raw instants.
139
+ */
140
+ const [liveWindow, previousWindow] = windows as [
141
+ [number, number],
142
+ [number, number],
143
+ ];
144
+ const liveWidth: number = liveWindow[1] - liveWindow[0];
145
+ expect(previousWindow[0]).toBe(liveWindow[0] - liveWidth);
146
+ expect(previousWindow[1]).toBe(liveWindow[1] - liveWidth);
147
+ expect(liveWidth).toBeGreaterThanOrEqual(WINDOW_MS);
148
+
149
+ await waitFor(() => {
150
+ const lastProps: Record<string, unknown> = metricChartsMock.mock.calls[
151
+ metricChartsMock.mock.calls.length - 1
152
+ ]?.[0] as Record<string, unknown>;
153
+ expect(lastProps["metricResultsPrevious"]).toBeTruthy();
154
+ expect(lastProps["compareOffsetMs"]).toBe(liveWidth);
155
+ });
156
+ });
157
+
158
+ test("compare off means exactly one fetch and no ghost props", async () => {
159
+ render(
160
+ <MetricView
161
+ data={buildData()}
162
+ hideQueryElements={true}
163
+ hideStartAndEndDate={true}
164
+ onChange={() => {
165
+ // not exercised
166
+ }}
167
+ />,
168
+ );
169
+
170
+ await waitFor(() => {
171
+ expect(fetchResultsMock).toHaveBeenCalled();
172
+ });
173
+ // Let any (wrong) shifted fetch get scheduled before asserting.
174
+ await new Promise((resolve: (value: unknown) => void) => {
175
+ setTimeout(resolve, 30);
176
+ });
177
+ /*
178
+ * MetricView may legitimately refetch the LIVE window more than once
179
+ * on mount — what compare-off forbids is any fetch of a SHIFTED
180
+ * (earlier) window.
181
+ */
182
+ const anyShiftedFetch: boolean = fetchResultsMock.mock.calls.some(
183
+ (call: Array<unknown>): boolean => {
184
+ const args: CapturedFetch = call[0] as CapturedFetch;
185
+ return (
186
+ args.metricViewData.startAndEndDate!.endValue.getTime() <=
187
+ WINDOW_START.getTime()
188
+ );
189
+ },
190
+ );
191
+ expect(anyShiftedFetch).toBe(false);
192
+ const lastProps: Record<string, unknown> = metricChartsMock.mock.calls[
193
+ metricChartsMock.mock.calls.length - 1
194
+ ]?.[0] as Record<string, unknown>;
195
+ expect(lastProps["metricResultsPrevious"]).toBeUndefined();
196
+ });
197
+
198
+ test("a failed shifted fetch degrades to live-only, never an error", async () => {
199
+ fetchResultsMock.mockImplementation(
200
+ (args: { metricViewData: MetricViewData }) => {
201
+ const start: number =
202
+ args.metricViewData.startAndEndDate!.startValue.getTime();
203
+ // The shifted (earlier) fetch fails; the live one succeeds.
204
+ if (start < WINDOW_START.getTime()) {
205
+ return Promise.reject(new Error("clickhouse hiccup"));
206
+ }
207
+ return Promise.resolve([{ data: [], truncated: false }]);
208
+ },
209
+ );
210
+
211
+ render(
212
+ <MetricView
213
+ data={buildData()}
214
+ hideQueryElements={true}
215
+ hideStartAndEndDate={true}
216
+ compareWithPreviousPeriod={true}
217
+ onChange={() => {
218
+ // not exercised
219
+ }}
220
+ />,
221
+ );
222
+
223
+ await waitFor(() => {
224
+ expect(metricChartsMock).toHaveBeenCalled();
225
+ });
226
+
227
+ const lastProps: Record<string, unknown> = metricChartsMock.mock.calls[
228
+ metricChartsMock.mock.calls.length - 1
229
+ ]?.[0] as Record<string, unknown>;
230
+ expect(lastProps["metricResults"]).toBeTruthy();
231
+ expect(lastProps["metricResultsPrevious"]).toBeUndefined();
232
+ });
233
+ });