@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,423 @@
1
+ import { DropdownOption } from "../../../UI/Components/Dropdown/Dropdown";
2
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
3
+ import Color from "../../../Types/Color";
4
+ import DropdownUtil from "../../../UI/Utils/Dropdown";
5
+ import { describe, expect, test } from "@jest/globals";
6
+
7
+ /*
8
+ * String enums do not emit a numeric reverse-mapping, so every key is a real
9
+ * member name. These stand in for the "English words shown verbatim" family
10
+ * (TechStack, CodeRepositoryType) that the source documents.
11
+ */
12
+ enum Fruit {
13
+ Apple = "apple",
14
+ Banana = "banana",
15
+ Cherry = "cherry",
16
+ }
17
+
18
+ /*
19
+ * A numeric enum. TypeScript compiles this to an object that also carries the
20
+ * reverse mapping { "0": "Up", "1": "Down", ... }, which is exactly the shape
21
+ * the util must strip out.
22
+ */
23
+ enum Direction {
24
+ Up,
25
+ Down,
26
+ Left,
27
+ Right,
28
+ }
29
+
30
+ /* A numeric enum with explicit, non-zero-based values. */
31
+ enum HttpStatus {
32
+ Ok = 200,
33
+ NotFound = 404,
34
+ }
35
+
36
+ /* A string enum whose value is a run of concatenated English words. */
37
+ enum TriggerReason {
38
+ OnErrorOrFrustration = "OnErrorOrFrustration",
39
+ }
40
+
41
+ /* A string enum whose value embeds an initialism run. */
42
+ enum SsoProtocol {
43
+ OidcProvider = "OIDCProvider",
44
+ }
45
+
46
+ /* A string enum whose value is a number-looking string. */
47
+ enum NumericStringValue {
48
+ Answer = "42",
49
+ }
50
+
51
+ /* A string enum whose value is the empty string. */
52
+ enum EmptyStringValue {
53
+ Blank = "",
54
+ }
55
+
56
+ /* An enum with no members at all. */
57
+ enum EmptyEnum {}
58
+
59
+ /*
60
+ * A hand-rolled stand-in for a BaseModel row. getDropdownOptionsFromEntityArray
61
+ * only ever touches getColumnValue and getFirstColorColumn, so faithfully
62
+ * reproducing those two is enough to exercise every branch without dragging in
63
+ * the decorator/metadata machinery that real models need.
64
+ */
65
+ interface StubColumnValues {
66
+ [columnName: string]: string | Color | null;
67
+ }
68
+
69
+ class EntityStub {
70
+ private readonly values: StubColumnValues;
71
+ private readonly colorColumn: string | null;
72
+
73
+ public constructor(
74
+ values: StubColumnValues,
75
+ colorColumn: string | null = null,
76
+ ) {
77
+ this.values = values;
78
+ this.colorColumn = colorColumn;
79
+ }
80
+
81
+ public getColumnValue(columnName: string): string | Color | null {
82
+ return columnName in this.values ? this.values[columnName]! : null;
83
+ }
84
+
85
+ public getFirstColorColumn(): string | null {
86
+ return this.colorColumn;
87
+ }
88
+ }
89
+
90
+ /* Turn a list of options into a plain [label, value] shape for terse asserts. */
91
+ type OptionPair = [string, DropdownOption["value"]];
92
+
93
+ const toPairs: (options: Array<DropdownOption>) => Array<OptionPair> = (
94
+ options: Array<DropdownOption>,
95
+ ): Array<OptionPair> => {
96
+ return options.map((option: DropdownOption): OptionPair => {
97
+ return [option.label, option.value];
98
+ });
99
+ };
100
+
101
+ describe("DropdownUtil", () => {
102
+ describe("getDropdownOptionsFromEnum", () => {
103
+ test("maps a string enum to value-labelled options in declaration order", () => {
104
+ const options: Array<DropdownOption> =
105
+ DropdownUtil.getDropdownOptionsFromEnum(Fruit);
106
+
107
+ expect(toPairs(options)).toEqual([
108
+ ["apple", "apple"],
109
+ ["banana", "banana"],
110
+ ["cherry", "cherry"],
111
+ ]);
112
+ });
113
+
114
+ test("uses the enum key as the label when useKeyAsLabel is true", () => {
115
+ const options: Array<DropdownOption> =
116
+ DropdownUtil.getDropdownOptionsFromEnum(Fruit, true);
117
+
118
+ expect(toPairs(options)).toEqual([
119
+ ["Apple", "apple"],
120
+ ["Banana", "banana"],
121
+ ["Cherry", "cherry"],
122
+ ]);
123
+ });
124
+
125
+ test("defaults useKeyAsLabel to false (value is used as the label)", () => {
126
+ const withDefault: Array<DropdownOption> =
127
+ DropdownUtil.getDropdownOptionsFromEnum(Fruit);
128
+ const explicitlyFalse: Array<DropdownOption> =
129
+ DropdownUtil.getDropdownOptionsFromEnum(Fruit, false);
130
+
131
+ expect(toPairs(withDefault)).toEqual(toPairs(explicitlyFalse));
132
+ expect(withDefault[0]!.label).toBe("apple");
133
+ });
134
+
135
+ test("strips the numeric reverse-mapping keys of a numeric enum", () => {
136
+ const options: Array<DropdownOption> =
137
+ DropdownUtil.getDropdownOptionsFromEnum(Direction);
138
+
139
+ /*
140
+ * Object.keys(Direction) is ["0","1","2","3","Up","Down","Left","Right"].
141
+ * The four numeric keys must be dropped, leaving four options. The label
142
+ * comes from the value (the number), the value is the coerced number.
143
+ */
144
+ expect(options).toHaveLength(4);
145
+ expect(toPairs(options)).toEqual([
146
+ ["0", 0],
147
+ ["1", 1],
148
+ ["2", 2],
149
+ ["3", 3],
150
+ ]);
151
+ });
152
+
153
+ test("keeps the member name as label for a numeric enum when useKeyAsLabel is true", () => {
154
+ const options: Array<DropdownOption> =
155
+ DropdownUtil.getDropdownOptionsFromEnum(Direction, true);
156
+
157
+ expect(toPairs(options)).toEqual([
158
+ ["Up", 0],
159
+ ["Down", 1],
160
+ ["Left", 2],
161
+ ["Right", 3],
162
+ ]);
163
+ });
164
+
165
+ test("coerces explicit numeric-enum values to numbers", () => {
166
+ const options: Array<DropdownOption> =
167
+ DropdownUtil.getDropdownOptionsFromEnum(HttpStatus, true);
168
+
169
+ expect(toPairs(options)).toEqual([
170
+ ["Ok", 200],
171
+ ["NotFound", 404],
172
+ ]);
173
+ /* The value is a real number, not the string "200". */
174
+ expect(typeof options[0]!.value).toBe("number");
175
+ });
176
+
177
+ test("converts a number-looking string value into a number", () => {
178
+ const options: Array<DropdownOption> =
179
+ DropdownUtil.getDropdownOptionsFromEnum(NumericStringValue);
180
+
181
+ expect(options).toHaveLength(1);
182
+ expect(options[0]!.label).toBe("42");
183
+ expect(options[0]!.value).toBe(42);
184
+ expect(typeof options[0]!.value).toBe("number");
185
+ });
186
+
187
+ test("treats an empty-string value as the number zero (Number('') === 0)", () => {
188
+ const options: Array<DropdownOption> =
189
+ DropdownUtil.getDropdownOptionsFromEnum(EmptyStringValue);
190
+
191
+ /*
192
+ * canBeConvertedToNumber("") is true because Number("") is 0, so the
193
+ * value is coerced to 0 while the label stays the empty string.
194
+ */
195
+ expect(options).toHaveLength(1);
196
+ expect(options[0]!.label).toBe("");
197
+ expect(options[0]!.value).toBe(0);
198
+ });
199
+
200
+ test("returns an empty array for an enum with no members", () => {
201
+ expect(DropdownUtil.getDropdownOptionsFromEnum(EmptyEnum)).toEqual([]);
202
+ });
203
+
204
+ test("returns a fresh array on each call", () => {
205
+ const first: Array<DropdownOption> =
206
+ DropdownUtil.getDropdownOptionsFromEnum(Fruit);
207
+ const second: Array<DropdownOption> =
208
+ DropdownUtil.getDropdownOptionsFromEnum(Fruit);
209
+
210
+ expect(first).not.toBe(second);
211
+ expect(first).toEqual(second);
212
+ });
213
+ });
214
+
215
+ describe("getDropdownOptionsFromEnumWithReadableLabels", () => {
216
+ test("spaces out a PascalCase value while leaving the stored value untouched", () => {
217
+ const options: Array<DropdownOption> =
218
+ DropdownUtil.getDropdownOptionsFromEnumWithReadableLabels(
219
+ TriggerReason,
220
+ );
221
+
222
+ expect(options).toHaveLength(1);
223
+ expect(options[0]!.label).toBe("On Error Or Frustration");
224
+ /* The value is the verbatim identifier, not the spaced-out label. */
225
+ expect(options[0]!.value).toBe("OnErrorOrFrustration");
226
+ });
227
+
228
+ test("keeps an initialism run together (OIDCProvider -> 'OIDC Provider')", () => {
229
+ const options: Array<DropdownOption> =
230
+ DropdownUtil.getDropdownOptionsFromEnumWithReadableLabels(SsoProtocol);
231
+
232
+ expect(options[0]!.label).toBe("OIDC Provider");
233
+ expect(options[0]!.value).toBe("OIDCProvider");
234
+ });
235
+
236
+ test("labels off the value, not the key, matching getDropdownOptionsFromEnum's default", () => {
237
+ /*
238
+ * Fruit's values are already lower-case single words, so the readable
239
+ * transform is a no-op and the label equals the value. This locks in the
240
+ * fact that the readable helper starts from the value-based labels.
241
+ */
242
+ const options: Array<DropdownOption> =
243
+ DropdownUtil.getDropdownOptionsFromEnumWithReadableLabels(Fruit);
244
+
245
+ expect(toPairs(options)).toEqual([
246
+ ["apple", "apple"],
247
+ ["banana", "banana"],
248
+ ["cherry", "cherry"],
249
+ ]);
250
+ });
251
+
252
+ test("returns an empty array for an enum with no members", () => {
253
+ expect(
254
+ DropdownUtil.getDropdownOptionsFromEnumWithReadableLabels(EmptyEnum),
255
+ ).toEqual([]);
256
+ });
257
+ });
258
+
259
+ describe("getDropdownOptionFromEnumForValue", () => {
260
+ test("returns the matching option for a present string value", () => {
261
+ const option: DropdownOption | undefined =
262
+ DropdownUtil.getDropdownOptionFromEnumForValue(Fruit, "banana");
263
+
264
+ expect(option).toBeDefined();
265
+ expect(option!.label).toBe("banana");
266
+ expect(option!.value).toBe("banana");
267
+ });
268
+
269
+ test("returns undefined when no option carries the value", () => {
270
+ const option: DropdownOption | undefined =
271
+ DropdownUtil.getDropdownOptionFromEnumForValue(Fruit, "durian");
272
+
273
+ expect(option).toBeUndefined();
274
+ });
275
+
276
+ test("returns undefined for a numeric enum searched by string, because values are coerced to numbers", () => {
277
+ /*
278
+ * Direction's option values are the numbers 0..3, and the lookup uses
279
+ * strict equality against the string parameter, so "0" never matches 0.
280
+ */
281
+ const option: DropdownOption | undefined =
282
+ DropdownUtil.getDropdownOptionFromEnumForValue(Direction, "0");
283
+
284
+ expect(option).toBeUndefined();
285
+ });
286
+
287
+ test("returns undefined when the enum has no members", () => {
288
+ expect(
289
+ DropdownUtil.getDropdownOptionFromEnumForValue(EmptyEnum, "anything"),
290
+ ).toBeUndefined();
291
+ });
292
+ });
293
+
294
+ describe("getDropdownOptionsFromEntityArray", () => {
295
+ test("maps each row's label and value columns", () => {
296
+ const array: Array<BaseModel> = [
297
+ new EntityStub({ name: "Acme", id: "row-1" }),
298
+ new EntityStub({ name: "Globex", id: "row-2" }),
299
+ ] as unknown as Array<BaseModel>;
300
+
301
+ const options: Array<DropdownOption> =
302
+ DropdownUtil.getDropdownOptionsFromEntityArray({
303
+ array: array,
304
+ labelField: "name",
305
+ valueField: "id",
306
+ });
307
+
308
+ expect(toPairs(options)).toEqual([
309
+ ["Acme", "row-1"],
310
+ ["Globex", "row-2"],
311
+ ]);
312
+ /* No color column, so no color is attached. */
313
+ expect(options[0]!.color).toBeUndefined();
314
+ });
315
+
316
+ test("attaches the color when the row exposes a populated color column", () => {
317
+ const color: Color = new Color("#112233");
318
+ const array: Array<BaseModel> = [
319
+ new EntityStub({ name: "Priority", id: "p-1", color: color }, "color"),
320
+ ] as unknown as Array<BaseModel>;
321
+
322
+ const options: Array<DropdownOption> =
323
+ DropdownUtil.getDropdownOptionsFromEntityArray({
324
+ array: array,
325
+ labelField: "name",
326
+ valueField: "id",
327
+ });
328
+
329
+ expect(options[0]!.color).toBe(color);
330
+ expect(options[0]!.color!.toString()).toBe("#112233");
331
+ });
332
+
333
+ test("omits the color when the named color column resolves to null", () => {
334
+ const array: Array<BaseModel> = [
335
+ new EntityStub({ name: "Priority", id: "p-1", color: null }, "color"),
336
+ ] as unknown as Array<BaseModel>;
337
+
338
+ const options: Array<DropdownOption> =
339
+ DropdownUtil.getDropdownOptionsFromEntityArray({
340
+ array: array,
341
+ labelField: "name",
342
+ valueField: "id",
343
+ });
344
+
345
+ expect(options).toHaveLength(1);
346
+ expect(options[0]!.color).toBeUndefined();
347
+ });
348
+
349
+ test("does not crash and adds no color when getFirstColorColumn is not a function", () => {
350
+ /*
351
+ * The source guards the color lookup behind a typeof === "function"
352
+ * check, so a row lacking the method entirely still maps cleanly.
353
+ */
354
+ const rowWithoutColorMethod: { getColumnValue(c: string): string } = {
355
+ getColumnValue: (columnName: string): string => {
356
+ return columnName === "name" ? "Legacy" : "legacy-1";
357
+ },
358
+ };
359
+ const array: Array<BaseModel> = [
360
+ rowWithoutColorMethod,
361
+ ] as unknown as Array<BaseModel>;
362
+
363
+ const options: Array<DropdownOption> =
364
+ DropdownUtil.getDropdownOptionsFromEntityArray({
365
+ array: array,
366
+ labelField: "name",
367
+ valueField: "id",
368
+ });
369
+
370
+ expect(toPairs(options)).toEqual([["Legacy", "legacy-1"]]);
371
+ expect(options[0]!.color).toBeUndefined();
372
+ });
373
+
374
+ test("returns an empty array for an empty input array", () => {
375
+ const options: Array<DropdownOption> =
376
+ DropdownUtil.getDropdownOptionsFromEntityArray({
377
+ array: [],
378
+ labelField: "name",
379
+ valueField: "id",
380
+ });
381
+
382
+ expect(options).toEqual([]);
383
+ });
384
+ });
385
+
386
+ describe("getDropdownOptionsFromArray", () => {
387
+ test("maps each string to an option whose label and value are that string", () => {
388
+ const options: Array<DropdownOption> =
389
+ DropdownUtil.getDropdownOptionsFromArray(["red", "green", "blue"]);
390
+
391
+ expect(toPairs(options)).toEqual([
392
+ ["red", "red"],
393
+ ["green", "green"],
394
+ ["blue", "blue"],
395
+ ]);
396
+ });
397
+
398
+ test("de-duplicates while preserving first-seen order", () => {
399
+ const options: Array<DropdownOption> =
400
+ DropdownUtil.getDropdownOptionsFromArray(["a", "b", "a", "c", "b"]);
401
+
402
+ expect(toPairs(options)).toEqual([
403
+ ["a", "a"],
404
+ ["b", "b"],
405
+ ["c", "c"],
406
+ ]);
407
+ });
408
+
409
+ test("keeps a single empty-string entry rather than dropping it", () => {
410
+ const options: Array<DropdownOption> =
411
+ DropdownUtil.getDropdownOptionsFromArray(["", "x", ""]);
412
+
413
+ expect(toPairs(options)).toEqual([
414
+ ["", ""],
415
+ ["x", "x"],
416
+ ]);
417
+ });
418
+
419
+ test("returns an empty array for an empty input", () => {
420
+ expect(DropdownUtil.getDropdownOptionsFromArray([])).toEqual([]);
421
+ });
422
+ });
423
+ });
@@ -0,0 +1,122 @@
1
+ import { afterEach, describe, expect, test } from "@jest/globals";
2
+ import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
3
+ import ProjectUtil from "../../../UI/Utils/Project";
4
+ import Project from "../../../Models/DatabaseModels/Project";
5
+ import Dictionary from "../../../Types/Dictionary";
6
+ import ObjectID from "../../../Types/ObjectID";
7
+
8
+ /*
9
+ * getCommonHeaders runs on EVERY model request (create/update/getList/count/
10
+ * getItem/delete) and used to hydrate the entire ~179-column Project model
11
+ * from LocalStorage just to read the tenant id. It now reads the id via
12
+ * ProjectUtil.getCurrentProjectId() - a plain string read - and only falls
13
+ * back to the full getCurrentProject() hydration when no id is available.
14
+ *
15
+ * The priority-order contract these tests pin:
16
+ * 1. getCurrentProjectId() wins whenever it yields an id, and the expensive
17
+ * getCurrentProject() hydration must NOT run at all (the perf win).
18
+ * 2. The SSO-login window keeps working: the project id is already in
19
+ * URL/SessionStorage but the full project JSON has not been fetched yet -
20
+ * the tenant id header must still be sent.
21
+ * 3. The historical fallback to the hydrated project is preserved for the
22
+ * case where the id read yields nothing.
23
+ * 4. Multi-tenant requests carry no tenant id at all.
24
+ */
25
+
26
+ const PROJECT_ID: string = "019acd20-1111-4111-8111-111111111111";
27
+ const FALLBACK_PROJECT_ID: string = "019acd20-2222-4222-8222-222222222222";
28
+
29
+ type BuildProjectFunction = (id: string) => Project;
30
+
31
+ const buildProject: BuildProjectFunction = (id: string): Project => {
32
+ const project: Project = new Project();
33
+ project.id = new ObjectID(id);
34
+
35
+ return project;
36
+ };
37
+
38
+ describe("ModelAPI.getCommonHeaders tenant id resolution", () => {
39
+ afterEach(() => {
40
+ jest.restoreAllMocks();
41
+ });
42
+
43
+ test("reads the tenant id from getCurrentProjectId() without hydrating the full project", () => {
44
+ jest
45
+ .spyOn(ProjectUtil, "getCurrentProjectId")
46
+ .mockReturnValue(new ObjectID(PROJECT_ID));
47
+
48
+ const getCurrentProject: jest.SpyInstance = jest
49
+ .spyOn(ProjectUtil, "getCurrentProject")
50
+ .mockReturnValue(buildProject(PROJECT_ID));
51
+
52
+ const headers: Dictionary<string> = ModelAPI.getCommonHeaders();
53
+
54
+ expect(headers["tenantid"]).toBe(PROJECT_ID);
55
+
56
+ /*
57
+ * The whole point of the change: the cheap string read is enough, so the
58
+ * BaseModel.fromJSON hydration of the stored project must not run.
59
+ */
60
+ expect(getCurrentProject).not.toHaveBeenCalled();
61
+ });
62
+
63
+ test("still sends the tenant id in the SSO-login window (id known, project data not fetched yet)", () => {
64
+ /*
65
+ * After SSO login the project id is available in URL/SessionStorage but
66
+ * localStorage has no project JSON yet, so getCurrentProject() would
67
+ * return null. The header must be sent regardless.
68
+ */
69
+ jest
70
+ .spyOn(ProjectUtil, "getCurrentProjectId")
71
+ .mockReturnValue(new ObjectID(PROJECT_ID));
72
+
73
+ jest.spyOn(ProjectUtil, "getCurrentProject").mockReturnValue(null);
74
+
75
+ const headers: Dictionary<string> = ModelAPI.getCommonHeaders();
76
+
77
+ expect(headers["tenantid"]).toBe(PROJECT_ID);
78
+ });
79
+
80
+ test("falls back to the hydrated project when no project id can be read", () => {
81
+ jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(null);
82
+
83
+ const getCurrentProject: jest.SpyInstance = jest
84
+ .spyOn(ProjectUtil, "getCurrentProject")
85
+ .mockReturnValue(buildProject(FALLBACK_PROJECT_ID));
86
+
87
+ const headers: Dictionary<string> = ModelAPI.getCommonHeaders();
88
+
89
+ expect(headers["tenantid"]).toBe(FALLBACK_PROJECT_ID);
90
+ expect(getCurrentProject).toHaveBeenCalledTimes(1);
91
+ });
92
+
93
+ test("sends no tenant id when neither source yields a project", () => {
94
+ jest.spyOn(ProjectUtil, "getCurrentProjectId").mockReturnValue(null);
95
+ jest.spyOn(ProjectUtil, "getCurrentProject").mockReturnValue(null);
96
+
97
+ const headers: Dictionary<string> = ModelAPI.getCommonHeaders();
98
+
99
+ expect(headers).not.toHaveProperty("tenantid");
100
+ });
101
+
102
+ test("multi-tenant requests carry the multi-tenant flag instead of a tenant id", () => {
103
+ const getCurrentProjectId: jest.SpyInstance = jest
104
+ .spyOn(ProjectUtil, "getCurrentProjectId")
105
+ .mockReturnValue(new ObjectID(PROJECT_ID));
106
+
107
+ const getCurrentProject: jest.SpyInstance = jest
108
+ .spyOn(ProjectUtil, "getCurrentProject")
109
+ .mockReturnValue(buildProject(PROJECT_ID));
110
+
111
+ const headers: Dictionary<string> = ModelAPI.getCommonHeaders({
112
+ isMultiTenantRequest: true,
113
+ });
114
+
115
+ expect(headers).not.toHaveProperty("tenantid");
116
+ expect(headers["is-multi-tenant-query"]).toBe("true");
117
+
118
+ // a multi-tenant request should not read the project at all.
119
+ expect(getCurrentProjectId).not.toHaveBeenCalled();
120
+ expect(getCurrentProject).not.toHaveBeenCalled();
121
+ });
122
+ });