@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,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
|
+
});
|