@oneuptime/common 12.0.13 → 12.0.14

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 (376) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
  2. package/Models/DatabaseModels/UserCall.ts +94 -3
  3. package/Models/DatabaseModels/UserEmail.ts +94 -3
  4. package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
  5. package/Models/DatabaseModels/UserPush.ts +37 -0
  6. package/Models/DatabaseModels/UserSMS.ts +94 -3
  7. package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
  8. package/Server/API/BaseAPI.ts +79 -14
  9. package/Server/API/UserCallAPI.ts +53 -45
  10. package/Server/API/UserEmailAPI.ts +53 -44
  11. package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
  12. package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
  13. package/Server/API/UserPushAPI.ts +126 -2
  14. package/Server/API/UserSmsAPI.ts +53 -44
  15. package/Server/API/UserWhatsAppAPI.ts +53 -50
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
  21. package/Server/Services/AnalyticsDatabaseService.ts +169 -0
  22. package/Server/Services/DatabaseService.ts +101 -0
  23. package/Server/Services/LogAggregationService.ts +5 -0
  24. package/Server/Services/PushNotificationService.ts +127 -12
  25. package/Server/Services/TeamMemberService.ts +53 -13
  26. package/Server/Services/UserCallService.ts +55 -24
  27. package/Server/Services/UserEmailService.ts +83 -17
  28. package/Server/Services/UserIncomingCallNumberService.ts +49 -85
  29. package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
  30. package/Server/Services/UserNotificationRuleService.ts +26 -0
  31. package/Server/Services/UserPushService.ts +89 -0
  32. package/Server/Services/UserSmsService.ts +50 -17
  33. package/Server/Services/UserWhatsAppService.ts +48 -15
  34. package/Server/Types/Database/QueryHelper.ts +30 -0
  35. package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
  36. package/Server/Utils/ChannelVerification.ts +510 -0
  37. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +13 -16
  38. package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
  39. package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
  40. package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
  41. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
  42. package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
  43. package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
  44. package/Server/Utils/TotpAuth.ts +137 -9
  45. package/Server/Utils/UserRegistrationToken.ts +182 -0
  46. package/Server/Utils/VerificationCode.ts +134 -0
  47. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
  48. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
  49. package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
  50. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
  51. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
  52. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
  53. package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
  54. package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
  55. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
  56. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
  57. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
  58. package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
  59. package/Tests/Server/API/UserSmsApi.test.ts +107 -14
  60. package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
  61. package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +315 -0
  62. package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
  63. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
  64. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
  65. package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
  66. package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
  67. package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
  68. package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
  69. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
  70. package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
  71. package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
  72. package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
  73. package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
  74. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
  75. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
  76. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
  77. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
  78. package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
  79. package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
  80. package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
  81. package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
  82. package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
  83. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
  84. package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
  85. package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
  86. package/Tests/Server/Utils/Browser.test.ts +162 -0
  87. package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
  88. package/Tests/Server/Utils/Express.test.ts +371 -0
  89. package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
  91. package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
  92. package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
  93. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
  94. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
  95. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
  96. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
  97. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
  98. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
  99. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
  100. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
  101. package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
  102. package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
  103. package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
  104. package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
  105. package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
  106. package/Tests/Types/Call/CallRequest.test.ts +189 -0
  107. package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
  108. package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
  109. package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
  110. package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
  111. package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
  112. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
  113. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
  114. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
  115. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
  116. package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
  117. package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
  118. package/Tests/Types/TextRandomGeneration.test.ts +211 -0
  119. package/Tests/UI/Components/Button.test.tsx +175 -1
  120. package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
  121. package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
  122. package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
  123. package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
  124. package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
  125. package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
  126. package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
  127. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
  128. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
  129. package/Tests/UI/Components/List.test.tsx +9 -2
  130. package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
  131. package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
  132. package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
  133. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
  134. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
  135. package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
  136. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
  137. package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
  138. package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
  139. package/Tests/UI/Components/Skeleton.test.tsx +71 -0
  140. package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
  141. package/Tests/UI/Components/Tabs.test.tsx +24 -0
  142. package/Tests/UI/Components/Toast.test.tsx +130 -7
  143. package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
  144. package/Tests/UI/Utils/Dropdown.test.ts +423 -0
  145. package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
  146. package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
  147. package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
  148. package/Tests/Utils/API.test.ts +16 -2
  149. package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
  150. package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
  151. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
  152. package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
  153. package/Types/Email/EmailTemplateType.ts +1 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
  155. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
  156. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
  157. package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
  158. package/Types/Permission.ts +35 -0
  159. package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
  160. package/Types/PushNotification/PushNotificationMessage.ts +15 -0
  161. package/Types/PushNotification/PushNotificationRequest.ts +8 -16
  162. package/Types/Service/ServiceLanguage.ts +144 -0
  163. package/Types/Text.ts +111 -19
  164. package/UI/Components/Button/Button.tsx +27 -14
  165. package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
  166. package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
  167. package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
  168. package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
  169. package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
  170. package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
  171. package/UI/Components/CommandPalette/Types.ts +49 -0
  172. package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
  173. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  174. package/UI/Components/Feed/FeedItem.tsx +1 -1
  175. package/UI/Components/List/List.tsx +37 -5
  176. package/UI/Components/List/ListSkeleton.tsx +54 -0
  177. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
  178. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
  179. package/UI/Components/Modal/Modal.tsx +2 -2
  180. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  181. package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
  182. package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
  183. package/UI/Components/Navbar/NavBar.tsx +14 -1
  184. package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
  185. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
  186. package/UI/Components/SideOver/SideOver.tsx +37 -2
  187. package/UI/Components/Skeleton/Skeleton.tsx +53 -0
  188. package/UI/Components/Table/Table.tsx +74 -44
  189. package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
  190. package/UI/Components/Tabs/Tab.tsx +1 -1
  191. package/UI/Components/Toast/Toast.tsx +118 -53
  192. package/UI/Components/Toast/ToastInit.tsx +82 -32
  193. package/UI/Styles/Theme.css +40 -0
  194. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
  195. package/UI/Utils/ModelListCache.ts +188 -0
  196. package/UI/Utils/Project.ts +29 -0
  197. package/Utils/API.ts +6 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
  201. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
  203. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
  205. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
  207. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
  209. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
  211. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  212. package/build/dist/Server/API/BaseAPI.js +69 -13
  213. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  214. package/build/dist/Server/API/UserCallAPI.js +42 -38
  215. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  216. package/build/dist/Server/API/UserEmailAPI.js +42 -38
  217. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  218. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
  219. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  220. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
  221. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
  222. package/build/dist/Server/API/UserPushAPI.js +95 -2
  223. package/build/dist/Server/API/UserPushAPI.js.map +1 -1
  224. package/build/dist/Server/API/UserSmsAPI.js +42 -38
  225. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  226. package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
  227. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  236. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
  237. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
  238. package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
  239. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  240. package/build/dist/Server/Services/DatabaseService.js +74 -0
  241. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  242. package/build/dist/Server/Services/LogAggregationService.js +5 -0
  243. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  244. package/build/dist/Server/Services/PushNotificationService.js +69 -30
  245. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  246. package/build/dist/Server/Services/TeamMemberService.js +43 -7
  247. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  248. package/build/dist/Server/Services/UserCallService.js +54 -22
  249. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  250. package/build/dist/Server/Services/UserEmailService.js +78 -17
  251. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  252. package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
  253. package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
  254. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
  255. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
  256. package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
  257. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  258. package/build/dist/Server/Services/UserPushService.js +77 -0
  259. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  260. package/build/dist/Server/Services/UserSmsService.js +50 -17
  261. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  262. package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
  263. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  264. package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
  265. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  266. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
  267. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
  268. package/build/dist/Server/Utils/ChannelVerification.js +348 -0
  269. package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
  270. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +13 -12
  271. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  272. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
  273. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  274. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
  275. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  276. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
  277. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
  278. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
  279. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
  280. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
  281. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
  282. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  283. package/build/dist/Server/Utils/TotpAuth.js +127 -8
  284. package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
  285. package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
  286. package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
  287. package/build/dist/Server/Utils/VerificationCode.js +120 -0
  288. package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
  289. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  290. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  291. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
  292. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  293. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
  294. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  295. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
  296. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  297. package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
  298. package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
  299. package/build/dist/Types/Permission.js +33 -0
  300. package/build/dist/Types/Permission.js.map +1 -1
  301. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
  302. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
  303. package/build/dist/Types/Service/ServiceLanguage.js +96 -0
  304. package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
  305. package/build/dist/Types/Text.js +82 -17
  306. package/build/dist/Types/Text.js.map +1 -1
  307. package/build/dist/UI/Components/Button/Button.js +22 -14
  308. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  309. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
  310. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  311. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
  312. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
  313. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
  314. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
  315. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
  316. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
  317. package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
  318. package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
  319. package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
  320. package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
  321. package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
  322. package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
  323. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
  324. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
  325. package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
  326. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  327. package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
  328. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  329. package/build/dist/UI/Components/List/List.js +19 -5
  330. package/build/dist/UI/Components/List/List.js.map +1 -1
  331. package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
  332. package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
  333. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  334. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
  335. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  336. package/build/dist/UI/Components/Modal/Modal.js +2 -2
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  338. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  339. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
  340. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  343. package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
  344. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  345. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
  346. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
  347. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
  348. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  349. package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
  350. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  351. package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
  352. package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
  353. package/build/dist/UI/Components/Table/Table.js +35 -24
  354. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  355. package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
  356. package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
  357. package/build/dist/UI/Components/Tabs/Tab.js +1 -1
  358. package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
  359. package/build/dist/UI/Components/Toast/Toast.js +77 -23
  360. package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
  361. package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
  362. package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
  363. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
  364. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  365. package/build/dist/UI/Utils/ModelListCache.js +126 -0
  366. package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
  367. package/build/dist/UI/Utils/Project.js +24 -1
  368. package/build/dist/UI/Utils/Project.js.map +1 -1
  369. package/build/dist/Utils/API.js +6 -1
  370. package/build/dist/Utils/API.js.map +1 -1
  371. package/jest.config.json +1 -1
  372. package/package.json +2 -3
  373. package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
  374. package/Tests/__mocks__/otpauth.js +0 -30
  375. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
  376. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
@@ -0,0 +1,504 @@
1
+ import DatabaseRequestType from "../../../../../Server/Types/BaseDatabase/DatabaseRequestType";
2
+ import AccessControlPermission from "../../../../../Server/Types/Database/Permissions/AccessControlPermission";
3
+ import BasePermission from "../../../../../Server/Types/Database/Permissions/BasePermission";
4
+ import OwnedScopePermission from "../../../../../Server/Types/Database/Permissions/OwnedScopePermission";
5
+ import PublicPermission from "../../../../../Server/Types/Database/Permissions/PublicPermission";
6
+ import QueryPermission from "../../../../../Server/Types/Database/Permissions/QueryPermission";
7
+ import SelectPermission from "../../../../../Server/Types/Database/Permissions/SelectPermission";
8
+ import TablePermission from "../../../../../Server/Types/Database/Permissions/TablePermission";
9
+ import TenantPermission from "../../../../../Server/Types/Database/Permissions/TenantPermission";
10
+ import UserPermissions from "../../../../../Server/Types/Database/Permissions/UserPermission";
11
+ import Query from "../../../../../Server/Types/Database/Query";
12
+ import Select from "../../../../../Server/Types/Database/Select";
13
+ import OnCallDutyPolicyEscalationRuleSchedule from "../../../../../Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule";
14
+ import OnCallDutyPolicySchedule from "../../../../../Models/DatabaseModels/OnCallDutyPolicySchedule";
15
+ import BaseModel from "../../../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
16
+ import DatabaseCommonInteractionProps from "../../../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
17
+ import { isOwnerOnlyColumn } from "../../../../../Types/Database/AccessControl/OwnerOnlyColumn";
18
+ import { TableColumnMetadata } from "../../../../../Types/Database/TableColumn";
19
+ import TableColumnType from "../../../../../Types/Database/TableColumnType";
20
+ import BadDataException from "../../../../../Types/Exception/BadDataException";
21
+ import NotAuthorizedException from "../../../../../Types/Exception/NotAuthorizedException";
22
+ import ObjectID from "../../../../../Types/ObjectID";
23
+ import Permission from "../../../../../Types/Permission";
24
+
25
+ /*
26
+ * THE BUG.
27
+ *
28
+ * Opening a policy's escalation rules failed outright with
29
+ *
30
+ * Column currentUserIdOnRoster on On-Call Policy Schedule does not
31
+ * support read on relation query.
32
+ *
33
+ * The escalation rule page lists the schedules attached to each rule and marks
34
+ * the ones that would currently page nobody. It reads that state off the join:
35
+ *
36
+ * modelType: OnCallDutyPolicyEscalationRuleSchedule
37
+ * select: { onCallDutyPolicySchedule: { name, currentUserIdOnRoster } }
38
+ *
39
+ * `name` carries canReadOnRelationQuery. `currentUserIdOnRoster` did not, and
40
+ * QueryPermission.checkRelationQueryPermission THROWS on the first unflagged
41
+ * inner key rather than dropping that key from the select - so the one missing
42
+ * flag failed the entire request, and the page rendered nothing at all. Every
43
+ * caller hit it, up to and including ProjectOwner, which is why deleting the
44
+ * schedule and re-creating it changed nothing: no row was ever the problem.
45
+ *
46
+ * WHAT THESE TESTS PIN.
47
+ *
48
+ * The two select shapes the UI actually sends, verbatim, so a select edited in
49
+ * the components without a matching model flag fails here (first describe).
50
+ * That the flag is the only thing that moved - the sibling columns on the same
51
+ * model, deep relations and unknown columns all still refuse, and a direct
52
+ * non-relation read is still governed by column permissions (third and fourth
53
+ * describes). And the deliberate cost of the flag, stated rather than skipped
54
+ * over: it bypasses the related column's own read list, exactly as `name`
55
+ * already did (fifth describe).
56
+ */
57
+
58
+ const projectId: ObjectID = ObjectID.generate();
59
+ const userId: ObjectID = ObjectID.generate();
60
+
61
+ /*
62
+ * Fresh props per assertion: DatabaseCommonInteractionPropsUtil MUTATES what it
63
+ * is handed, pushing the auto-granted Public and CurrentUser onto the list, so
64
+ * a shared object carries state between tests.
65
+ */
66
+ function makeProps(
67
+ permissions: Array<Permission>,
68
+ ): DatabaseCommonInteractionProps {
69
+ return {
70
+ userId: userId,
71
+ tenantId: projectId,
72
+ userTenantAccessPermission: {
73
+ [projectId.toString()]: {
74
+ projectId: projectId,
75
+ permissions: permissions.map((permission: Permission) => {
76
+ return {
77
+ permission: permission,
78
+ labelIds: [],
79
+ isBlockPermission: false,
80
+ _type: "UserPermission" as const,
81
+ };
82
+ }),
83
+ _type: "UserTenantAccessPermission",
84
+ },
85
+ },
86
+ };
87
+ }
88
+
89
+ function checkRelationSelect(
90
+ select: Select<OnCallDutyPolicyEscalationRuleSchedule>,
91
+ props: DatabaseCommonInteractionProps,
92
+ ): void {
93
+ QueryPermission.checkRelationQueryPermission(
94
+ OnCallDutyPolicyEscalationRuleSchedule,
95
+ {} as Query<OnCallDutyPolicyEscalationRuleSchedule>,
96
+ select,
97
+ props,
98
+ );
99
+ }
100
+
101
+ /*
102
+ * The select copied out of
103
+ * App/FeatureSet/Dashboard/src/Components/OnCallPolicy/EscalationRule/EscalationRules.tsx.
104
+ * Kept as a literal rather than imported so that changing the component
105
+ * without changing the model surfaces here as a failure.
106
+ */
107
+ const ESCALATION_RULES_SELECT: Select<OnCallDutyPolicyEscalationRuleSchedule> =
108
+ {
109
+ _id: true,
110
+ onCallDutyPolicyEscalationRuleId: true,
111
+ onCallDutyPolicySchedule: {
112
+ _id: true,
113
+ name: true,
114
+ currentUserIdOnRoster: true,
115
+ },
116
+ } as Select<OnCallDutyPolicyEscalationRuleSchedule>;
117
+
118
+ /*
119
+ * The select copied out of
120
+ * App/FeatureSet/Dashboard/src/Components/OnCallPolicy/OnCallPolicySummary.tsx.
121
+ */
122
+ const POLICY_SUMMARY_SELECT: Select<OnCallDutyPolicyEscalationRuleSchedule> = {
123
+ _id: true,
124
+ onCallDutyPolicyEscalationRuleId: true,
125
+ onCallDutyPolicySchedule: {
126
+ _id: true,
127
+ name: true,
128
+ currentUserIdOnRoster: true,
129
+ },
130
+ } as Select<OnCallDutyPolicyEscalationRuleSchedule>;
131
+
132
+ describe("On-call schedule relation select - the reported failure", () => {
133
+ it("accepts the escalation rules page select", () => {
134
+ expect(() => {
135
+ return checkRelationSelect(
136
+ ESCALATION_RULES_SELECT,
137
+ makeProps([Permission.ProjectOwner]),
138
+ );
139
+ }).not.toThrow();
140
+ });
141
+
142
+ it("accepts the policy summary select", () => {
143
+ expect(() => {
144
+ return checkRelationSelect(
145
+ POLICY_SUMMARY_SELECT,
146
+ makeProps([Permission.ProjectOwner]),
147
+ );
148
+ }).not.toThrow();
149
+ });
150
+
151
+ it("no longer raises the reported message for any caller", () => {
152
+ /*
153
+ * The reporter was a project owner and re-created the schedule twice. The
154
+ * throw was in the permission layer and had nothing to do with the row, so
155
+ * pin it across the whole read list rather than for one role.
156
+ */
157
+ const readers: Array<Permission> = [
158
+ Permission.ProjectOwner,
159
+ Permission.ProjectAdmin,
160
+ Permission.ProjectMember,
161
+ Permission.Viewer,
162
+ Permission.OnCallAdmin,
163
+ Permission.OnCallMember,
164
+ Permission.OnCallViewer,
165
+ Permission.ReadProjectOnCallDutyPolicyEscalationRuleSchedule,
166
+ ];
167
+
168
+ for (const permission of readers) {
169
+ expect(() => {
170
+ return checkRelationSelect(
171
+ ESCALATION_RULES_SELECT,
172
+ makeProps([permission]),
173
+ );
174
+ }).not.toThrow();
175
+ }
176
+ });
177
+
178
+ it("accepts the roster column selected on its own", () => {
179
+ // Without `name` alongside it, in case the component drops the label later.
180
+ expect(() => {
181
+ return checkRelationSelect(
182
+ {
183
+ onCallDutyPolicySchedule: {
184
+ currentUserIdOnRoster: true,
185
+ },
186
+ } as Select<OnCallDutyPolicyEscalationRuleSchedule>,
187
+ makeProps([Permission.ProjectMember]),
188
+ );
189
+ }).not.toThrow();
190
+ });
191
+ });
192
+
193
+ describe("On-call schedule relation select - through the full permission stack", () => {
194
+ /*
195
+ * checkRelationQueryPermission is one of several gates BasePermission runs
196
+ * over a read, and the escalation rule page reaches it through all of them.
197
+ * Tenant and user scoping need a database, so they are stubbed; every
198
+ * permission gate on the select path is left real.
199
+ */
200
+ beforeEach(() => {
201
+ jest
202
+ .spyOn(PublicPermission, "checkIfUserIsLoggedIn")
203
+ .mockImplementation(() => {});
204
+ jest
205
+ .spyOn(TenantPermission, "addTenantScopeToQuery")
206
+ .mockImplementation(async (_modelType: any, query: any) => {
207
+ return query;
208
+ });
209
+ jest
210
+ .spyOn(UserPermissions, "addUserScopeToQuery")
211
+ .mockImplementation(async (_modelType: any, query: any) => {
212
+ return query;
213
+ });
214
+ jest
215
+ .spyOn(TablePermission, "checkTableLevelPermissions")
216
+ .mockImplementation(() => {});
217
+ jest
218
+ .spyOn(AccessControlPermission, "addAccessControlIdsToQuery")
219
+ .mockImplementation(async (_modelType: any, query: any) => {
220
+ return query;
221
+ });
222
+ jest
223
+ .spyOn(OwnedScopePermission, "addOwnedScopeToQuery")
224
+ .mockImplementation(async (_modelType: any, query: any) => {
225
+ return query;
226
+ });
227
+ });
228
+
229
+ afterEach(() => {
230
+ jest.restoreAllMocks();
231
+ });
232
+
233
+ it("lets the escalation rule read through checkPermissions", async () => {
234
+ await expect(
235
+ BasePermission.checkPermissions(
236
+ OnCallDutyPolicyEscalationRuleSchedule,
237
+ { onCallDutyPolicyId: ObjectID.generate() } as any,
238
+ ESCALATION_RULES_SELECT,
239
+ makeProps([Permission.ProjectOwner]),
240
+ DatabaseRequestType.Read,
241
+ ),
242
+ ).resolves.toBeDefined();
243
+ });
244
+
245
+ it("actually reaches the relation gate on that path", async () => {
246
+ /*
247
+ * Guards the test above from passing because the gate was skipped rather
248
+ * than because it allowed the select.
249
+ */
250
+ const spy: jest.SpyInstance = jest.spyOn(
251
+ QueryPermission,
252
+ "checkRelationQueryPermission",
253
+ );
254
+
255
+ await BasePermission.checkPermissions(
256
+ OnCallDutyPolicyEscalationRuleSchedule,
257
+ { onCallDutyPolicyId: ObjectID.generate() } as any,
258
+ ESCALATION_RULES_SELECT,
259
+ makeProps([Permission.ProjectOwner]),
260
+ DatabaseRequestType.Read,
261
+ );
262
+
263
+ expect(spy).toHaveBeenCalled();
264
+ });
265
+
266
+ it("still runs the top-level select gate on that path", async () => {
267
+ const spy: jest.SpyInstance = jest.spyOn(
268
+ SelectPermission,
269
+ "checkSelectPermission",
270
+ );
271
+
272
+ await BasePermission.checkPermissions(
273
+ OnCallDutyPolicyEscalationRuleSchedule,
274
+ { onCallDutyPolicyId: ObjectID.generate() } as any,
275
+ ESCALATION_RULES_SELECT,
276
+ makeProps([Permission.ProjectOwner]),
277
+ DatabaseRequestType.Read,
278
+ );
279
+
280
+ expect(spy).toHaveBeenCalled();
281
+ });
282
+ });
283
+
284
+ describe("On-call schedule relation select - the model flag itself", () => {
285
+ const schedule: BaseModel = new OnCallDutyPolicySchedule();
286
+
287
+ function metadata(columnName: string): TableColumnMetadata {
288
+ return schedule.getTableColumnMetadata(columnName);
289
+ }
290
+
291
+ it("marks currentUserIdOnRoster readable through a relation", () => {
292
+ expect(metadata("currentUserIdOnRoster").canReadOnRelationQuery).toBe(true);
293
+ });
294
+
295
+ it("keeps it an ObjectID column, not an entity", () => {
296
+ /*
297
+ * The relation traversal only recurses into Entity columns. If this column
298
+ * ever became one, `currentUserIdOnRoster: true` would stop being a leaf
299
+ * select and the flag would no longer be what is being tested.
300
+ */
301
+ expect(metadata("currentUserIdOnRoster").type).toBe(
302
+ TableColumnType.ObjectID,
303
+ );
304
+ });
305
+
306
+ it("puts it alongside the columns the join already exposed", () => {
307
+ expect(metadata("name").canReadOnRelationQuery).toBe(true);
308
+ expect(metadata("projectId").canReadOnRelationQuery).toBe(true);
309
+ });
310
+
311
+ it("does not mark it owner-only", () => {
312
+ /*
313
+ * canReadOnRelationQuery runs AFTER the owner-only gate in
314
+ * checkRelationQueryPermission and cannot override it. Marking this column
315
+ * @OwnerOnlyColumn later would silently re-break the page, so state the
316
+ * expectation here.
317
+ */
318
+ expect(isOwnerOnlyColumn(schedule, "currentUserIdOnRoster")).toBe(false);
319
+ });
320
+
321
+ it("leaves the rest of the schedule's columns alone", () => {
322
+ /*
323
+ * The fix is one flag on one column, not a widening of the model. Every
324
+ * column below is still unreadable through a relation.
325
+ */
326
+ const stillClosed: Array<string> = [
327
+ "description",
328
+ "timezone",
329
+ "slug",
330
+ "nextUserIdOnRoster",
331
+ "rosterHandoffAt",
332
+ "rosterNextHandoffAt",
333
+ "rosterNextStartAt",
334
+ "rosterStartAt",
335
+ "createdByUserId",
336
+ "deletedByUserId",
337
+ ];
338
+
339
+ for (const columnName of stillClosed) {
340
+ expect(metadata(columnName)).toBeTruthy();
341
+ expect(Boolean(metadata(columnName).canReadOnRelationQuery)).toBe(false);
342
+ }
343
+ });
344
+ });
345
+
346
+ describe("On-call schedule relation select - what still refuses", () => {
347
+ const props: DatabaseCommonInteractionProps = makeProps([
348
+ Permission.ProjectOwner,
349
+ ]);
350
+
351
+ it("refuses a sibling column that was never flagged", () => {
352
+ expect(() => {
353
+ return checkRelationSelect(
354
+ {
355
+ onCallDutyPolicySchedule: {
356
+ name: true,
357
+ timezone: true,
358
+ },
359
+ } as Select<OnCallDutyPolicyEscalationRuleSchedule>,
360
+ makeProps([Permission.ProjectOwner]),
361
+ );
362
+ }).toThrow(BadDataException);
363
+ });
364
+
365
+ it("refuses the next-on-roster column", () => {
366
+ /*
367
+ * The neighbouring column, same shape, same model, one line away in the
368
+ * source. Only the column the page needs was opened.
369
+ */
370
+ expect(() => {
371
+ return checkRelationSelect(
372
+ {
373
+ onCallDutyPolicySchedule: {
374
+ nextUserIdOnRoster: true,
375
+ },
376
+ } as Select<OnCallDutyPolicyEscalationRuleSchedule>,
377
+ makeProps([Permission.ProjectOwner]),
378
+ );
379
+ }).toThrow(
380
+ "Column nextUserIdOnRoster on On-Call Policy Schedule does not support read on relation query.",
381
+ );
382
+ });
383
+
384
+ it("refuses a deep relation hung off the roster user", () => {
385
+ /*
386
+ * currentUserOnRoster is the entity beside the id column. Walking into it
387
+ * would reach the User table; deep relations are refused before any column
388
+ * check happens.
389
+ */
390
+ expect(() => {
391
+ return checkRelationSelect(
392
+ {
393
+ onCallDutyPolicySchedule: {
394
+ currentUserOnRoster: { email: true },
395
+ },
396
+ } as unknown as Select<OnCallDutyPolicyEscalationRuleSchedule>,
397
+ props,
398
+ );
399
+ }).toThrow(
400
+ "You cannot query deep relations. Querying deep relations is not supported.",
401
+ );
402
+ });
403
+
404
+ it("refuses a column that does not exist on the schedule", () => {
405
+ expect(() => {
406
+ return checkRelationSelect(
407
+ {
408
+ onCallDutyPolicySchedule: {
409
+ currentUserIdOnRosterr: true,
410
+ },
411
+ } as unknown as Select<OnCallDutyPolicyEscalationRuleSchedule>,
412
+ props,
413
+ );
414
+ }).toThrow(BadDataException);
415
+ });
416
+
417
+ it("still refuses a direct read of the column without permission", () => {
418
+ /*
419
+ * The flag governs the relation gate only. Selecting the column at the top
420
+ * level of a schedule query goes through SelectPermission, which reads the
421
+ * column's own @ColumnAccessControl list - and a caller holding none of
422
+ * those permissions is refused exactly as before.
423
+ */
424
+ expect(() => {
425
+ return SelectPermission.checkSelectPermission(
426
+ OnCallDutyPolicySchedule,
427
+ { currentUserIdOnRoster: true } as Select<OnCallDutyPolicySchedule>,
428
+ makeProps([Permission.ProjectUser]),
429
+ );
430
+ }).toThrow(NotAuthorizedException);
431
+ });
432
+
433
+ it("allows the direct read for a caller who does hold the permission", () => {
434
+ expect(() => {
435
+ return SelectPermission.checkSelectPermission(
436
+ OnCallDutyPolicySchedule,
437
+ { currentUserIdOnRoster: true } as Select<OnCallDutyPolicySchedule>,
438
+ makeProps([Permission.ReadProjectOnCallDutyPolicySchedule]),
439
+ );
440
+ }).not.toThrow();
441
+ });
442
+ });
443
+
444
+ describe("On-call schedule relation select - the cost of the flag", () => {
445
+ /*
446
+ * canReadOnRelationQuery short-circuits the related column's own read list -
447
+ * that is what the flag DOES, and it is why the model comment argues the case
448
+ * rather than leaving the flag bare. Pinned here so the tradeoff is visible
449
+ * and deliberate instead of being discovered later.
450
+ */
451
+
452
+ it("exposes the roster id to a caller scoped only to the join table", () => {
453
+ /*
454
+ * ReadProjectOnCallDutyPolicyEscalationRuleSchedule is not on the schedule
455
+ * column's own read list. Through the join it reads anyway - as it already
456
+ * did for the schedule's name.
457
+ */
458
+ const joinOnlyProps: DatabaseCommonInteractionProps = makeProps([
459
+ Permission.ReadProjectOnCallDutyPolicyEscalationRuleSchedule,
460
+ ]);
461
+
462
+ expect(() => {
463
+ return checkRelationSelect(
464
+ {
465
+ onCallDutyPolicySchedule: {
466
+ name: true,
467
+ currentUserIdOnRoster: true,
468
+ },
469
+ } as Select<OnCallDutyPolicyEscalationRuleSchedule>,
470
+ joinOnlyProps,
471
+ );
472
+ }).not.toThrow();
473
+ });
474
+
475
+ it("is the same route the schedule name already took", () => {
476
+ /*
477
+ * If `name` alone ever started refusing for this caller, the paragraph
478
+ * above stops being true and the flag would need re-arguing.
479
+ */
480
+ expect(() => {
481
+ return checkRelationSelect(
482
+ {
483
+ onCallDutyPolicySchedule: { name: true },
484
+ } as Select<OnCallDutyPolicyEscalationRuleSchedule>,
485
+ makeProps([
486
+ Permission.ReadProjectOnCallDutyPolicyEscalationRuleSchedule,
487
+ ]),
488
+ );
489
+ }).not.toThrow();
490
+ });
491
+
492
+ it("does not let that caller reach anything else on the schedule", () => {
493
+ expect(() => {
494
+ return checkRelationSelect(
495
+ {
496
+ onCallDutyPolicySchedule: { description: true },
497
+ } as Select<OnCallDutyPolicyEscalationRuleSchedule>,
498
+ makeProps([
499
+ Permission.ReadProjectOnCallDutyPolicyEscalationRuleSchedule,
500
+ ]),
501
+ );
502
+ }).toThrow(BadDataException);
503
+ });
504
+ });
@@ -0,0 +1,91 @@
1
+ import QueryHelper from "../../../../Server/Types/Database/QueryHelper";
2
+ import { FindOperator } from "typeorm";
3
+ import { describe, expect, it } from "@jest/globals";
4
+
5
+ /*
6
+ * findWithSameTextAnyOf is the multi-value twin of findWithSameText, added
7
+ * so a monitor's step config — where a user TYPED the identifier and an
8
+ * agent STAMPED the row's name, and the two legitimately differ by case —
9
+ * can resolve several identifiers in one round-trip instead of one
10
+ * findOneBy each.
11
+ *
12
+ * Two properties matter and neither is visible from the operator's JSON
13
+ * (the SQL generator is a function on `.value`): the comparison must be
14
+ * case-folded on BOTH sides, and the identifiers must be BOUND as
15
+ * parameters, never interpolated — they come from user-editable monitor
16
+ * configuration.
17
+ */
18
+
19
+ interface RawOperator {
20
+ type: string;
21
+ objectLiteralParameters: Record<string, unknown>;
22
+ // TypeORM builds the SQL through this, given the column alias.
23
+ getSql: (aliasPath: string) => string;
24
+ }
25
+
26
+ function asRaw(operator: unknown): RawOperator {
27
+ return operator as unknown as RawOperator;
28
+ }
29
+
30
+ describe("QueryHelper.findWithSameTextAnyOf", () => {
31
+ it("emits a case-insensitive IN over the column", () => {
32
+ const operator: RawOperator = asRaw(
33
+ QueryHelper.findWithSameTextAnyOf(["prod-01", "prod-02"]),
34
+ );
35
+
36
+ expect(operator).toBeInstanceOf(FindOperator);
37
+ expect(operator.type).toBe("raw");
38
+
39
+ const sql: string = operator.getSql('"Host"."hostIdentifier"');
40
+
41
+ // LOWER() on the column is what makes the match case-insensitive.
42
+ expect(sql).toContain('LOWER("Host"."hostIdentifier")');
43
+ expect(sql).toContain("IN (");
44
+ });
45
+
46
+ it("binds the identifiers as parameters instead of interpolating them", () => {
47
+ const operator: RawOperator = asRaw(
48
+ QueryHelper.findWithSameTextAnyOf(["prod-01"]),
49
+ );
50
+
51
+ const sql: string = operator.getSql('"Host"."hostIdentifier"');
52
+
53
+ /*
54
+ * The value must appear ONLY in the parameter bag. If it showed up in
55
+ * the SQL text, a monitor step identifier would be an injection
56
+ * vector.
57
+ */
58
+ expect(sql).not.toContain("prod-01");
59
+ expect(Object.values(operator.objectLiteralParameters)).toEqual([
60
+ ["prod-01"],
61
+ ]);
62
+ });
63
+
64
+ it("lower-cases and trims every identifier it binds", () => {
65
+ /*
66
+ * The column side is lower-cased by the SQL; the value side has to be
67
+ * lower-cased here or the comparison never matches. Whitespace comes
68
+ * from users pasting identifiers into the monitor form.
69
+ */
70
+ const operator: RawOperator = asRaw(
71
+ QueryHelper.findWithSameTextAnyOf([" PROD-01 ", "Prod-02"]),
72
+ );
73
+
74
+ expect(Object.values(operator.objectLiteralParameters)).toEqual([
75
+ ["prod-01", "prod-02"],
76
+ ]);
77
+ });
78
+
79
+ it("does not collide with another call's parameter name", () => {
80
+ /*
81
+ * Both operators can land in one query (two resource types resolved
82
+ * in the same findBy), so the random parameter key must differ.
83
+ */
84
+ const first: RawOperator = asRaw(QueryHelper.findWithSameTextAnyOf(["a"]));
85
+ const second: RawOperator = asRaw(QueryHelper.findWithSameTextAnyOf(["b"]));
86
+
87
+ expect(Object.keys(first.objectLiteralParameters)[0]).not.toBe(
88
+ Object.keys(second.objectLiteralParameters)[0],
89
+ );
90
+ });
91
+ });