@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.
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
- package/Models/DatabaseModels/UserCall.ts +94 -3
- package/Models/DatabaseModels/UserEmail.ts +94 -3
- package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
- package/Models/DatabaseModels/UserPush.ts +37 -0
- package/Models/DatabaseModels/UserSMS.ts +94 -3
- package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
- package/Server/API/BaseAPI.ts +79 -14
- package/Server/API/UserCallAPI.ts +53 -45
- package/Server/API/UserEmailAPI.ts +53 -44
- package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
- package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
- package/Server/API/UserPushAPI.ts +126 -2
- package/Server/API/UserSmsAPI.ts +53 -44
- package/Server/API/UserWhatsAppAPI.ts +53 -50
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
- package/Server/Services/AnalyticsDatabaseService.ts +169 -0
- package/Server/Services/DatabaseService.ts +101 -0
- package/Server/Services/LogAggregationService.ts +5 -0
- package/Server/Services/PushNotificationService.ts +127 -12
- package/Server/Services/TeamMemberService.ts +53 -13
- package/Server/Services/UserCallService.ts +55 -24
- package/Server/Services/UserEmailService.ts +83 -17
- package/Server/Services/UserIncomingCallNumberService.ts +49 -85
- package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
- package/Server/Services/UserNotificationRuleService.ts +26 -0
- package/Server/Services/UserPushService.ts +89 -0
- package/Server/Services/UserSmsService.ts +50 -17
- package/Server/Services/UserWhatsAppService.ts +48 -15
- package/Server/Types/Database/QueryHelper.ts +30 -0
- package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
- package/Server/Utils/ChannelVerification.ts +510 -0
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +13 -16
- package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
- package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
- package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
- package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
- package/Server/Utils/TotpAuth.ts +137 -9
- package/Server/Utils/UserRegistrationToken.ts +182 -0
- package/Server/Utils/VerificationCode.ts +134 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
- package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
- package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
- package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
- package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
- package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
- package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
- package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
- package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
- package/Tests/Server/API/UserSmsApi.test.ts +107 -14
- package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
- package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +315 -0
- package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
- package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
- package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
- package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
- package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
- package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
- package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
- package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
- package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
- package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
- package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
- package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
- package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
- package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
- package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
- package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
- package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
- package/Tests/Server/Utils/Browser.test.ts +162 -0
- package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
- package/Tests/Server/Utils/Express.test.ts +371 -0
- package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
- package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
- package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
- package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
- package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
- package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
- package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
- package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
- package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
- package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
- package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
- package/Tests/Types/Call/CallRequest.test.ts +189 -0
- package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
- package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
- package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
- package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
- package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
- package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
- package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
- package/Tests/Types/TextRandomGeneration.test.ts +211 -0
- package/Tests/UI/Components/Button.test.tsx +175 -1
- package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
- package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
- package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
- package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
- package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
- package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
- package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
- package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
- package/Tests/UI/Components/List.test.tsx +9 -2
- package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
- package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
- package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
- package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
- package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
- package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
- package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
- package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
- package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
- package/Tests/UI/Components/Skeleton.test.tsx +71 -0
- package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
- package/Tests/UI/Components/Tabs.test.tsx +24 -0
- package/Tests/UI/Components/Toast.test.tsx +130 -7
- package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
- package/Tests/UI/Utils/Dropdown.test.ts +423 -0
- package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
- package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
- package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
- package/Tests/Utils/API.test.ts +16 -2
- package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
- package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
- package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
- package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
- package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
- package/Types/Permission.ts +35 -0
- package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
- package/Types/PushNotification/PushNotificationMessage.ts +15 -0
- package/Types/PushNotification/PushNotificationRequest.ts +8 -16
- package/Types/Service/ServiceLanguage.ts +144 -0
- package/Types/Text.ts +111 -19
- package/UI/Components/Button/Button.tsx +27 -14
- package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
- package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
- package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
- package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
- package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
- package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
- package/UI/Components/CommandPalette/Types.ts +49 -0
- package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
- package/UI/Components/Dropdown/Dropdown.tsx +6 -1
- package/UI/Components/Feed/FeedItem.tsx +1 -1
- package/UI/Components/List/List.tsx +37 -5
- package/UI/Components/List/ListSkeleton.tsx +54 -0
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
- package/UI/Components/Modal/Modal.tsx +2 -2
- package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
- package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
- package/UI/Components/Navbar/NavBar.tsx +14 -1
- package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
- package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
- package/UI/Components/SideOver/SideOver.tsx +37 -2
- package/UI/Components/Skeleton/Skeleton.tsx +53 -0
- package/UI/Components/Table/Table.tsx +74 -44
- package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
- package/UI/Components/Tabs/Tab.tsx +1 -1
- package/UI/Components/Toast/Toast.tsx +118 -53
- package/UI/Components/Toast/ToastInit.tsx +82 -32
- package/UI/Styles/Theme.css +40 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
- package/UI/Utils/ModelListCache.ts +188 -0
- package/UI/Utils/Project.ts +29 -0
- package/Utils/API.ts +6 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
- package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
- package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
- package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
- package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +69 -13
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/UserCallAPI.js +42 -38
- package/build/dist/Server/API/UserCallAPI.js.map +1 -1
- package/build/dist/Server/API/UserEmailAPI.js +42 -38
- package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
- package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
- package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
- package/build/dist/Server/API/UserPushAPI.js +95 -2
- package/build/dist/Server/API/UserPushAPI.js.map +1 -1
- package/build/dist/Server/API/UserSmsAPI.js +42 -38
- package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
- package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
- package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
- package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +74 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +5 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +69 -30
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +43 -7
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/UserCallService.js +54 -22
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +78 -17
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
- package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +77 -0
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/UserSmsService.js +50 -17
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
- package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
- package/build/dist/Server/Utils/ChannelVerification.js +348 -0
- package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +13 -12
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
- package/build/dist/Server/Utils/TotpAuth.js +127 -8
- package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
- package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
- package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
- package/build/dist/Server/Utils/VerificationCode.js +120 -0
- package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +33 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
- package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
- package/build/dist/Types/Service/ServiceLanguage.js +96 -0
- package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
- package/build/dist/Types/Text.js +82 -17
- package/build/dist/Types/Text.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +22 -14
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
- package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
- package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
- package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
- package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
- package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
- package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
- package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
- package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
- package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
- package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
- package/build/dist/UI/Components/List/List.js +19 -5
- package/build/dist/UI/Components/List/List.js.map +1 -1
- package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
- package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
- package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
- package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
- package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
- package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
- package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
- package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +35 -24
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
- package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
- package/build/dist/UI/Components/Tabs/Tab.js +1 -1
- package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
- package/build/dist/UI/Components/Toast/Toast.js +77 -23
- package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
- package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelListCache.js +126 -0
- package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
- package/build/dist/UI/Utils/Project.js +24 -1
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/Utils/API.js +6 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/jest.config.json +1 -1
- package/package.json +2 -3
- package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
- package/Tests/__mocks__/otpauth.js +0 -30
- package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
- package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import PushNotificationService, {
|
|
2
|
+
type ExpoDeliveryOptions,
|
|
3
|
+
} from "../../../Server/Services/PushNotificationService";
|
|
4
|
+
import PushNotificationMessage from "../../../Types/PushNotification/PushNotificationMessage";
|
|
5
|
+
import PushDeviceType from "../../../Types/PushNotification/PushDeviceType";
|
|
6
|
+
import AndroidNotificationChannel from "../../../Types/PushNotification/AndroidNotificationChannel";
|
|
7
|
+
import PushNotificationUtil from "../../../Server/Utils/PushNotificationUtil";
|
|
8
|
+
import { describe, expect, test } from "@jest/globals";
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* A critical alert is the only notification OneUptime sends that is allowed to
|
|
12
|
+
* ring a phone its owner has silenced, and the two mobile platforms grant that
|
|
13
|
+
* through completely different fields. iOS reads the payload's `sound` object
|
|
14
|
+
* and `interruptionLevel`; Android ignores both and obeys the notification
|
|
15
|
+
* CHANNEL the payload names.
|
|
16
|
+
*
|
|
17
|
+
* getExpoDeliveryOptions is where that split is decided, and every case below
|
|
18
|
+
* is a way a page could arrive silently at 3am if it drifted:
|
|
19
|
+
*
|
|
20
|
+
* - a critical iOS page without `sound.critical` is a normal notification
|
|
21
|
+
* that the ringer switch mutes;
|
|
22
|
+
* - a critical Android page on the oncall_high channel is a normal
|
|
23
|
+
* notification that Do Not Disturb mutes;
|
|
24
|
+
* - and a NON-critical page that quietly gained either would override the
|
|
25
|
+
* ringer for owner subscriptions and note-posted notices, which is the
|
|
26
|
+
* mirror-image failure: users who turn the app's notifications off
|
|
27
|
+
* entirely because it woke them for something that could have waited.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
function messageWith(isCriticalAlert?: boolean): PushNotificationMessage {
|
|
31
|
+
const message: PushNotificationMessage = {
|
|
32
|
+
title: "Incident #42: Checkout is down",
|
|
33
|
+
body: "A new incident has been created.",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
if (isCriticalAlert !== undefined) {
|
|
37
|
+
message.isCriticalAlert = isCriticalAlert;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return message;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("getExpoDeliveryOptions - critical alerts on iOS", () => {
|
|
44
|
+
test("sends the critical sound object APNs needs to bypass the ringer switch", () => {
|
|
45
|
+
const delivery: ExpoDeliveryOptions =
|
|
46
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
47
|
+
messageWith(true),
|
|
48
|
+
PushDeviceType.iOS,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(delivery.sound).toEqual({
|
|
52
|
+
critical: true,
|
|
53
|
+
name: "default",
|
|
54
|
+
volume: 1,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("sets interruptionLevel critical so Focus and Do Not Disturb are bypassed", () => {
|
|
59
|
+
const delivery: ExpoDeliveryOptions =
|
|
60
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
61
|
+
messageWith(true),
|
|
62
|
+
PushDeviceType.iOS,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
expect(delivery.interruptionLevel).toBe("critical");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("plays at full volume - a page that wakes nobody has not been delivered", () => {
|
|
69
|
+
const delivery: ExpoDeliveryOptions =
|
|
70
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
71
|
+
messageWith(true),
|
|
72
|
+
PushDeviceType.iOS,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
expect((delivery.sound as { volume?: number }).volume).toBe(
|
|
76
|
+
PushNotificationService.CRITICAL_ALERT_VOLUME,
|
|
77
|
+
);
|
|
78
|
+
expect(PushNotificationService.CRITICAL_ALERT_VOLUME).toBe(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("keeps iOS off Android channel ids", () => {
|
|
82
|
+
const delivery: ExpoDeliveryOptions =
|
|
83
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
84
|
+
messageWith(true),
|
|
85
|
+
PushDeviceType.iOS,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(delivery.channelId).toBe("default");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("getExpoDeliveryOptions - critical alerts on Android", () => {
|
|
93
|
+
test("routes to the channel that carries the Do Not Disturb bypass", () => {
|
|
94
|
+
const delivery: ExpoDeliveryOptions =
|
|
95
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
96
|
+
messageWith(true),
|
|
97
|
+
PushDeviceType.Android,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(delivery.channelId).toBe(AndroidNotificationChannel.Critical);
|
|
101
|
+
expect(delivery.channelId).toBe("oncall_critical");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("still carries the critical sound object, which Android ignores harmlessly", () => {
|
|
105
|
+
const delivery: ExpoDeliveryOptions =
|
|
106
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
107
|
+
messageWith(true),
|
|
108
|
+
PushDeviceType.Android,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(delivery.sound).toEqual({
|
|
112
|
+
critical: true,
|
|
113
|
+
name: "default",
|
|
114
|
+
volume: 1,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("getExpoDeliveryOptions - ordinary notifications are left alone", () => {
|
|
120
|
+
test.each([
|
|
121
|
+
["explicitly false", false],
|
|
122
|
+
["absent", undefined],
|
|
123
|
+
])(
|
|
124
|
+
"an iOS message with isCriticalAlert %s gets the plain default sound",
|
|
125
|
+
(_label: string, flag: boolean | undefined) => {
|
|
126
|
+
const delivery: ExpoDeliveryOptions =
|
|
127
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
128
|
+
messageWith(flag),
|
|
129
|
+
PushDeviceType.iOS,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(delivery.sound).toBe("default");
|
|
133
|
+
expect(delivery.interruptionLevel).toBeUndefined();
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
test.each([
|
|
138
|
+
["explicitly false", false],
|
|
139
|
+
["absent", undefined],
|
|
140
|
+
])(
|
|
141
|
+
"an Android message with isCriticalAlert %s stays on oncall_high",
|
|
142
|
+
(_label: string, flag: boolean | undefined) => {
|
|
143
|
+
const delivery: ExpoDeliveryOptions =
|
|
144
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
145
|
+
messageWith(flag),
|
|
146
|
+
PushDeviceType.Android,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(delivery.channelId).toBe(AndroidNotificationChannel.High);
|
|
150
|
+
expect(delivery.channelId).toBe("oncall_high");
|
|
151
|
+
expect(delivery.sound).toBe("default");
|
|
152
|
+
expect(delivery.interruptionLevel).toBeUndefined();
|
|
153
|
+
},
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
test("the Android channel for a non-critical page is unchanged from before this feature", () => {
|
|
157
|
+
/*
|
|
158
|
+
* Pinned as a literal on purpose. Every device already in the field has an
|
|
159
|
+
* oncall_high channel; renaming the default target would strand them on a
|
|
160
|
+
* channel their app never created, which Android answers by delivering the
|
|
161
|
+
* page with default settings rather than by failing.
|
|
162
|
+
*/
|
|
163
|
+
const delivery: ExpoDeliveryOptions =
|
|
164
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
165
|
+
messageWith(false),
|
|
166
|
+
PushDeviceType.Android,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
expect(delivery.channelId).toBe("oncall_high");
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("getExpoDeliveryOptions - properties that hold for every message", () => {
|
|
174
|
+
const cases: Array<[string, PushDeviceType, boolean]> = [
|
|
175
|
+
["iOS critical", PushDeviceType.iOS, true],
|
|
176
|
+
["iOS normal", PushDeviceType.iOS, false],
|
|
177
|
+
["Android critical", PushDeviceType.Android, true],
|
|
178
|
+
["Android normal", PushDeviceType.Android, false],
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
test.each(cases)(
|
|
182
|
+
"%s is sent at high priority so it is not batched by the platform",
|
|
183
|
+
(_label: string, deviceType: PushDeviceType, isCritical: boolean) => {
|
|
184
|
+
const delivery: ExpoDeliveryOptions =
|
|
185
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
186
|
+
messageWith(isCritical),
|
|
187
|
+
deviceType,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
expect(delivery.priority).toBe("high");
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
test.each(cases)(
|
|
195
|
+
"%s always names a channel, so no page falls back to Android's default",
|
|
196
|
+
(_label: string, deviceType: PushDeviceType, isCritical: boolean) => {
|
|
197
|
+
const delivery: ExpoDeliveryOptions =
|
|
198
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
199
|
+
messageWith(isCritical),
|
|
200
|
+
deviceType,
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
expect(typeof delivery.channelId).toBe("string");
|
|
204
|
+
expect(delivery.channelId.length).toBeGreaterThan(0);
|
|
205
|
+
},
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
test("never silences a notification - sound is never null", () => {
|
|
209
|
+
for (const [, deviceType, isCritical] of cases) {
|
|
210
|
+
const delivery: ExpoDeliveryOptions =
|
|
211
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
212
|
+
messageWith(isCritical),
|
|
213
|
+
deviceType,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
expect(delivery.sound).not.toBeNull();
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("the flag is read as a boolean, so a truthy non-boolean does not leak through as critical", () => {
|
|
221
|
+
/*
|
|
222
|
+
* The column is a boolean and the API parses it as one, but this method is
|
|
223
|
+
* also reachable from callers that build a message by hand. "yes" is not
|
|
224
|
+
* true.
|
|
225
|
+
*/
|
|
226
|
+
const message: PushNotificationMessage = messageWith();
|
|
227
|
+
(message as unknown as { isCriticalAlert: unknown }).isCriticalAlert =
|
|
228
|
+
"yes";
|
|
229
|
+
|
|
230
|
+
const delivery: ExpoDeliveryOptions =
|
|
231
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
232
|
+
message,
|
|
233
|
+
PushDeviceType.iOS,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
/*
|
|
237
|
+
* Boolean("yes") is true, so this DOES escalate. Asserted rather than
|
|
238
|
+
* wished away: the guarantee is that the boolean-ness is enforced at the
|
|
239
|
+
* edges (the API parser and the database column), not here.
|
|
240
|
+
*/
|
|
241
|
+
expect(delivery.interruptionLevel).toBe("critical");
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("Messages built by PushNotificationUtil are not critical by default", () => {
|
|
246
|
+
/*
|
|
247
|
+
* The factory is shared by the on-call path and by owner subscriptions. Only
|
|
248
|
+
* the on-call path may escalate, and it does so explicitly after the fact -
|
|
249
|
+
* so a factory that started returning isCriticalAlert would silently make
|
|
250
|
+
* every "note posted" notification override Do Not Disturb.
|
|
251
|
+
*/
|
|
252
|
+
test("createIncidentCreatedNotification leaves the flag unset", () => {
|
|
253
|
+
const message: PushNotificationMessage =
|
|
254
|
+
PushNotificationUtil.createIncidentCreatedNotification({
|
|
255
|
+
incidentTitle: "Checkout is down",
|
|
256
|
+
projectName: "Acme",
|
|
257
|
+
incidentViewLink: "https://dashboard.example.com/incident/1",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
expect(message.isCriticalAlert).toBeUndefined();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("createAlertCreatedNotification leaves the flag unset", () => {
|
|
264
|
+
const message: PushNotificationMessage =
|
|
265
|
+
PushNotificationUtil.createAlertCreatedNotification({
|
|
266
|
+
alertTitle: "Disk almost full",
|
|
267
|
+
projectName: "Acme",
|
|
268
|
+
alertViewLink: "https://dashboard.example.com/alert/1",
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
expect(message.isCriticalAlert).toBeUndefined();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("createGenericNotification leaves the flag unset", () => {
|
|
275
|
+
const message: PushNotificationMessage =
|
|
276
|
+
PushNotificationUtil.createGenericNotification({
|
|
277
|
+
title: "Test Notification from OneUptime",
|
|
278
|
+
body: "This is a test.",
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
expect(message.isCriticalAlert).toBeUndefined();
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("a factory-built message delivers as a normal notification", () => {
|
|
285
|
+
const message: PushNotificationMessage =
|
|
286
|
+
PushNotificationUtil.createIncidentCreatedNotification({
|
|
287
|
+
incidentTitle: "Checkout is down",
|
|
288
|
+
projectName: "Acme",
|
|
289
|
+
incidentViewLink: "https://dashboard.example.com/incident/1",
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const delivery: ExpoDeliveryOptions =
|
|
293
|
+
PushNotificationService.getExpoDeliveryOptions(
|
|
294
|
+
message,
|
|
295
|
+
PushDeviceType.Android,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
expect(delivery.channelId).toBe("oncall_high");
|
|
299
|
+
expect(delivery.sound).toBe("default");
|
|
300
|
+
});
|
|
301
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import UserEmailService from "../../../Server/Services/UserEmailService";
|
|
2
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
3
|
+
import { beforeEach, describe, expect, it } from "@jest/globals";
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* atomicIncrementColumnValueByOneAndGetValue, the primitive that makes the
|
|
7
|
+
* verification attempt limit safe under concurrency (GHSA-5cr8-vph4-3hrf).
|
|
8
|
+
*
|
|
9
|
+
* The property that matters is that the increment and the observation are ONE
|
|
10
|
+
* statement. A read-modify-write lets N racing requests all see the same
|
|
11
|
+
* pre-increment value and all conclude they are inside the limit — which is
|
|
12
|
+
* the exact shape of a brute-force loop, so a counter with that hole is not a
|
|
13
|
+
* counter. These tests pin the statement shape and the reply parsing; the
|
|
14
|
+
* behaviour it buys is exercised end to end in
|
|
15
|
+
* Tests/Server/Utils/ChannelVerification.test.ts.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
interface CapturedQuery {
|
|
19
|
+
sql: string;
|
|
20
|
+
params: Array<unknown>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const buildRepository: (data: {
|
|
24
|
+
reply: unknown;
|
|
25
|
+
captured: Array<CapturedQuery>;
|
|
26
|
+
}) => unknown = (data: { reply: unknown; captured: Array<CapturedQuery> }) => {
|
|
27
|
+
return {
|
|
28
|
+
metadata: {
|
|
29
|
+
tableName: "UserEmail",
|
|
30
|
+
primaryColumns: [{ databaseName: "_id" }],
|
|
31
|
+
findColumnWithPropertyName: (propertyName: string) => {
|
|
32
|
+
if (propertyName === "verificationFailedAttempts") {
|
|
33
|
+
return { databaseName: "verificationFailedAttempts" };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return undefined;
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
manager: {
|
|
40
|
+
query: (sql: string, params: Array<unknown>) => {
|
|
41
|
+
data.captured.push({ sql, params });
|
|
42
|
+
return Promise.resolve(data.reply);
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const ITEM_ID: ObjectID = new ObjectID("6b2a1f00-1111-4111-8111-111111111111");
|
|
49
|
+
|
|
50
|
+
describe("DatabaseService.atomicIncrementColumnValueByOneAndGetValue", () => {
|
|
51
|
+
let captured: Array<CapturedQuery>;
|
|
52
|
+
|
|
53
|
+
const run: (reply: unknown) => Promise<number> = (reply: unknown) => {
|
|
54
|
+
UserEmailService.getRepository = jest
|
|
55
|
+
.fn()
|
|
56
|
+
.mockReturnValue(buildRepository({ reply, captured })) as never;
|
|
57
|
+
|
|
58
|
+
return UserEmailService.atomicIncrementColumnValueByOneAndGetValue({
|
|
59
|
+
id: ITEM_ID,
|
|
60
|
+
columnName: "verificationFailedAttempts",
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
captured = [];
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("increments and reads back in a single statement", async () => {
|
|
69
|
+
await run([[{ verificationFailedAttempts: 3 }], 1]);
|
|
70
|
+
|
|
71
|
+
expect(captured).toHaveLength(1);
|
|
72
|
+
|
|
73
|
+
const sql: string = captured[0]?.sql as string;
|
|
74
|
+
|
|
75
|
+
expect(sql).toContain('UPDATE "UserEmail"');
|
|
76
|
+
expect(sql).toContain(
|
|
77
|
+
'"verificationFailedAttempts" = COALESCE("verificationFailedAttempts", 0) + 1',
|
|
78
|
+
);
|
|
79
|
+
expect(sql).toContain('RETURNING "verificationFailedAttempts"');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("binds the id rather than interpolating it", async () => {
|
|
83
|
+
await run([[{ verificationFailedAttempts: 1 }], 1]);
|
|
84
|
+
|
|
85
|
+
expect(captured[0]?.sql).toContain('WHERE "_id" = $1');
|
|
86
|
+
expect(captured[0]?.params).toEqual([ITEM_ID.toString()]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("returns the post-increment value", async () => {
|
|
90
|
+
expect(await run([[{ verificationFailedAttempts: 4 }], 1])).toBe(4);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* TypeORM wraps UPDATE replies as [rows, affectedCount]; other shapes come
|
|
95
|
+
* back as a bare row array. Both have to parse.
|
|
96
|
+
*/
|
|
97
|
+
it("understands a bare row array too", async () => {
|
|
98
|
+
expect(await run([{ verificationFailedAttempts: 7 }])).toBe(7);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("parses a count the driver returned as a string", async () => {
|
|
102
|
+
expect(await run([[{ verificationFailedAttempts: "9" }], 1])).toBe(9);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* A missing row must be an error, never a zero — the caller gates on this
|
|
107
|
+
* number, and a zero reads as a fresh attempt budget.
|
|
108
|
+
*/
|
|
109
|
+
it("throws when the row does not exist", async () => {
|
|
110
|
+
await expect(run([[], 0])).rejects.toThrow("not found");
|
|
111
|
+
await expect(run([])).rejects.toThrow("not found");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("throws when the reply carries no usable number", async () => {
|
|
115
|
+
await expect(
|
|
116
|
+
run([[{ verificationFailedAttempts: "not-a-number" }], 1]),
|
|
117
|
+
).rejects.toThrow("did not return a number");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("refuses a column that is not on the entity", async () => {
|
|
121
|
+
UserEmailService.getRepository = jest
|
|
122
|
+
.fn()
|
|
123
|
+
.mockReturnValue(buildRepository({ reply: [], captured })) as never;
|
|
124
|
+
|
|
125
|
+
await expect(
|
|
126
|
+
UserEmailService.atomicIncrementColumnValueByOneAndGetValue({
|
|
127
|
+
id: ITEM_ID,
|
|
128
|
+
columnName: "notAColumn" as never,
|
|
129
|
+
}),
|
|
130
|
+
).rejects.toThrow("unknown column");
|
|
131
|
+
|
|
132
|
+
expect(captured).toHaveLength(0);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("requires an id", async () => {
|
|
136
|
+
await expect(
|
|
137
|
+
UserEmailService.atomicIncrementColumnValueByOneAndGetValue({
|
|
138
|
+
id: undefined as unknown as ObjectID,
|
|
139
|
+
columnName: "verificationFailedAttempts",
|
|
140
|
+
}),
|
|
141
|
+
).rejects.toThrow("id is required");
|
|
142
|
+
});
|
|
143
|
+
});
|