@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,518 @@
1
+ import Color from "../../../Types/Color";
2
+ import Decimal from "../../../Types/Decimal";
3
+ import Email from "../../../Types/Email";
4
+ import Recurring from "../../../Types/Events/Recurring";
5
+ import HashedString from "../../../Types/HashedString";
6
+ import Name from "../../../Types/Name";
7
+ import ObjectID from "../../../Types/ObjectID";
8
+ import Port from "../../../Types/Port";
9
+ import { JSONObject } from "../../../Types/JSON";
10
+ import {
11
+ And,
12
+ Between,
13
+ DataSource,
14
+ Equal,
15
+ EntitySchema,
16
+ FindOperator,
17
+ ILike,
18
+ In,
19
+ IsNull,
20
+ Not,
21
+ Or,
22
+ Raw,
23
+ } from "typeorm";
24
+ import { ValueTransformer } from "typeorm/decorator/options/ValueTransformer";
25
+
26
+ /*
27
+ * A privacy/scope filter AND-combines the caller's own filter with a Raw
28
+ * clause (Common/Server/Utils/PrivacyFilterUtil.ts), which hands TypeORM a
29
+ * nested FindOperator on a column that carries a DatabaseProperty
30
+ * transformer. TypeORM then calls `operator.transformValue(transformer)`
31
+ * (SelectQueryBuilder.buildWhere), and FindOperator.transformValue maps the
32
+ * transformer's `to()` over each CHILD OPERATOR whenever the operator holds
33
+ * multiple parameters — which And() and Or() both do.
34
+ *
35
+ * Every `toDatabase()` in Common/Types is written for a *value*, so handed a
36
+ * child operator it produces garbage instead: "[object Object]" for the
37
+ * string-ish types, `null` for Port, a serialized operator for the JSON
38
+ * types. The equality child is silently replaced by that garbage and the
39
+ * predicate can no longer match — on a uuid column Postgres rejects it
40
+ * outright with 22P02 invalid input syntax for type uuid.
41
+ *
42
+ * These tests pin the contract that fixes it: a FindOperator handed to the
43
+ * transformer keeps its identity and gets its LEAF value(s) transformed,
44
+ * exactly once, however deeply it is nested.
45
+ */
46
+
47
+ const UUID: string = "11111111-2222-4333-8444-555555555555";
48
+ const OTHER_UUID: string = "99999999-8888-4777-8666-555555555555";
49
+
50
+ type RawClauseType = FindOperator<any>;
51
+
52
+ function makeRawClause(): RawClauseType {
53
+ return Raw((alias: string): string => {
54
+ return `(${alias} IS NOT NULL)`;
55
+ }) as RawClauseType;
56
+ }
57
+
58
+ function childrenOf(operator: FindOperator<any>): Array<any> {
59
+ return operator.value as unknown as Array<any>;
60
+ }
61
+
62
+ describe("DatabaseProperty database transformer — nested FindOperator values", () => {
63
+ describe("ObjectID (the reported failure: 1698 uuid columns use this transformer)", () => {
64
+ it("transforms the leaf of a nested Equal instead of stringifying the operator", () => {
65
+ const operator: FindOperator<any> = And(
66
+ Equal(new ObjectID(UUID) as any),
67
+ makeRawClause(),
68
+ ) as FindOperator<any>;
69
+
70
+ operator.transformValue(ObjectID.getDatabaseTransformer());
71
+
72
+ const children: Array<any> = childrenOf(operator);
73
+ expect(children).toHaveLength(2);
74
+ // Before the fix this child was the string "[object Object]".
75
+ expect(children[0]).toBeInstanceOf(FindOperator);
76
+ expect(children[0].type).toBe("equal");
77
+ expect(children[0].value).toBe(UUID);
78
+ expect(children[0].value).not.toBe("[object Object]");
79
+ });
80
+
81
+ it("leaves a Raw sibling byte-identical", () => {
82
+ const clause: RawClauseType = makeRawClause();
83
+ const operator: FindOperator<any> = And(
84
+ Equal(new ObjectID(UUID) as any),
85
+ clause,
86
+ ) as FindOperator<any>;
87
+
88
+ operator.transformValue(ObjectID.getDatabaseTransformer());
89
+
90
+ expect(childrenOf(operator)[1]).toBe(clause);
91
+ expect(childrenOf(operator)[1].type).toBe("raw");
92
+ });
93
+
94
+ it("accepts an ObjectID leaf, a string leaf and a nested ObjectID equally", () => {
95
+ const fromInstance: FindOperator<any> = And(
96
+ Equal(new ObjectID(UUID) as any),
97
+ makeRawClause(),
98
+ ) as FindOperator<any>;
99
+ const fromString: FindOperator<any> = And(
100
+ Equal(UUID),
101
+ makeRawClause(),
102
+ ) as FindOperator<any>;
103
+
104
+ fromInstance.transformValue(ObjectID.getDatabaseTransformer());
105
+ fromString.transformValue(ObjectID.getDatabaseTransformer());
106
+
107
+ expect(childrenOf(fromInstance)[0].value).toBe(UUID);
108
+ expect(childrenOf(fromString)[0].value).toBe(UUID);
109
+ });
110
+
111
+ it("handles Or, In, Not, Between and IsNull children", () => {
112
+ const or: FindOperator<any> = Or(
113
+ Equal(new ObjectID(UUID) as any),
114
+ makeRawClause(),
115
+ ) as FindOperator<any>;
116
+ const includes: FindOperator<any> = And(
117
+ In([new ObjectID(UUID), new ObjectID(OTHER_UUID)] as any),
118
+ makeRawClause(),
119
+ ) as FindOperator<any>;
120
+ const negated: FindOperator<any> = And(
121
+ Not(Equal(new ObjectID(UUID) as any)),
122
+ makeRawClause(),
123
+ ) as FindOperator<any>;
124
+ const between: FindOperator<any> = And(
125
+ Between(new ObjectID(UUID) as any, new ObjectID(OTHER_UUID) as any),
126
+ makeRawClause(),
127
+ ) as FindOperator<any>;
128
+ const isNull: FindOperator<any> = And(
129
+ IsNull(),
130
+ makeRawClause(),
131
+ ) as FindOperator<any>;
132
+
133
+ for (const operator of [or, includes, negated, between, isNull]) {
134
+ operator.transformValue(ObjectID.getDatabaseTransformer());
135
+ }
136
+
137
+ expect(childrenOf(or)[0].value).toBe(UUID);
138
+ expect(childrenOf(includes)[0].type).toBe("in");
139
+ expect(childrenOf(includes)[0].value).toEqual([UUID, OTHER_UUID]);
140
+ /*
141
+ * Not() wraps a single child operator, which TypeORM recurses into on
142
+ * its own. FindOperator's `value` getter unwraps a nested operator, so
143
+ * the transformed leaf shows up on `value` and the operator itself on
144
+ * `child`.
145
+ */
146
+ expect(childrenOf(negated)[0].type).toBe("not");
147
+ expect(childrenOf(negated)[0].child.type).toBe("equal");
148
+ expect(childrenOf(negated)[0].value).toBe(UUID);
149
+ expect(childrenOf(between)[0].value).toEqual([UUID, OTHER_UUID]);
150
+ expect(childrenOf(isNull)[0].type).toBe("isNull");
151
+ });
152
+
153
+ it("recurses through a doubly-nested And", () => {
154
+ /*
155
+ * Reachable in production: BaseAPI.getList hands the SAME query object
156
+ * to findBy and then countBy, so the count query's privacy hook
157
+ * AND-combines a clause onto the And the find query already built.
158
+ */
159
+ const innerClause: RawClauseType = makeRawClause();
160
+ const outerClause: RawClauseType = makeRawClause();
161
+ const inner: FindOperator<any> = And(
162
+ Equal(new ObjectID(UUID) as any),
163
+ innerClause,
164
+ ) as FindOperator<any>;
165
+ const outer: FindOperator<any> = And(
166
+ inner,
167
+ outerClause,
168
+ ) as FindOperator<any>;
169
+
170
+ outer.transformValue(ObjectID.getDatabaseTransformer());
171
+
172
+ expect(childrenOf(outer)[0]).toBe(inner);
173
+ expect(childrenOf(outer)[0].type).toBe("and");
174
+ expect(childrenOf(inner)[0].value).toBe(UUID);
175
+ expect(childrenOf(inner)[1]).toBe(innerClause);
176
+ expect(childrenOf(outer)[1]).toBe(outerClause);
177
+ });
178
+
179
+ it("preserves an all-Raw nested And (the QueryUtil filter-stacking shape)", () => {
180
+ const rawA: RawClauseType = makeRawClause();
181
+ const rawB: RawClauseType = makeRawClause();
182
+ const rawC: RawClauseType = makeRawClause();
183
+ const inner: FindOperator<any> = And(rawA, rawB) as FindOperator<any>;
184
+ const outer: FindOperator<any> = And(inner, rawC) as FindOperator<any>;
185
+
186
+ outer.transformValue(ObjectID.getDatabaseTransformer());
187
+
188
+ /*
189
+ * The pre-existing `_type === "raw"` escape hatch rescues a Raw child,
190
+ * but an And OF Raws is itself type "and", so it was stringified too.
191
+ */
192
+ expect(childrenOf(outer)[0]).toBe(inner);
193
+ expect(childrenOf(inner)[0]).toBe(rawA);
194
+ expect(childrenOf(inner)[1]).toBe(rawB);
195
+ });
196
+
197
+ it("does not throw on a nested pattern operator", () => {
198
+ const operator: FindOperator<any> = And(
199
+ ILike("%abc%"),
200
+ makeRawClause(),
201
+ ) as FindOperator<any>;
202
+
203
+ expect(() => {
204
+ operator.transformValue(ObjectID.getDatabaseTransformer());
205
+ }).not.toThrow();
206
+ expect(childrenOf(operator)[0].value).toBe("%abc%");
207
+ });
208
+
209
+ it("still transforms a plain value and a top-level operator", () => {
210
+ const transformer: ValueTransformer = ObjectID.getDatabaseTransformer();
211
+
212
+ expect(transformer.to(new ObjectID(UUID))).toBe(UUID);
213
+
214
+ const topLevel: FindOperator<any> = Equal(
215
+ new ObjectID(UUID) as any,
216
+ ) as FindOperator<any>;
217
+ const returned: any = transformer.to(topLevel);
218
+
219
+ expect(returned).toBe(topLevel);
220
+ expect(topLevel.value).toBe(UUID);
221
+ });
222
+
223
+ it("keeps the undefined / null contract that omitted columns rely on", () => {
224
+ const transformer: ValueTransformer = ObjectID.getDatabaseTransformer();
225
+
226
+ // undefined must stay undefined so a column DEFAULT stays reachable.
227
+ expect(transformer.to(undefined)).toBeUndefined();
228
+ expect(transformer.to(null)).toBeNull();
229
+ });
230
+ });
231
+
232
+ describe("non-ObjectID types (an ObjectID-only patch would not fix these)", () => {
233
+ it("Port keeps a numeric leaf instead of collapsing the operator to null", () => {
234
+ const operator: FindOperator<any> = And(
235
+ Equal(new Port(587) as any),
236
+ makeRawClause(),
237
+ ) as FindOperator<any>;
238
+
239
+ operator.transformValue(Port.getDatabaseTransformer());
240
+
241
+ // Before the fix this child was `null` — silently unmatchable.
242
+ expect(childrenOf(operator)[0]).toBeInstanceOf(FindOperator);
243
+ expect(childrenOf(operator)[0].value).toBe(587);
244
+ expect(typeof childrenOf(operator)[0].value).toBe("number");
245
+ });
246
+
247
+ it("Port keeps a falsy 0 leaf", () => {
248
+ const operator: FindOperator<any> = And(
249
+ Equal(new Port(0) as any),
250
+ makeRawClause(),
251
+ ) as FindOperator<any>;
252
+
253
+ operator.transformValue(Port.getDatabaseTransformer());
254
+
255
+ expect(childrenOf(operator)[0].value).toBe(0);
256
+ });
257
+
258
+ it("Email normalizes the leaf rather than stringifying the operator", () => {
259
+ const operator: FindOperator<any> = And(
260
+ Equal(new Email("User@Example.COM") as any),
261
+ makeRawClause(),
262
+ ) as FindOperator<any>;
263
+
264
+ operator.transformValue(Email.getDatabaseTransformer());
265
+
266
+ expect(childrenOf(operator)[0].value).toBe("user@example.com");
267
+ });
268
+
269
+ it("Decimal, HashedString, Name and Color transform their leaves", () => {
270
+ const decimal: FindOperator<any> = And(
271
+ Equal(new Decimal("12.5") as any),
272
+ makeRawClause(),
273
+ ) as FindOperator<any>;
274
+ const hashed: FindOperator<any> = And(
275
+ Equal(new HashedString("raw-token", false) as any),
276
+ makeRawClause(),
277
+ ) as FindOperator<any>;
278
+ const name: FindOperator<any> = And(
279
+ Equal(new Name("Jane Doe") as any),
280
+ makeRawClause(),
281
+ ) as FindOperator<any>;
282
+ const color: FindOperator<any> = And(
283
+ Equal(new Color("#ff0000") as any),
284
+ makeRawClause(),
285
+ ) as FindOperator<any>;
286
+
287
+ decimal.transformValue(Decimal.getDatabaseTransformer());
288
+ hashed.transformValue(HashedString.getDatabaseTransformer());
289
+ name.transformValue(Name.getDatabaseTransformer());
290
+ color.transformValue(Color.getDatabaseTransformer());
291
+
292
+ expect(childrenOf(decimal)[0].value).toBe("12.5");
293
+ expect(childrenOf(hashed)[0].value).toBe("raw-token");
294
+ expect(childrenOf(name)[0].value).toBe("Jane Doe");
295
+ expect(childrenOf(color)[0].value).toBe("#ff0000");
296
+ });
297
+
298
+ it("Recurring transforms a JSON leaf to its serialized form", () => {
299
+ const operator: FindOperator<any> = And(
300
+ Equal(Recurring.getDefault() as any),
301
+ makeRawClause(),
302
+ ) as FindOperator<any>;
303
+
304
+ operator.transformValue(Recurring.getDatabaseTransformer());
305
+
306
+ expect(childrenOf(operator)[0]).toBeInstanceOf(FindOperator);
307
+ expect(childrenOf(operator)[0].value).toEqual(
308
+ Recurring.getDefault().toJSON(),
309
+ );
310
+ });
311
+ });
312
+
313
+ describe("idempotence — one query object is transformed more than once", () => {
314
+ /*
315
+ * BaseAPI.getList (Common/Server/API/BaseAPI.ts) awaits findBy and then
316
+ * countBy with the SAME query object, and transformValue mutates the
317
+ * operator in place. So every transformer must be a fixpoint: handed its
318
+ * own output it must return that output unchanged, not re-wrap or throw.
319
+ */
320
+ type TransformerOwnerType = {
321
+ getDatabaseTransformer: () => ValueTransformer;
322
+ };
323
+
324
+ const cases: Array<{
325
+ name: string;
326
+ owner: TransformerOwnerType;
327
+ operator: () => FindOperator<any>;
328
+ }> = [
329
+ {
330
+ name: "ObjectID",
331
+ owner: ObjectID,
332
+ operator: () => {
333
+ return And(
334
+ Equal(new ObjectID(UUID) as any),
335
+ makeRawClause(),
336
+ ) as FindOperator<any>;
337
+ },
338
+ },
339
+ {
340
+ name: "Port",
341
+ owner: Port,
342
+ operator: () => {
343
+ return And(
344
+ Equal(new Port(587) as any),
345
+ makeRawClause(),
346
+ ) as FindOperator<any>;
347
+ },
348
+ },
349
+ {
350
+ name: "Email",
351
+ owner: Email,
352
+ operator: () => {
353
+ return And(
354
+ Equal(new Email("user@example.com") as any),
355
+ makeRawClause(),
356
+ ) as FindOperator<any>;
357
+ },
358
+ },
359
+ {
360
+ name: "Decimal",
361
+ owner: Decimal,
362
+ operator: () => {
363
+ return And(
364
+ Equal(new Decimal("12.5") as any),
365
+ makeRawClause(),
366
+ ) as FindOperator<any>;
367
+ },
368
+ },
369
+ {
370
+ name: "HashedString",
371
+ owner: HashedString,
372
+ operator: () => {
373
+ return And(
374
+ Equal(new HashedString("raw-token", false) as any),
375
+ makeRawClause(),
376
+ ) as FindOperator<any>;
377
+ },
378
+ },
379
+ {
380
+ name: "Recurring (scalar)",
381
+ owner: Recurring,
382
+ operator: () => {
383
+ return And(
384
+ Equal(Recurring.getDefault() as any),
385
+ makeRawClause(),
386
+ ) as FindOperator<any>;
387
+ },
388
+ },
389
+ {
390
+ name: "Recurring (array column)",
391
+ owner: Recurring,
392
+ operator: () => {
393
+ return And(
394
+ Equal([Recurring.getDefault(), Recurring.getDefault()] as any),
395
+ makeRawClause(),
396
+ ) as FindOperator<any>;
397
+ },
398
+ },
399
+ ];
400
+
401
+ for (const testCase of cases) {
402
+ it(`${testCase.name} is a fixpoint under repeated transformation`, () => {
403
+ const operator: FindOperator<any> = testCase.operator();
404
+ const transformer: ValueTransformer =
405
+ testCase.owner.getDatabaseTransformer();
406
+
407
+ operator.transformValue(transformer);
408
+ const afterFirstPass: string = JSON.stringify(
409
+ childrenOf(operator)[0].value,
410
+ );
411
+
412
+ expect(() => {
413
+ operator.transformValue(transformer);
414
+ operator.transformValue(transformer);
415
+ }).not.toThrow();
416
+
417
+ expect(JSON.stringify(childrenOf(operator)[0].value)).toBe(
418
+ afterFirstPass,
419
+ );
420
+ });
421
+ }
422
+ });
423
+
424
+ describe("end to end — the SQL and bound parameters TypeORM emits", () => {
425
+ /*
426
+ * The unit tests above pin the transformer. This one pins the thing the
427
+ * user actually saw: the parameter bound against a uuid column. It runs
428
+ * real TypeORM query building against real metadata; no database
429
+ * connection is needed to render the statement.
430
+ */
431
+ let dataSource: DataSource;
432
+
433
+ const IncidentFeedLike: EntitySchema = new EntitySchema({
434
+ name: "IncidentFeedLike",
435
+ tableName: "IncidentFeedLike",
436
+ columns: {
437
+ _id: { primary: true, type: "uuid", generated: "uuid" },
438
+ incidentId: {
439
+ type: "uuid",
440
+ transformer: ObjectID.getDatabaseTransformer(),
441
+ },
442
+ feedInfoInMarkdown: { type: "character varying", nullable: true },
443
+ },
444
+ });
445
+
446
+ beforeAll(async () => {
447
+ dataSource = new DataSource({
448
+ type: "postgres",
449
+ entities: [IncidentFeedLike],
450
+ synchronize: false,
451
+ });
452
+ /*
453
+ * Builds entity metadata without opening a connection, so the test can
454
+ * render real SQL offline. Protected on DataSource, hence the cast.
455
+ */
456
+ await (
457
+ dataSource as unknown as { buildMetadatas: () => Promise<void> }
458
+ ).buildMetadatas();
459
+ });
460
+
461
+ it("binds the uuid, not '[object Object]', for a privacy-filtered read", () => {
462
+ const [sql, parameters]: [string, Array<any>] = dataSource
463
+ .createQueryBuilder(IncidentFeedLike, "IncidentFeedLike")
464
+ .setFindOptions({
465
+ where: {
466
+ incidentId: And(Equal(UUID), makeRawClause()) as any,
467
+ },
468
+ take: 10,
469
+ })
470
+ .getQueryAndParameters();
471
+
472
+ expect(parameters).toEqual([UUID]);
473
+ expect(parameters).not.toContain("[object Object]");
474
+ expect(sql).toContain('"IncidentFeedLike"."incidentId" = $1');
475
+ expect(sql).toContain('"IncidentFeedLike"."incidentId" IS NOT NULL');
476
+ });
477
+
478
+ it("binds the uuid when the caller passed an ObjectID instance", () => {
479
+ const [, parameters]: [string, Array<any>] = dataSource
480
+ .createQueryBuilder(IncidentFeedLike, "IncidentFeedLike")
481
+ .setFindOptions({
482
+ where: {
483
+ incidentId: And(
484
+ Equal(new ObjectID(UUID) as any),
485
+ makeRawClause(),
486
+ ) as any,
487
+ },
488
+ take: 10,
489
+ })
490
+ .getQueryAndParameters();
491
+
492
+ expect(parameters).toEqual([UUID]);
493
+ });
494
+
495
+ it("keeps both predicates when the same query object is read twice", () => {
496
+ const query: JSONObject = {
497
+ incidentId: And(Equal(UUID), makeRawClause()) as any,
498
+ };
499
+
500
+ const first: [string, Array<any>] = dataSource
501
+ .createQueryBuilder(IncidentFeedLike, "IncidentFeedLike")
502
+ .setFindOptions({ where: query as any, take: 10 })
503
+ .getQueryAndParameters();
504
+
505
+ const second: [string, Array<any>] = dataSource
506
+ .createQueryBuilder(IncidentFeedLike, "IncidentFeedLike")
507
+ .setFindOptions({ where: query as any })
508
+ .getQueryAndParameters();
509
+
510
+ expect(first[1]).toEqual([UUID]);
511
+ expect(second[1]).toEqual([UUID]);
512
+ expect(second[0]).toContain('"IncidentFeedLike"."incidentId" = $1');
513
+ expect(second[0]).toContain(
514
+ '"IncidentFeedLike"."incidentId" IS NOT NULL',
515
+ );
516
+ });
517
+ });
518
+ });