@oneuptime/common 11.5.13 → 11.6.1

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 (156) hide show
  1. package/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +41 -24
  2. package/Models/AnalyticsModels/MetricBaselineHourly.ts +2 -0
  3. package/Models/AnalyticsModels/MetricItemAggMV1m.ts +5 -2
  4. package/Models/AnalyticsModels/MetricItemAggMV1mByContainer.ts +5 -2
  5. package/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.ts +5 -2
  6. package/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +5 -2
  7. package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +5 -2
  8. package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
  9. package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
  10. package/Models/DatabaseModels/GlobalConfig.ts +76 -0
  11. package/Server/API/EnterpriseLicenseAPI.ts +57 -0
  12. package/Server/API/GlobalConfigAPI.ts +62 -1
  13. package/Server/EnvironmentConfig.ts +22 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  17. package/Server/Services/AnalyticsDatabaseService.ts +61 -46
  18. package/Server/Services/DatabaseService.ts +66 -1
  19. package/Server/Services/WorkflowService.ts +100 -39
  20. package/Server/Types/Workflow/Components/Schedule.ts +57 -32
  21. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
  22. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +41 -4
  23. package/Server/Utils/Express.ts +13 -0
  24. package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
  25. package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
  26. package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
  27. package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
  28. package/Tests/Models/AnalyticsModels/AnalyticsBaseModel.test.ts +122 -0
  29. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
  30. package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
  31. package/Tests/Server/Services/WorkflowService.test.ts +308 -0
  32. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +164 -3
  33. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +99 -0
  34. package/Tests/Server/Utils/Attribution.test.ts +203 -0
  35. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
  36. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
  37. package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
  38. package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
  39. package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
  40. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
  41. package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
  42. package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
  43. package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
  44. package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
  45. package/Tests/Types/HashCode.test.ts +41 -0
  46. package/Tests/Types/Log/LogQueryParser.test.ts +186 -0
  47. package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
  48. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
  49. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
  50. package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
  51. package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
  52. package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
  53. package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
  54. package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
  55. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
  56. package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
  57. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
  58. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
  59. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
  60. package/Tests/Utils/Array.test.ts +156 -0
  61. package/Tests/Utils/CronTab.test.ts +199 -0
  62. package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +197 -0
  63. package/Tests/Utils/ModelImportExport.test.ts +101 -0
  64. package/Tests/Utils/Number.test.ts +179 -0
  65. package/Tests/Utils/ValueFormatter.test.ts +305 -0
  66. package/Tests/Utils/VersionUtil.test.ts +348 -0
  67. package/Tests/jest.setup.ts +1 -0
  68. package/Types/AnalyticsDatabase/TableColumn.ts +23 -0
  69. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
  70. package/Types/Monitor/MonitorStep.ts +24 -4
  71. package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
  72. package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
  73. package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
  74. package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
  75. package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
  76. package/UI/Utils/NotificationMethodUtil.ts +310 -0
  77. package/Utils/CronTab.ts +823 -0
  78. package/Utils/ModelImportExport.ts +21 -6
  79. package/Utils/VersionUtil.ts +260 -0
  80. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +25 -20
  81. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
  82. package/build/dist/Models/AnalyticsModels/MetricBaselineHourly.js +2 -0
  83. package/build/dist/Models/AnalyticsModels/MetricBaselineHourly.js.map +1 -1
  84. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1m.js +5 -2
  85. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1m.js.map +1 -1
  86. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByContainer.js +5 -2
  87. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByContainer.js.map +1 -1
  88. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.js +5 -2
  89. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByHostV2.js.map +1 -1
  90. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +5 -2
  91. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js.map +1 -1
  92. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +5 -2
  93. package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js.map +1 -1
  94. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
  95. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  96. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
  97. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
  98. package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
  99. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  100. package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
  101. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  102. package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
  103. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  104. package/build/dist/Server/EnvironmentConfig.js +17 -0
  105. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.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/Services/AnalyticsDatabaseService.js +53 -17
  113. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  114. package/build/dist/Server/Services/DatabaseService.js +53 -1
  115. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  116. package/build/dist/Server/Services/WorkflowService.js +80 -30
  117. package/build/dist/Server/Services/WorkflowService.js.map +1 -1
  118. package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
  119. package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
  120. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
  121. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
  122. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +29 -4
  123. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  124. package/build/dist/Server/Utils/Express.js +12 -0
  125. package/build/dist/Server/Utils/Express.js.map +1 -1
  126. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
  127. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
  128. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
  129. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
  130. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
  131. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
  132. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
  133. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
  134. package/build/dist/Types/AnalyticsDatabase/TableColumn.js +7 -0
  135. package/build/dist/Types/AnalyticsDatabase/TableColumn.js.map +1 -1
  136. package/build/dist/Types/Monitor/MonitorStep.js +20 -4
  137. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  138. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
  139. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
  140. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
  141. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  142. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
  143. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  144. package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
  145. package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
  146. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
  147. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
  148. package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
  149. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
  150. package/build/dist/Utils/CronTab.js +609 -0
  151. package/build/dist/Utils/CronTab.js.map +1 -0
  152. package/build/dist/Utils/ModelImportExport.js +20 -6
  153. package/build/dist/Utils/ModelImportExport.js.map +1 -1
  154. package/build/dist/Utils/VersionUtil.js +193 -0
  155. package/build/dist/Utils/VersionUtil.js.map +1 -0
  156. package/package.json +1 -1
@@ -0,0 +1,348 @@
1
+ import VersionUtil, { ParsedVersion } from "../../Utils/VersionUtil";
2
+
3
+ describe("VersionUtil", () => {
4
+ describe("normalize", () => {
5
+ it("strips a leading v from a GitHub tag name", () => {
6
+ expect(VersionUtil.normalize("v11.5.13")).toBe("11.5.13");
7
+ expect(VersionUtil.normalize("V11.5.13")).toBe("11.5.13");
8
+ });
9
+
10
+ it("trims surrounding whitespace", () => {
11
+ expect(VersionUtil.normalize(" 11.5.13\n")).toBe("11.5.13");
12
+ });
13
+
14
+ it("returns an empty string for non-strings", () => {
15
+ expect(VersionUtil.normalize(null)).toBe("");
16
+ expect(VersionUtil.normalize(undefined)).toBe("");
17
+ expect(VersionUtil.normalize(11)).toBe("");
18
+ expect(VersionUtil.normalize({ version: "1.0.0" })).toBe("");
19
+ });
20
+
21
+ it("caps absurdly long input", () => {
22
+ expect(VersionUtil.normalize("1".repeat(5000)).length).toBe(100);
23
+ });
24
+ });
25
+
26
+ describe("parse", () => {
27
+ it("parses a plain release", () => {
28
+ const parsed: ParsedVersion | null = VersionUtil.parse("11.5.13");
29
+
30
+ expect(parsed).toEqual({
31
+ major: 11,
32
+ minor: 5,
33
+ patch: 13,
34
+ prerelease: null,
35
+ });
36
+ });
37
+
38
+ it("parses a prerelease", () => {
39
+ const parsed: ParsedVersion | null = VersionUtil.parse("11.6.0-rc.1");
40
+
41
+ expect(parsed).toEqual({
42
+ major: 11,
43
+ minor: 6,
44
+ patch: 0,
45
+ prerelease: "rc.1",
46
+ });
47
+ });
48
+
49
+ it("discards build metadata, which takes no part in precedence", () => {
50
+ expect(VersionUtil.parse("11.5.13+abc123")?.prerelease).toBeNull();
51
+ expect(VersionUtil.compare("11.5.13+abc123", "11.5.13")).toBe(0);
52
+ });
53
+
54
+ it("returns null for partial, empty and garbage versions", () => {
55
+ const invalid: Array<unknown> = [
56
+ "11.5",
57
+ "11",
58
+ "",
59
+ " ",
60
+ "latest",
61
+ "unknown",
62
+ "v",
63
+ "1.0.0.0",
64
+ "-1.0.0",
65
+ "1.0.0-",
66
+ null,
67
+ undefined,
68
+ 11.5,
69
+ {},
70
+ [],
71
+ ];
72
+
73
+ for (const value of invalid) {
74
+ expect(VersionUtil.parse(value)).toBeNull();
75
+ }
76
+ });
77
+
78
+ it("rejects numbers too large to be a real version", () => {
79
+ expect(VersionUtil.parse("99999999999.0.0")).toBeNull();
80
+ });
81
+
82
+ it("reports validity", () => {
83
+ expect(VersionUtil.isValid("11.5.13")).toBe(true);
84
+ expect(VersionUtil.isValid("unknown")).toBe(false);
85
+ });
86
+ });
87
+
88
+ describe("canonicalize", () => {
89
+ it("rebuilds the version from its parts", () => {
90
+ expect(VersionUtil.canonicalize("11.5.13")).toBe("11.5.13");
91
+ expect(VersionUtil.canonicalize("v11.5.13")).toBe("11.5.13");
92
+ expect(VersionUtil.canonicalize(" 11.6.0-rc.1 ")).toBe("11.6.0-rc.1");
93
+ });
94
+
95
+ /*
96
+ * A caller that validates with isValid() and then stores the string it
97
+ * passed in can end up storing "v11.5.13" for the input "vv11.5.13",
98
+ * because normalize() strips one "v" per call and isValid() normalizes a
99
+ * second time. canonicalize is the fix: it rebuilds from a single parse,
100
+ * so a doubled prefix is simply not a version and the result never carries
101
+ * a "v" for the UIs to double up on.
102
+ */
103
+ it("never returns a value that still carries a v prefix", () => {
104
+ expect(VersionUtil.canonicalize("vv11.5.13")).toBeNull();
105
+ expect(VersionUtil.canonicalize("vV11.5.13")).toBeNull();
106
+
107
+ for (const value of ["11.5.13", "v11.5.13", " V11.5.13 "]) {
108
+ expect(VersionUtil.canonicalize(value)).toBe("11.5.13");
109
+ }
110
+ });
111
+
112
+ it("drops build metadata, which takes no part in precedence", () => {
113
+ expect(VersionUtil.canonicalize("11.5.13+abc123")).toBe("11.5.13");
114
+ });
115
+
116
+ it("is idempotent", () => {
117
+ const once: string | null = VersionUtil.canonicalize("v11.5.13");
118
+
119
+ expect(VersionUtil.canonicalize(once)).toBe(once);
120
+ });
121
+
122
+ it("returns null for anything that is not a version", () => {
123
+ expect(VersionUtil.canonicalize("unknown")).toBeNull();
124
+ expect(VersionUtil.canonicalize("")).toBeNull();
125
+ expect(VersionUtil.canonicalize(null)).toBeNull();
126
+ expect(VersionUtil.canonicalize("vvv1.2.3")).toBeNull();
127
+ });
128
+ });
129
+
130
+ describe("compare", () => {
131
+ it("orders by major, then minor, then patch", () => {
132
+ expect(VersionUtil.compare("10.0.0", "11.0.0")).toBe(-1);
133
+ expect(VersionUtil.compare("11.4.0", "11.5.0")).toBe(-1);
134
+ expect(VersionUtil.compare("11.5.12", "11.5.13")).toBe(-1);
135
+ expect(VersionUtil.compare("11.5.13", "11.5.12")).toBe(1);
136
+ expect(VersionUtil.compare("2.0.0", "10.0.0")).toBe(-1);
137
+ });
138
+
139
+ it("treats equal versions as equal regardless of a v prefix", () => {
140
+ expect(VersionUtil.compare("11.5.13", "v11.5.13")).toBe(0);
141
+ });
142
+
143
+ it("ranks a prerelease below the release of the same triple", () => {
144
+ expect(VersionUtil.compare("11.6.0-rc.1", "11.6.0")).toBe(-1);
145
+ expect(VersionUtil.compare("11.6.0", "11.6.0-rc.1")).toBe(1);
146
+ });
147
+
148
+ it("orders prerelease identifiers by semver precedence", () => {
149
+ expect(VersionUtil.compare("1.0.0-alpha", "1.0.0-alpha.1")).toBe(-1);
150
+ expect(VersionUtil.compare("1.0.0-alpha.1", "1.0.0-alpha.beta")).toBe(-1);
151
+ expect(VersionUtil.compare("1.0.0-alpha.beta", "1.0.0-beta")).toBe(-1);
152
+ expect(VersionUtil.compare("1.0.0-beta.2", "1.0.0-beta.11")).toBe(-1);
153
+ expect(VersionUtil.compare("1.0.0-rc.1", "1.0.0-rc.1")).toBe(0);
154
+ });
155
+
156
+ it("returns null when either side cannot be parsed", () => {
157
+ expect(VersionUtil.compare("unknown", "11.5.13")).toBeNull();
158
+ expect(VersionUtil.compare("11.5.13", "")).toBeNull();
159
+ expect(VersionUtil.compare(null, undefined)).toBeNull();
160
+ });
161
+
162
+ it("is antisymmetric across a range of pairs", () => {
163
+ const versions: Array<string> = [
164
+ "1.0.0",
165
+ "1.0.1",
166
+ "1.1.0",
167
+ "2.0.0",
168
+ "11.5.13",
169
+ ];
170
+
171
+ for (const a of versions) {
172
+ for (const b of versions) {
173
+ const forward: number = VersionUtil.compare(a, b) as number;
174
+ const reverse: number = VersionUtil.compare(b, a) as number;
175
+
176
+ // `|| 0` keeps the negation of 0 from producing -0, which toBe rejects.
177
+ expect(forward).toBe(-reverse || 0);
178
+ }
179
+ }
180
+ });
181
+ });
182
+
183
+ describe("isUpdateAvailable", () => {
184
+ it("is true only when the latest release is strictly newer", () => {
185
+ expect(
186
+ VersionUtil.isUpdateAvailable({
187
+ currentVersion: "11.5.13",
188
+ latestVersion: "11.6.0",
189
+ }),
190
+ ).toBe(true);
191
+
192
+ expect(
193
+ VersionUtil.isUpdateAvailable({
194
+ currentVersion: "11.5.13",
195
+ latestVersion: "v11.5.14",
196
+ }),
197
+ ).toBe(true);
198
+ });
199
+
200
+ it("is false when the installation is up to date", () => {
201
+ expect(
202
+ VersionUtil.isUpdateAvailable({
203
+ currentVersion: "11.5.13",
204
+ latestVersion: "11.5.13",
205
+ }),
206
+ ).toBe(false);
207
+ });
208
+
209
+ it("never suggests a downgrade for a build ahead of the release", () => {
210
+ expect(
211
+ VersionUtil.isUpdateAvailable({
212
+ currentVersion: "11.6.0",
213
+ latestVersion: "11.5.13",
214
+ }),
215
+ ).toBe(false);
216
+ });
217
+
218
+ it("never nudges a stable installation onto a prerelease", () => {
219
+ expect(
220
+ VersionUtil.isUpdateAvailable({
221
+ currentVersion: "11.5.13",
222
+ latestVersion: "11.6.0-beta.1",
223
+ }),
224
+ ).toBe(false);
225
+ });
226
+
227
+ it("does tell a release candidate about the matching stable release", () => {
228
+ expect(
229
+ VersionUtil.isUpdateAvailable({
230
+ currentVersion: "11.6.0-rc.1",
231
+ latestVersion: "11.6.0",
232
+ }),
233
+ ).toBe(true);
234
+ });
235
+
236
+ it("is false when the latest release is a prerelease of a new major", () => {
237
+ expect(
238
+ VersionUtil.isUpdateAvailable({
239
+ currentVersion: "11.5.13",
240
+ latestVersion: "12.0.0-rc.1",
241
+ }),
242
+ ).toBe(false);
243
+ });
244
+
245
+ it("is false whenever either version is unknown or missing", () => {
246
+ const unknownValues: Array<unknown> = [
247
+ "unknown",
248
+ "",
249
+ null,
250
+ undefined,
251
+ "latest",
252
+ 42,
253
+ ];
254
+
255
+ for (const value of unknownValues) {
256
+ expect(
257
+ VersionUtil.isUpdateAvailable({
258
+ currentVersion: value,
259
+ latestVersion: "11.6.0",
260
+ }),
261
+ ).toBe(false);
262
+
263
+ expect(
264
+ VersionUtil.isUpdateAvailable({
265
+ currentVersion: "11.5.13",
266
+ latestVersion: value,
267
+ }),
268
+ ).toBe(false);
269
+ }
270
+ });
271
+ });
272
+
273
+ describe("isMajorUpgrade", () => {
274
+ it("is true only when the update crosses a major version", () => {
275
+ expect(
276
+ VersionUtil.isMajorUpgrade({
277
+ currentVersion: "11.5.13",
278
+ latestVersion: "12.0.0",
279
+ }),
280
+ ).toBe(true);
281
+
282
+ expect(
283
+ VersionUtil.isMajorUpgrade({
284
+ currentVersion: "10.9.9",
285
+ latestVersion: "12.0.0",
286
+ }),
287
+ ).toBe(true);
288
+ });
289
+
290
+ it("is false for a minor or patch update", () => {
291
+ expect(
292
+ VersionUtil.isMajorUpgrade({
293
+ currentVersion: "11.5.13",
294
+ latestVersion: "11.6.0",
295
+ }),
296
+ ).toBe(false);
297
+
298
+ expect(
299
+ VersionUtil.isMajorUpgrade({
300
+ currentVersion: "11.5.13",
301
+ latestVersion: "11.5.14",
302
+ }),
303
+ ).toBe(false);
304
+ });
305
+
306
+ it("is false when there is no update to take at all", () => {
307
+ expect(
308
+ VersionUtil.isMajorUpgrade({
309
+ currentVersion: "12.0.0",
310
+ latestVersion: "12.0.0",
311
+ }),
312
+ ).toBe(false);
313
+
314
+ // Ahead of the release — a new major behind us is still not an upgrade.
315
+ expect(
316
+ VersionUtil.isMajorUpgrade({
317
+ currentVersion: "12.1.0",
318
+ latestVersion: "11.9.9",
319
+ }),
320
+ ).toBe(false);
321
+ });
322
+
323
+ it("is false when the new major is only a prerelease", () => {
324
+ expect(
325
+ VersionUtil.isMajorUpgrade({
326
+ currentVersion: "11.5.13",
327
+ latestVersion: "12.0.0-rc.1",
328
+ }),
329
+ ).toBe(false);
330
+ });
331
+
332
+ it("is false whenever either version is unknown or missing", () => {
333
+ expect(
334
+ VersionUtil.isMajorUpgrade({
335
+ currentVersion: "unknown",
336
+ latestVersion: "12.0.0",
337
+ }),
338
+ ).toBe(false);
339
+
340
+ expect(
341
+ VersionUtil.isMajorUpgrade({
342
+ currentVersion: "11.5.13",
343
+ latestVersion: null,
344
+ }),
345
+ ).toBe(false);
346
+ });
347
+ });
348
+ });
@@ -1,4 +1,5 @@
1
1
  import "@testing-library/jest-dom";
2
+ import { jest } from "@jest/globals";
2
3
  import { TextEncoder, TextDecoder } from "util";
3
4
  import { webcrypto, randomUUID } from "crypto";
4
5
 
@@ -213,6 +213,27 @@ export default class AnalyticsTableColumn {
213
213
  this._aggregateFunctionDefinition = v;
214
214
  }
215
215
 
216
+ /*
217
+ * Optional ClickHouse SimpleAggregateFunction wrapper for scalar columns.
218
+ *
219
+ * Unlike AggregateFunction, a SimpleAggregateFunction keeps the scalar's
220
+ * ordinary wire/query type (for example DateTime) while teaching
221
+ * AggregatingMergeTree how to combine values during background merges.
222
+ * `max` on a DateTime retention column is the canonical use: reads and TTLs
223
+ * still see a DateTime, but rows sharing the sort key retain the latest
224
+ * expiry instead of an arbitrary one.
225
+ *
226
+ * The underlying type comes from `type`; this field contains only the
227
+ * function name, e.g. "max".
228
+ */
229
+ private _simpleAggregateFunction: string | undefined;
230
+ public get simpleAggregateFunction(): string | undefined {
231
+ return this._simpleAggregateFunction;
232
+ }
233
+ public set simpleAggregateFunction(v: string | undefined) {
234
+ this._simpleAggregateFunction = v;
235
+ }
236
+
216
237
  public constructor(data: {
217
238
  key: string;
218
239
  title: string;
@@ -232,6 +253,7 @@ export default class AnalyticsTableColumn {
232
253
  isLowCardinality?: boolean | undefined;
233
254
  mapKeysColumn?: string | undefined;
234
255
  aggregateFunctionDefinition?: string | undefined;
256
+ simpleAggregateFunction?: string | undefined;
235
257
  }) {
236
258
  this.accessControl = data.accessControl;
237
259
  this.key = data.key;
@@ -250,5 +272,6 @@ export default class AnalyticsTableColumn {
250
272
  this.isLowCardinality = data.isLowCardinality || false;
251
273
  this.mapKeysColumn = data.mapKeysColumn;
252
274
  this.aggregateFunctionDefinition = data.aggregateFunctionDefinition;
275
+ this.simpleAggregateFunction = data.simpleAggregateFunction;
253
276
  }
254
277
  }
@@ -13,6 +13,12 @@ type EnterpriseLicenseInstanceSummary = {
13
13
  userCount: number | null;
14
14
  // ISO date string of the most recent usage report from this instance.
15
15
  lastReportedAt: string | null;
16
+ /*
17
+ * OneUptime version this instance last reported, for example "11.5.13".
18
+ * Null for instances that have not reported since upgrading to a build
19
+ * that sends its version, and for dev builds with no APP_VERSION baked in.
20
+ */
21
+ version: string | null;
16
22
  };
17
23
 
18
24
  export default EnterpriseLicenseInstanceSummary;
@@ -302,10 +302,30 @@ export default class MonitorStep extends DatabaseProperty {
302
302
  retryCountOnError: undefined,
303
303
  requestTimeoutInMs: undefined,
304
304
  retryCount: undefined,
305
- logMonitor: undefined,
306
- traceMonitor: undefined,
307
- metricMonitor: undefined,
308
- exceptionMonitor: undefined,
305
+ /*
306
+ * Seed the telemetry sub-config for the monitor's OWN type so a
307
+ * monitor saved on defaults (without the user touching the telemetry
308
+ * sub-form) still persists a usable query config. Without this the
309
+ * sub-config stayed undefined and the worker threw "<type> query/config
310
+ * is missing" on every evaluation, so the monitor never ran. Only the
311
+ * matching type is seeded; every other type stays undefined.
312
+ */
313
+ logMonitor:
314
+ arg.monitorType === MonitorType.Logs
315
+ ? MonitorStepLogMonitorUtil.getDefault()
316
+ : undefined,
317
+ traceMonitor:
318
+ arg.monitorType === MonitorType.Traces
319
+ ? MonitorStepTraceMonitorUtil.getDefault()
320
+ : undefined,
321
+ metricMonitor:
322
+ arg.monitorType === MonitorType.Metrics
323
+ ? MonitorStepMetricMonitorUtil.getDefault()
324
+ : undefined,
325
+ exceptionMonitor:
326
+ arg.monitorType === MonitorType.Exceptions
327
+ ? MonitorStepExceptionMonitorUtil.getDefault()
328
+ : undefined,
309
329
  profileMonitor: undefined,
310
330
  snmpMonitor: undefined,
311
331
  networkDeviceMonitor: undefined,
@@ -0,0 +1,105 @@
1
+ import MetricFormulaConfigData from "../Metrics/MetricFormulaConfigData";
2
+ import MetricQueryConfigData from "../Metrics/MetricQueryConfigData";
3
+ import MetricsViewConfig from "../Metrics/MetricsViewConfig";
4
+ import { MonitorStepType } from "./MonitorStep";
5
+
6
+ /**
7
+ * Every monitor-step data key that carries a `metricViewConfig`. These are the
8
+ * "metric-shaped" monitor types — the ones whose alert criteria evaluate a
9
+ * metric query (CheckOn.MetricValue). The order here is not significant; a
10
+ * given monitor step only ever populates one of these keys.
11
+ *
12
+ * Keeping this list in one place is what prevents the class of bug where a new
13
+ * metric-shaped monitor type (e.g. Host, Podman, DockerSwarm, IoT) is wired
14
+ * into the step form but forgotten in the criteria UI, leaving the "Which
15
+ * metric query should this alert rule check?" dropdown blank ("No options").
16
+ */
17
+ const METRIC_VIEW_CONFIG_KEYS: Array<keyof MonitorStepType> = [
18
+ "metricMonitor",
19
+ "hostMonitor",
20
+ "kubernetesMonitor",
21
+ "dockerMonitor",
22
+ "dockerSwarmMonitor",
23
+ "podmanMonitor",
24
+ "proxmoxMonitor",
25
+ "cephMonitor",
26
+ "iotMonitor",
27
+ ];
28
+
29
+ export default class MonitorStepMetricViewConfigUtil {
30
+ /**
31
+ * Resolve the `metricViewConfig` from whichever metric-shaped monitor sub-config
32
+ * is populated on this monitor step. Returns undefined when the step is not
33
+ * metric-shaped (or none of the metric configs is present).
34
+ */
35
+ public static getMetricViewConfig(
36
+ monitorStepData: MonitorStepType | undefined,
37
+ ): MetricsViewConfig | undefined {
38
+ if (!monitorStepData) {
39
+ return undefined;
40
+ }
41
+
42
+ /*
43
+ * Each metric-shaped sub-config is an object with an optional
44
+ * `metricViewConfig`. Index through a record view so the lookup stays
45
+ * data-driven (see METRIC_VIEW_CONFIG_KEYS) without fighting the wide
46
+ * union of MonitorStepType's value types.
47
+ */
48
+ const stepRecord: Record<
49
+ string,
50
+ { metricViewConfig?: MetricsViewConfig } | undefined
51
+ > = monitorStepData as unknown as Record<
52
+ string,
53
+ { metricViewConfig?: MetricsViewConfig } | undefined
54
+ >;
55
+
56
+ for (const key of METRIC_VIEW_CONFIG_KEYS) {
57
+ const subConfig: { metricViewConfig?: MetricsViewConfig } | undefined =
58
+ stepRecord[key];
59
+
60
+ if (subConfig?.metricViewConfig) {
61
+ return subConfig.metricViewConfig;
62
+ }
63
+ }
64
+
65
+ return undefined;
66
+ }
67
+
68
+ /**
69
+ * Collect the distinct, non-empty metric variables (aliases) from the step's
70
+ * query configs and formula configs. These become the options for the
71
+ * criteria "Metric" dropdown. Empty variables are dropped and duplicates are
72
+ * removed while preserving first-seen order.
73
+ */
74
+ public static getMetricVariables(
75
+ monitorStepData: MonitorStepType | undefined,
76
+ ): Array<string> {
77
+ const metricViewConfig: MetricsViewConfig | undefined =
78
+ this.getMetricViewConfig(monitorStepData);
79
+
80
+ if (!metricViewConfig) {
81
+ return [];
82
+ }
83
+
84
+ const variables: Array<string> = [];
85
+
86
+ for (const queryConfig of metricViewConfig.queryConfigs || []) {
87
+ variables.push(
88
+ (queryConfig as MetricQueryConfigData).metricAliasData
89
+ ?.metricVariable || "",
90
+ );
91
+ }
92
+
93
+ for (const formulaConfig of metricViewConfig.formulaConfigs || []) {
94
+ variables.push(
95
+ (formulaConfig as MetricFormulaConfigData).metricAliasData
96
+ ?.metricVariable || "",
97
+ );
98
+ }
99
+
100
+ // Remove duplicates (first-seen order) and drop empty strings.
101
+ return variables.filter((item: string, index: number): boolean => {
102
+ return variables.indexOf(item) === index && item !== "";
103
+ });
104
+ }
105
+ }