@oneuptime/common 12.0.11 → 12.0.13

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 (251) hide show
  1. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
  2. package/Models/DatabaseModels/NetworkDevice.ts +64 -0
  3. package/Models/DatabaseModels/StatusPage.ts +59 -0
  4. package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
  5. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
  6. package/Server/API/AIChatAPI.ts +55 -56
  7. package/Server/API/CommonAPI.ts +89 -0
  8. package/Server/API/MicrosoftTeamsAPI.ts +20 -9
  9. package/Server/API/SlackAPI.ts +13 -10
  10. package/Server/API/StatusPageAPI.ts +2197 -2128
  11. package/Server/API/TelemetryAPI.ts +72 -3
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  15. package/Server/Middleware/UserAuthorization.ts +11 -2
  16. package/Server/Services/ApiKeyPermissionService.ts +116 -2
  17. package/Server/Services/DatabaseService.ts +17 -6
  18. package/Server/Services/LogAggregationService.ts +34 -0
  19. package/Server/Services/LogService.ts +21 -0
  20. package/Server/Services/NetworkSiteService.ts +12 -0
  21. package/Server/Services/SpanService.ts +21 -0
  22. package/Server/Services/TraceAggregationService.ts +15 -0
  23. package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
  24. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  25. package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
  26. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +9 -0
  27. package/Server/Types/Workflow/Components/JavaScript.ts +9 -0
  28. package/Server/Utils/APIKey/AccessPermission.ts +16 -40
  29. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
  30. package/Server/Utils/LogRedaction.ts +576 -0
  31. package/Server/Utils/Logger.ts +123 -46
  32. package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
  33. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +12 -0
  34. package/Server/Utils/Monitor/MonitorResource.ts +29 -9
  35. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
  36. package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
  37. package/Server/Utils/StartServer.ts +30 -9
  38. package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
  39. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
  40. package/Server/Utils/VM/VMRunner.ts +693 -874
  41. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
  42. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
  43. package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
  44. package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
  45. package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
  46. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
  47. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
  48. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
  49. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
  50. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
  51. package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
  52. package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
  53. package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
  54. package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
  55. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
  56. package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
  57. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
  58. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
  59. package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
  60. package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
  61. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
  62. package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
  63. package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
  64. package/Tests/Server/Types/Workflow/Components/CustomCodeScriptError.test.ts +121 -0
  65. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
  66. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
  67. package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
  68. package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
  69. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
  70. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorJavascriptExpression.test.ts +92 -0
  71. package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
  72. package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
  73. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
  74. package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
  75. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
  76. package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
  77. package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
  78. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
  79. package/Tests/Server/Utils/VM/VMRunnerIsolation.test.ts +385 -0
  80. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +3 -0
  81. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
  82. package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
  83. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
  84. package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
  85. package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
  86. package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
  87. package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
  88. package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
  89. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
  90. package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
  91. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
  92. package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
  93. package/Tests/UI/Components/MasterPage.test.tsx +36 -1
  94. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
  95. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
  96. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
  97. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
  98. package/Types/API/Hostname.ts +79 -0
  99. package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
  100. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
  101. package/Types/Database/DatabaseProperty.ts +38 -0
  102. package/Types/Database/TableColumn.ts +34 -7
  103. package/Types/Events/Recurring.ts +16 -1
  104. package/Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse.ts +14 -2
  105. package/Types/Monitor/MonitorStep.ts +13 -1
  106. package/Types/Monitor/MonitorType.ts +345 -40
  107. package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
  108. package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
  109. package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
  110. package/UI/Components/CardSelect/CardSelect.tsx +714 -105
  111. package/UI/Components/EmptyState/EmptyState.tsx +8 -1
  112. package/UI/Components/Footer/Footer.tsx +32 -6
  113. package/UI/Components/Forms/Fields/FormField.tsx +3 -0
  114. package/UI/Components/Forms/Types/Field.ts +8 -0
  115. package/UI/Components/Loader/PageLoader.tsx +29 -8
  116. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
  117. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
  118. package/UI/Components/MasterPage/MasterPage.tsx +24 -11
  119. package/Utils/Monitor/MonitorMetricType.ts +16 -1
  120. package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
  121. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
  122. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
  123. package/Utils/Telemetry/CrossSignalScope.ts +75 -4
  124. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
  125. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  126. package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
  127. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  128. package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
  129. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  130. package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
  131. package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
  132. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  133. package/build/dist/Server/API/AIChatAPI.js +48 -35
  134. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  135. package/build/dist/Server/API/CommonAPI.js +59 -0
  136. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  137. package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
  138. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  139. package/build/dist/Server/API/SlackAPI.js +13 -10
  140. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  141. package/build/dist/Server/API/StatusPageAPI.js +745 -673
  142. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  143. package/build/dist/Server/API/TelemetryAPI.js +34 -3
  144. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  145. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
  146. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
  147. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
  148. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
  149. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  150. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  151. package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
  152. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  153. package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
  154. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  155. package/build/dist/Server/Services/DatabaseService.js +15 -6
  156. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  157. package/build/dist/Server/Services/LogAggregationService.js +2 -0
  158. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  159. package/build/dist/Server/Services/LogService.js +16 -0
  160. package/build/dist/Server/Services/LogService.js.map +1 -1
  161. package/build/dist/Server/Services/NetworkSiteService.js +12 -0
  162. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  163. package/build/dist/Server/Services/SpanService.js +16 -0
  164. package/build/dist/Server/Services/SpanService.js.map +1 -1
  165. package/build/dist/Server/Services/TraceAggregationService.js +2 -0
  166. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  167. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
  168. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  169. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  170. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  171. package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
  172. package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
  173. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +8 -0
  174. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  175. package/build/dist/Server/Types/Workflow/Components/JavaScript.js +8 -0
  176. package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
  177. package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
  178. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  179. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
  180. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  181. package/build/dist/Server/Utils/LogRedaction.js +449 -0
  182. package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
  183. package/build/dist/Server/Utils/Logger.js +98 -45
  184. package/build/dist/Server/Utils/Logger.js.map +1 -1
  185. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
  186. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
  187. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +12 -1
  188. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  189. package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
  190. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  191. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
  192. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  193. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
  194. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
  195. package/build/dist/Server/Utils/StartServer.js +10 -7
  196. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  197. package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
  198. package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
  199. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
  200. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
  201. package/build/dist/Server/Utils/VM/VMRunner.js +402 -525
  202. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  203. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
  204. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  205. package/build/dist/Types/API/Hostname.js +63 -0
  206. package/build/dist/Types/API/Hostname.js.map +1 -1
  207. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
  208. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  209. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
  210. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  211. package/build/dist/Types/Database/DatabaseProperty.js +38 -0
  212. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  213. package/build/dist/Types/Database/TableColumn.js +24 -6
  214. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  215. package/build/dist/Types/Events/Recurring.js +13 -1
  216. package/build/dist/Types/Events/Recurring.js.map +1 -1
  217. package/build/dist/Types/Monitor/MonitorStep.js +9 -1
  218. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  219. package/build/dist/Types/Monitor/MonitorType.js +330 -31
  220. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  221. package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
  222. package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
  223. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
  224. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
  225. package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
  226. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  227. package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
  228. package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
  229. package/build/dist/UI/Components/Footer/Footer.js +7 -4
  230. package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
  231. package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
  232. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  233. package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
  234. package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
  235. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
  236. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  237. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
  238. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  239. package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
  240. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  241. package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
  242. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  243. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
  244. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  245. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
  246. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
  247. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
  248. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
  249. package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
  250. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  251. package/package.json +1 -1
@@ -0,0 +1,286 @@
1
+ import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
2
+ import Incident from "../../../Models/DatabaseModels/Incident";
3
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
4
+ import Project from "../../../Models/DatabaseModels/Project";
5
+ import {
6
+ TableColumnMetadata,
7
+ getTableColumn,
8
+ getTableColumns,
9
+ } from "../../../Types/Database/TableColumn";
10
+ import TableColumnType from "../../../Types/Database/TableColumnType";
11
+ import Dictionary from "../../../Types/Dictionary";
12
+ import "reflect-metadata";
13
+
14
+ type ModelCtor = new () => DatabaseBaseModel;
15
+
16
+ const MODELS: Array<[string, ModelCtor]> = [
17
+ ["Monitor", Monitor],
18
+ ["Incident", Incident],
19
+ ["Project", Project],
20
+ ];
21
+
22
+ /*
23
+ * Locate the module-private metadata symbol (e.g. Symbol(TableColumn)) by
24
+ * scanning the metadata keys of every instance property, so the tests can
25
+ * rebuild the dictionary from scratch exactly the way the pre-cache
26
+ * implementation did — without importing any private state.
27
+ */
28
+ function findMetadataSymbol(
29
+ model: DatabaseBaseModel,
30
+ symbolName: string,
31
+ ): symbol | null {
32
+ for (const key of Object.keys(model)) {
33
+ const metadataKeys: Array<unknown> = Reflect.getMetadataKeys(
34
+ model,
35
+ key,
36
+ ) as Array<unknown>;
37
+ for (const metadataKey of metadataKeys) {
38
+ if (
39
+ typeof metadataKey === "symbol" &&
40
+ metadataKey.toString() === `Symbol(${symbolName})`
41
+ ) {
42
+ return metadataKey;
43
+ }
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+
49
+ // The exact algorithm getTableColumns() used before caching was introduced.
50
+ function rebuildFromReflect<T>(
51
+ model: DatabaseBaseModel,
52
+ symbolName: string,
53
+ ): Dictionary<T> {
54
+ const metadataSymbol: symbol | null = findMetadataSymbol(model, symbolName);
55
+ const dictionary: Dictionary<T> = {};
56
+
57
+ if (!metadataSymbol) {
58
+ return dictionary;
59
+ }
60
+
61
+ for (const key of Object.keys(model)) {
62
+ const metadata: T | undefined = Reflect.getMetadata(
63
+ metadataSymbol,
64
+ model,
65
+ key,
66
+ ) as T | undefined;
67
+ if (metadata) {
68
+ dictionary[key] = metadata;
69
+ }
70
+ }
71
+
72
+ return dictionary;
73
+ }
74
+
75
+ function rebuildTableColumns(
76
+ model: DatabaseBaseModel,
77
+ ): Dictionary<TableColumnMetadata> {
78
+ return rebuildFromReflect<TableColumnMetadata>(model, "TableColumn");
79
+ }
80
+
81
+ describe("getTableColumns caching", () => {
82
+ for (const [name, ModelClass] of MODELS) {
83
+ test(`matches a from-scratch Reflect rebuild for ${name} — keys, order, and value identity`, () => {
84
+ const model: DatabaseBaseModel = new ModelClass();
85
+ const expected: Dictionary<TableColumnMetadata> =
86
+ rebuildTableColumns(model);
87
+ const actual: Dictionary<TableColumnMetadata> = getTableColumns(model);
88
+
89
+ // Sanity: real models have plenty of decorated columns.
90
+ expect(Object.keys(expected).length).toBeGreaterThan(5);
91
+
92
+ // Same keys in the same insertion order.
93
+ expect(Object.keys(actual)).toEqual(Object.keys(expected));
94
+
95
+ // Values are the exact same Reflect metadata singletons as before.
96
+ for (const key of Object.keys(expected)) {
97
+ expect(actual[key]).toBe(expected[key]);
98
+ }
99
+ });
100
+ }
101
+
102
+ test("second call for the same class performs zero Reflect metadata reads", () => {
103
+ const monitor: Monitor = new Monitor();
104
+ const expected: Dictionary<TableColumnMetadata> =
105
+ rebuildTableColumns(monitor);
106
+
107
+ getTableColumns(monitor); // warm the cache
108
+
109
+ const spy: jest.SpyInstance = jest.spyOn(Reflect, "getMetadata");
110
+ try {
111
+ const result: Dictionary<TableColumnMetadata> = getTableColumns(monitor);
112
+ expect(spy).not.toHaveBeenCalled();
113
+ expect(Object.keys(result)).toEqual(Object.keys(expected));
114
+ } finally {
115
+ spy.mockRestore();
116
+ }
117
+ });
118
+
119
+ test("cache is shared across different instances of the same class", () => {
120
+ getTableColumns(new Monitor()); // warm the cache with one instance
121
+
122
+ const spy: jest.SpyInstance = jest.spyOn(Reflect, "getMetadata");
123
+ try {
124
+ const result: Dictionary<TableColumnMetadata> = getTableColumns(
125
+ new Monitor(),
126
+ );
127
+ expect(spy).not.toHaveBeenCalled();
128
+ expect(result["name"]).toBeDefined();
129
+ } finally {
130
+ spy.mockRestore();
131
+ }
132
+ });
133
+
134
+ test("different classes get distinct dictionaries", () => {
135
+ const monitorColumns: Dictionary<TableColumnMetadata> = getTableColumns(
136
+ new Monitor(),
137
+ );
138
+ const incidentColumns: Dictionary<TableColumnMetadata> = getTableColumns(
139
+ new Incident(),
140
+ );
141
+ const projectColumns: Dictionary<TableColumnMetadata> = getTableColumns(
142
+ new Project(),
143
+ );
144
+
145
+ expect(monitorColumns).not.toBe(incidentColumns);
146
+ expect(monitorColumns).not.toBe(projectColumns);
147
+ expect(incidentColumns).not.toBe(projectColumns);
148
+
149
+ expect(Object.keys(monitorColumns)).not.toEqual(
150
+ Object.keys(incidentColumns),
151
+ );
152
+ expect(monitorColumns["monitorType"]).toBeDefined();
153
+ expect(incidentColumns["monitorType"]).toBeUndefined();
154
+ expect(projectColumns["monitorType"]).toBeUndefined();
155
+ });
156
+
157
+ test("mutations by one caller never leak into later calls (API reference deletes entries)", () => {
158
+ const expected: Dictionary<TableColumnMetadata> = rebuildTableColumns(
159
+ new Monitor(),
160
+ );
161
+
162
+ const first: Dictionary<TableColumnMetadata> = getTableColumns(
163
+ new Monitor(),
164
+ );
165
+
166
+ // The API reference docs page strips columns exactly like this.
167
+ delete first["deletedAt"];
168
+ delete first["version"];
169
+ delete first["name"];
170
+ first["monitorType"] = { type: TableColumnType.Phone };
171
+
172
+ const second: Dictionary<TableColumnMetadata> = getTableColumns(
173
+ new Monitor(),
174
+ );
175
+
176
+ expect(second).not.toBe(first);
177
+ expect(Object.keys(second)).toEqual(Object.keys(expected));
178
+ expect(second["deletedAt"]).toBe(expected["deletedAt"]);
179
+ expect(second["version"]).toBe(expected["version"]);
180
+ expect(second["name"]).toBe(expected["name"]);
181
+ expect(second["monitorType"]).toBe(expected["monitorType"]);
182
+ expect(second["monitorType"]!.type).not.toBe(TableColumnType.Phone);
183
+ });
184
+
185
+ test("Columns wrapper on the model still lists every decorated column", () => {
186
+ for (const [, ModelClass] of MODELS) {
187
+ const model: DatabaseBaseModel = new ModelClass();
188
+ const expected: Dictionary<TableColumnMetadata> =
189
+ rebuildTableColumns(model);
190
+ expect(model.getTableColumns().columns).toEqual(Object.keys(expected));
191
+ }
192
+ });
193
+
194
+ test("filter helpers derive from identical metadata after caching", () => {
195
+ for (const [, ModelClass] of MODELS) {
196
+ const model: DatabaseBaseModel = new ModelClass();
197
+ const expected: Dictionary<TableColumnMetadata> =
198
+ rebuildTableColumns(model);
199
+
200
+ const expectedFor: (
201
+ predicate: (metadata: TableColumnMetadata) => boolean,
202
+ ) => Array<string> = (
203
+ predicate: (metadata: TableColumnMetadata) => boolean,
204
+ ): Array<string> => {
205
+ return Object.keys(expected).filter((key: string) => {
206
+ return predicate(expected[key] as TableColumnMetadata);
207
+ });
208
+ };
209
+
210
+ expect(model.getRequiredColumns().columns).toEqual(
211
+ expectedFor((metadata: TableColumnMetadata) => {
212
+ return Boolean(metadata.required);
213
+ }),
214
+ );
215
+ expect(model.getUniqueColumns().columns).toEqual(
216
+ expectedFor((metadata: TableColumnMetadata) => {
217
+ return Boolean(metadata.unique);
218
+ }),
219
+ );
220
+ expect(model.getHashedColumns().columns).toEqual(
221
+ expectedFor((metadata: TableColumnMetadata) => {
222
+ return Boolean(metadata.hashed);
223
+ }),
224
+ );
225
+ expect(model.getEncryptedColumns().columns).toEqual(
226
+ expectedFor((metadata: TableColumnMetadata) => {
227
+ return Boolean(metadata.encrypted);
228
+ }),
229
+ );
230
+ }
231
+ });
232
+ });
233
+
234
+ describe("getTableColumnMetadata", () => {
235
+ for (const [name, ModelClass] of MODELS) {
236
+ test(`returns the exact rebuild value for every instance key of ${name}`, () => {
237
+ const model: DatabaseBaseModel = new ModelClass();
238
+ const expected: Dictionary<TableColumnMetadata> =
239
+ rebuildTableColumns(model);
240
+
241
+ for (const key of Object.keys(model)) {
242
+ const expectedValue: TableColumnMetadata | undefined = expected[key];
243
+ const actualValue: TableColumnMetadata =
244
+ model.getTableColumnMetadata(key);
245
+
246
+ if (expectedValue) {
247
+ expect(actualValue).toBe(expectedValue);
248
+ } else {
249
+ // Undecorated instance keys (e.g. isPermissionIf) stay undefined.
250
+ expect(actualValue).toBeUndefined();
251
+ }
252
+ }
253
+ });
254
+ }
255
+
256
+ test("returns undefined for unknown keys", () => {
257
+ const monitor: Monitor = new Monitor();
258
+ expect(monitor.getTableColumnMetadata("doesNotExist")).toBeUndefined();
259
+ expect(monitor.getTableColumnMetadata("")).toBeUndefined();
260
+ expect(monitor.getTableColumnMetadata("constructor")).toBeUndefined();
261
+ expect(monitor.getTableColumnMetadata("isPermissionIf")).toBeUndefined();
262
+ });
263
+
264
+ test("agrees with the standalone single-column reader", () => {
265
+ const monitor: Monitor = new Monitor();
266
+ for (const key of Object.keys(monitor)) {
267
+ expect(monitor.getTableColumnMetadata(key)).toBe(
268
+ getTableColumn(monitor, key),
269
+ );
270
+ }
271
+ });
272
+
273
+ test("reads a single metadata entry instead of rebuilding the whole dictionary", () => {
274
+ const monitor: Monitor = new Monitor();
275
+
276
+ const spy: jest.SpyInstance = jest.spyOn(Reflect, "getMetadata");
277
+ try {
278
+ const metadata: TableColumnMetadata =
279
+ monitor.getTableColumnMetadata("name");
280
+ expect(metadata).toBeDefined();
281
+ expect(spy).toHaveBeenCalledTimes(1);
282
+ } finally {
283
+ spy.mockRestore();
284
+ }
285
+ });
286
+ });
@@ -218,4 +218,104 @@ describe("MonitorStep", () => {
218
218
  expect(domainMonitor["lookupMethod"]).toBe(DomainLookupMethod.WHOIS);
219
219
  });
220
220
  });
221
+
222
+ /*
223
+ * Regression suite for https://github.com/OneUptime/oneuptime/issues/3225.
224
+ *
225
+ * fromJSON normalizes metricMonitor and domainMonitor through their
226
+ * Util.fromJSON/toJSON pair, each with a comment explaining that the probe
227
+ * would otherwise receive a config that does not satisfy its own type.
228
+ * dnssecMonitor was a raw passthrough.
229
+ *
230
+ * A DNSSEC config authored through the API, Terraform or an imported
231
+ * template need not carry `resolvers`, and the probe iterates that list
232
+ * without a guard - so it threw a TypeError BEFORE posting any result. The
233
+ * monitor produced nothing at all, on every cycle, with no status change
234
+ * and no error the user could see: the same silent-failure class the issue
235
+ * reports for SSL Certificate monitors.
236
+ */
237
+ describe("dnssecMonitor normalization (issue #3225)", () => {
238
+ /*
239
+ * Built from a real default step for the same reason the domainMonitor
240
+ * tests above are: fromJSON validates the whole envelope, so a hand-rolled
241
+ * fragment would fail for reasons unrelated to what is under test.
242
+ */
243
+ function restoreWithDnssecMonitor(
244
+ dnssecMonitor: JSONObject | undefined,
245
+ ): JSONObject | undefined {
246
+ const json: JSONObject =
247
+ MonitorStep.getDefaultMonitorStep(DEFAULT_ARG).toJSON();
248
+
249
+ if (dnssecMonitor) {
250
+ (json["value"] as JSONObject)["dnssecMonitor"] = dnssecMonitor;
251
+ }
252
+
253
+ return MonitorStep.fromJSON(json).data?.dnssecMonitor as unknown as
254
+ | JSONObject
255
+ | undefined;
256
+ }
257
+
258
+ test("back-fills resolvers when the stored config has none", () => {
259
+ const dnssec: JSONObject | undefined = restoreWithDnssecMonitor({
260
+ domainName: "oneuptime.com",
261
+ });
262
+
263
+ expect(dnssec?.["resolvers"]).toEqual(["1.1.1.1", "8.8.8.8", "9.9.9.9"]);
264
+ });
265
+
266
+ test("back-fills resolvers when the stored list is empty", () => {
267
+ const dnssec: JSONObject | undefined = restoreWithDnssecMonitor({
268
+ domainName: "oneuptime.com",
269
+ resolvers: [],
270
+ });
271
+
272
+ expect((dnssec?.["resolvers"] as Array<string>).length).toBeGreaterThan(
273
+ 0,
274
+ );
275
+ });
276
+
277
+ test("preserves resolvers the caller actually supplied", () => {
278
+ const dnssec: JSONObject | undefined = restoreWithDnssecMonitor({
279
+ domainName: "oneuptime.com",
280
+ resolvers: ["9.9.9.9"],
281
+ });
282
+
283
+ expect(dnssec?.["resolvers"]).toEqual(["9.9.9.9"]);
284
+ });
285
+
286
+ test("back-fills the rest of the config the probe relies on", () => {
287
+ const dnssec: JSONObject | undefined = restoreWithDnssecMonitor({
288
+ domainName: "oneuptime.com",
289
+ });
290
+
291
+ expect(dnssec?.["checkNameserverConsistency"]).toBe(true);
292
+ expect(dnssec?.["signatureExpiryWarningDays"]).toBe(7);
293
+ expect(dnssec?.["timeout"]).toBe(10000);
294
+ expect(dnssec?.["retries"]).toBe(3);
295
+ });
296
+
297
+ test("a re-save repairs an already-stored row", () => {
298
+ /*
299
+ * toJSON(fromJSON(x)) is what a monitor edit performs, so rows
300
+ * persisted before the fix heal the next time they are written.
301
+ */
302
+ const json: JSONObject =
303
+ MonitorStep.getDefaultMonitorStep(DEFAULT_ARG).toJSON();
304
+
305
+ (json["value"] as JSONObject)["dnssecMonitor"] = {
306
+ domainName: "oneuptime.com",
307
+ };
308
+
309
+ const resaved: JSONObject = MonitorStep.fromJSON(json).toJSON()[
310
+ "value"
311
+ ] as JSONObject;
312
+ const dnssec: JSONObject = resaved["dnssecMonitor"] as JSONObject;
313
+
314
+ expect((dnssec["resolvers"] as Array<string>).length).toBeGreaterThan(0);
315
+ });
316
+
317
+ test("leaves a step without a dnssecMonitor untouched", () => {
318
+ expect(restoreWithDnssecMonitor(undefined)).toBeUndefined();
319
+ });
320
+ });
221
321
  });