@oneuptime/common 13.0.7 → 13.0.8
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/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
- package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
- package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
- package/Server/API/AIBillingAPI.ts +1 -0
- package/Server/API/AIInsightAPI.ts +1 -4
- package/Server/API/AIInvestigationAPI.ts +1 -4
- package/Server/API/AIReadinessAPI.ts +4 -5
- package/Server/API/AlertAPI.ts +1 -0
- package/Server/API/AutoRemediationAPI.ts +1 -4
- package/Server/API/BaseAPI.ts +13 -0
- package/Server/API/BaseAnalyticsAPI.ts +80 -9
- package/Server/API/BillingAPI.ts +2 -0
- package/Server/API/BillingInvoiceAPI.ts +1 -0
- package/Server/API/BillingPaymentMethodAPI.ts +2 -0
- package/Server/API/CodeFixRunAPI.ts +1 -3
- package/Server/API/CommonAPI.ts +68 -4
- package/Server/API/DashboardAPI.ts +12 -12
- package/Server/API/GlobalConfigAPI.ts +17 -0
- package/Server/API/IncidentAPI.ts +2 -0
- package/Server/API/IncidentEpisodeAPI.ts +1 -0
- package/Server/API/NotificationAPI.ts +1 -0
- package/Server/API/OnCallCalendarAPI.ts +4 -5
- package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
- package/Server/API/OnCallReadinessAPI.ts +4 -1
- package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
- package/Server/API/ProjectAPI.ts +1 -0
- package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
- package/Server/API/StatusPageAPI.ts +161 -32
- package/Server/API/TeamMemberAPI.ts +3 -0
- package/Server/API/UserCallAPI.ts +2 -0
- package/Server/API/UserEmailAPI.ts +2 -0
- package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
- package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
- package/Server/API/UserSlackAPI.ts +1 -0
- package/Server/API/UserSmsAPI.ts +2 -0
- package/Server/API/UserTotpAuthAPI.ts +1 -0
- package/Server/API/UserWebAuthnAPI.ts +2 -0
- package/Server/API/UserWebhookAPI.ts +1 -0
- package/Server/API/UserWhatsAppAPI.ts +2 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
- package/Server/Middleware/UserAuthorization.ts +79 -5
- package/Server/Services/ApiKeyPermissionService.ts +101 -4
- package/Server/Services/DatabaseService.ts +245 -0
- package/Server/Services/DetectionRuleService.ts +5 -4
- package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
- package/Server/Services/PayAsYouGoBillingService.ts +23 -28
- package/Server/Services/ProjectService.ts +18 -0
- package/Server/Services/RumSessionPinService.ts +274 -22
- package/Server/Services/TeamPermissionService.ts +120 -26
- package/Server/Services/ThreatIntelFeedService.ts +6 -3
- package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
- package/Server/Types/Database/Permissions/Index.ts +75 -11
- package/Server/Types/Database/Permissions/TablePermission.ts +24 -28
- package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
- package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
- package/Server/Utils/MasterAdminPageAccess.ts +61 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
- package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
- package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
- package/Server/Utils/Realtime.ts +163 -45
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
- package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
- package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
- package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
- package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
- package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
- package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
- package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
- package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
- package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
- package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
- package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
- package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
- package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
- package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
- package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
- package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +95 -68
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +28 -3
- package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +761 -0
- package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
- package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
- package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
- package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
- package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +321 -0
- package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
- package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
- package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
- package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
- package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
- package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
- package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
- package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
- package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
- package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
- package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
- package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
- package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
- package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
- package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +140 -0
- package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
- package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +175 -17
- package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
- package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
- package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
- package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
- package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
- package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
- package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
- package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
- package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
- package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
- package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
- package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
- package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
- package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
- package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
- package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
- package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
- package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
- package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
- package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
- package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
- package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
- package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
- package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
- package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
- package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
- package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
- package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
- package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
- package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
- package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
- package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
- package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
- package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
- package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
- package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
- package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
- package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
- package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
- package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
- package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
- package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
- package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
- package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
- package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
- package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
- package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
- package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
- package/Tests/Server/Utils/Realtime.test.ts +502 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
- package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
- package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
- package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
- package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
- package/Tests/Types/Call/CallProvider.test.ts +528 -0
- package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
- package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
- package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
- package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
- package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
- package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +89 -1
- package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
- package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
- package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
- package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
- package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
- package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
- package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
- package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
- package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
- package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
- package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
- package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/UI/Utils/Cookie.test.ts +440 -0
- package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
- package/Tests/UI/Utils/Navigation.test.ts +33 -0
- package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
- package/Tests/UI/Utils/Realtime.test.ts +759 -0
- package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
- package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
- package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
- package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
- package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
- package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
- package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
- package/Types/Realtime/EventName.ts +11 -0
- package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
- package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
- package/Types/SecurityEvent/SigmaRule.ts +51 -0
- package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
- package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
- package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
- package/UI/Components/Dropdown/Dropdown.tsx +6 -1
- package/UI/Components/EditionLabel/EditionLabel.tsx +12 -0
- package/UI/Components/EventItem/EventItem.tsx +12 -1
- package/UI/Components/Forms/BasicForm.tsx +151 -126
- package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
- package/UI/Components/Forms/Fields/FormField.tsx +10 -4
- package/UI/Components/Forms/FormSummary.tsx +7 -0
- package/UI/Components/Forms/Types/Field.ts +10 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
- package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
- package/UI/Components/ModelTable/TableView.tsx +1 -1
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Utils/API/API.ts +173 -12
- package/UI/Utils/API/RequestOptions.ts +11 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
- package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
- package/UI/Utils/Navigation.ts +9 -0
- package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
- package/UI/Utils/Realtime.ts +176 -30
- package/Utils/API.ts +14 -5
- package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
- package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
- package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
- package/build/dist/Server/API/AIBillingAPI.js +1 -1
- package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +1 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AIReadinessAPI.js +1 -3
- package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
- package/build/dist/Server/API/AlertAPI.js +1 -1
- package/build/dist/Server/API/AlertAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +12 -0
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
- package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
- package/build/dist/Server/API/BillingAPI.js +2 -2
- package/build/dist/Server/API/BillingAPI.js.map +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
- package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
- package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
- package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
- package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +50 -5
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +12 -12
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +12 -0
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +2 -2
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
- package/build/dist/Server/API/NotificationAPI.js +1 -1
- package/build/dist/Server/API/NotificationAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
- package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
- package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectAPI.js +1 -1
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +125 -33
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +3 -3
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/API/UserCallAPI.js +2 -2
- package/build/dist/Server/API/UserCallAPI.js.map +1 -1
- package/build/dist/Server/API/UserEmailAPI.js +2 -2
- package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/UserSlackAPI.js +1 -1
- package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
- package/build/dist/Server/API/UserSmsAPI.js +2 -2
- package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
- package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
- package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
- package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
- package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
- package/build/dist/Server/API/UserWebhookAPI.js +1 -1
- package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
- package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
- package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +64 -4
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +171 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +4 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
- package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
- package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
- package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +17 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +180 -16
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/TeamPermissionService.js +89 -27
- package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
- package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
- package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
- package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +14 -13
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
- package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
- package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Realtime.js +126 -37
- package/build/dist/Server/Utils/Realtime.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
- package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
- package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
- package/build/dist/Types/Realtime/EventName.js +10 -0
- package/build/dist/Types/Realtime/EventName.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
- package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
- package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
- package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
- package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
- package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
- package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
- package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +11 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
- package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
- package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
- package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
- package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
- package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/UI/Utils/API/API.js +143 -19
- package/build/dist/UI/Utils/API/API.js.map +1 -1
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +8 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
- package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
- package/build/dist/UI/Utils/Realtime.js +120 -15
- package/build/dist/UI/Utils/Realtime.js.map +1 -1
- package/build/dist/Utils/API.js +13 -5
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
- package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
- package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
- package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
import OnCallCalendarFeedCache from "../Infrastructure/OnCallCalendarFeedCache";
|
|
2
|
+
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
3
|
+
import OnCallDutyPolicyScheduleLayerUserService from "../Services/OnCallDutyPolicyScheduleLayerUserService";
|
|
4
|
+
import OnCallDutyPolicyScheduleOwnerTeamService from "../Services/OnCallDutyPolicyScheduleOwnerTeamService";
|
|
5
|
+
import OnCallDutyPolicyScheduleService from "../Services/OnCallDutyPolicyScheduleService";
|
|
6
|
+
import TeamMemberService from "../Services/TeamMemberService";
|
|
7
|
+
import TeamService from "../Services/TeamService";
|
|
8
|
+
import DatabaseRequestType from "../Types/BaseDatabase/DatabaseRequestType";
|
|
9
|
+
import TablePermission from "../Types/Database/Permissions/TablePermission";
|
|
10
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
11
|
+
import Express, {
|
|
12
|
+
ExpressRequest,
|
|
13
|
+
ExpressResponse,
|
|
14
|
+
ExpressRouter,
|
|
15
|
+
NextFunction,
|
|
16
|
+
} from "../Utils/Express";
|
|
17
|
+
import logger from "../Utils/Logger";
|
|
18
|
+
import OnCallCalendarFeedRenderer, {
|
|
19
|
+
CachedScheduleSegments,
|
|
20
|
+
ScheduleInfo,
|
|
21
|
+
} from "../Utils/OnCall/OnCallCalendarFeedRenderer";
|
|
22
|
+
import Response from "../Utils/Response";
|
|
23
|
+
import CommonAPI from "./CommonAPI";
|
|
24
|
+
import OnCallDutyPolicySchedule from "../../Models/DatabaseModels/OnCallDutyPolicySchedule";
|
|
25
|
+
import OnCallDutyPolicyScheduleLayerUser from "../../Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser";
|
|
26
|
+
import OnCallDutyPolicyScheduleOwnerTeam from "../../Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerTeam";
|
|
27
|
+
import OnCallDutyPolicyUserOverride from "../../Models/DatabaseModels/OnCallDutyPolicyUserOverride";
|
|
28
|
+
import Team from "../../Models/DatabaseModels/Team";
|
|
29
|
+
import { DatabaseBaseModelType } from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
30
|
+
import TeamMember from "../../Models/DatabaseModels/TeamMember";
|
|
31
|
+
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
32
|
+
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
33
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
34
|
+
import OneUptimeDate from "../../Types/Date";
|
|
35
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
36
|
+
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
37
|
+
import PaymentRequiredException from "../../Types/Exception/PaymentRequiredException";
|
|
38
|
+
import ServiceUnavailableException from "../../Types/Exception/ServiceUnavailableException";
|
|
39
|
+
import { JSONObject } from "../../Types/JSON";
|
|
40
|
+
import ObjectID from "../../Types/ObjectID";
|
|
41
|
+
import MaterializedShiftUtil from "../../Types/OnCallDutyPolicy/MaterializedShift";
|
|
42
|
+
import ScheduleTimelineUtil, {
|
|
43
|
+
SCHEDULE_TIMELINE_ROUTE,
|
|
44
|
+
ScheduleTimelineResponse,
|
|
45
|
+
ScheduleTimelineScheduleJson,
|
|
46
|
+
ScheduleTimelineShiftJson,
|
|
47
|
+
ScheduleTimelineTeamJson,
|
|
48
|
+
TIMELINE_MAX_SCHEDULES,
|
|
49
|
+
TimelineWindow,
|
|
50
|
+
} from "../../Types/OnCallDutyPolicy/ScheduleTimeline";
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
* GET /on-call-schedule-timeline?from=&to=[&teamId=]
|
|
54
|
+
*
|
|
55
|
+
* Every on-call schedule the caller can read in the tenant project, with the
|
|
56
|
+
* resolved shifts of each over [from, to): the data behind the "all schedules
|
|
57
|
+
* in one week / month view" page and the per-team schedule view.
|
|
58
|
+
*
|
|
59
|
+
* WHO MAY SEE WHAT. The shifts are the schedules' rosters -- who is on which
|
|
60
|
+
* layer, and when -- so the route first requires what the CRUD read of layer
|
|
61
|
+
* users requires (an Allow from that model's read list and no team Block on
|
|
62
|
+
* it; see assertCanReadTable); a role that can list schedules but not open
|
|
63
|
+
* their layers gets a 403 here too, not a back door. Override provenance (whose shift is being
|
|
64
|
+
* covered, the override's window) is likewise only sent to callers who could
|
|
65
|
+
* read user overrides; anyone else still sees who is paged, which the
|
|
66
|
+
* schedule row itself already exposes as currentUserOnRoster.
|
|
67
|
+
*
|
|
68
|
+
* WHICH schedules appear is then decided by reading the schedule list with
|
|
69
|
+
* the CALLER's props, so the table permission and the label scoping
|
|
70
|
+
* (@CanAccessIfCanReadOn) apply exactly as on the schedules table. Everything
|
|
71
|
+
* after that -- the shift expansion, user names, team names, "am I on this
|
|
72
|
+
* roster" -- is a root read keyed on the ids that gate returned.
|
|
73
|
+
*
|
|
74
|
+
* WHAT IT COSTS. The shifts come from the schedule-level cache the calendar
|
|
75
|
+
* feeds and /my-shifts share (OnCallCalendarFeedRenderer), keyed on each
|
|
76
|
+
* schedule's shiftConfigVersion and the UTC-day-aligned window, so a team
|
|
77
|
+
* paging through the same week shares one expansion per schedule. A miss
|
|
78
|
+
* holds one of the per-process render slots, and like /my-shifts this route
|
|
79
|
+
* leaves half of them for the public feeds: a dashboard can retry, a calendar
|
|
80
|
+
* client that gets a 503 shows somebody a stale on-call calendar.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
const router: ExpressRouter = Express.getRouter();
|
|
84
|
+
|
|
85
|
+
// Seconds the dashboard is told to wait before retrying a capped render.
|
|
86
|
+
export const TIMELINE_RETRY_AFTER_SECONDS: number = 5;
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* The optional `?teamId=` filter: only schedules that team owns. Absent or
|
|
90
|
+
* blank -> no filter. Present but not a UUID -> 400; a page that asked for
|
|
91
|
+
* one team's schedules must never quietly get everyone's.
|
|
92
|
+
*/
|
|
93
|
+
export function readTimelineTeamFilter(req: ExpressRequest): ObjectID | null {
|
|
94
|
+
const raw: unknown = req.query?.["teamId"];
|
|
95
|
+
|
|
96
|
+
if (raw === undefined || raw === null) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const value: string = (
|
|
101
|
+
Array.isArray(raw) ? String(raw[0] ?? "") : String(raw)
|
|
102
|
+
).trim();
|
|
103
|
+
|
|
104
|
+
if (!value) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!ObjectID.isValidUUID(value)) {
|
|
109
|
+
throw new BadDataException("teamId must be a valid id.");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return new ObjectID(value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// The requested window, validated and clamped (see ScheduleTimelineUtil).
|
|
116
|
+
export function readTimelineWindow(
|
|
117
|
+
req: ExpressRequest,
|
|
118
|
+
now: Date,
|
|
119
|
+
): TimelineWindow {
|
|
120
|
+
const pick: (name: string) => unknown = (name: string): unknown => {
|
|
121
|
+
const raw: unknown = req.query?.[name];
|
|
122
|
+
return Array.isArray(raw) ? raw[0] : raw;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return ScheduleTimelineUtil.clampWindow({
|
|
126
|
+
from: pick("from"),
|
|
127
|
+
to: pick("to"),
|
|
128
|
+
now,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
* Assemble the response from what the route read. Pure, so the shape and
|
|
134
|
+
* the ordering rules are tested without a database:
|
|
135
|
+
*
|
|
136
|
+
* - schedules keep the caller-scoped read's order (by name);
|
|
137
|
+
* - a schedule the cache produced nothing for still gets a row (it is
|
|
138
|
+
* visible to the caller; an empty row says "nobody is on call");
|
|
139
|
+
* - shifts are projected with ScheduleTimelineUtil.toTimelineShift and
|
|
140
|
+
* sorted by start;
|
|
141
|
+
* - teams are the owner teams of the returned schedules, by name.
|
|
142
|
+
*/
|
|
143
|
+
export function buildTimelineResponse(data: {
|
|
144
|
+
window: TimelineWindow;
|
|
145
|
+
now: Date;
|
|
146
|
+
schedules: Array<{ id: string; name: string }>;
|
|
147
|
+
segments: Array<CachedScheduleSegments>;
|
|
148
|
+
ownerTeamIdsBySchedule: Map<string, Array<string>>;
|
|
149
|
+
rosterScheduleIds: Set<string>;
|
|
150
|
+
teamNames: Map<string, string>;
|
|
151
|
+
memberTeamIds: Set<string>;
|
|
152
|
+
totalScheduleCount: number;
|
|
153
|
+
// False strips override provenance: the caller cannot read user overrides.
|
|
154
|
+
includeOverrides: boolean;
|
|
155
|
+
}): ScheduleTimelineResponse {
|
|
156
|
+
const segmentsById: Map<string, CachedScheduleSegments> = new Map();
|
|
157
|
+
|
|
158
|
+
for (const segment of data.segments) {
|
|
159
|
+
segmentsById.set(segment.scheduleId, segment);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const schedules: Array<ScheduleTimelineScheduleJson> = [];
|
|
163
|
+
const usedTeamIds: Set<string> = new Set<string>();
|
|
164
|
+
let truncated: boolean = false;
|
|
165
|
+
|
|
166
|
+
for (const schedule of data.schedules) {
|
|
167
|
+
const segment: CachedScheduleSegments | undefined = segmentsById.get(
|
|
168
|
+
schedule.id,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const shifts: Array<ScheduleTimelineShiftJson> = [];
|
|
172
|
+
|
|
173
|
+
if (segment) {
|
|
174
|
+
for (const shift of MaterializedShiftUtil.fromJSONArray(segment.shifts)) {
|
|
175
|
+
const projected: ScheduleTimelineShiftJson | null =
|
|
176
|
+
ScheduleTimelineUtil.toTimelineShift(shift, data.window);
|
|
177
|
+
|
|
178
|
+
if (projected) {
|
|
179
|
+
shifts.push(
|
|
180
|
+
data.includeOverrides
|
|
181
|
+
? projected
|
|
182
|
+
: { ...projected, override: null },
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
shifts.sort(
|
|
189
|
+
(a: ScheduleTimelineShiftJson, b: ScheduleTimelineShiftJson): number => {
|
|
190
|
+
return Date.parse(a.start) - Date.parse(b.start);
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const ownerTeamIds: Array<string> = (
|
|
195
|
+
data.ownerTeamIdsBySchedule.get(schedule.id) || []
|
|
196
|
+
).filter((teamId: string) => {
|
|
197
|
+
// A team row that has since been deleted has no name to group under.
|
|
198
|
+
return data.teamNames.has(teamId);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
for (const teamId of ownerTeamIds) {
|
|
202
|
+
usedTeamIds.add(teamId);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
truncated = truncated || Boolean(segment?.truncated);
|
|
206
|
+
|
|
207
|
+
schedules.push({
|
|
208
|
+
scheduleId: schedule.id,
|
|
209
|
+
scheduleName: segment?.scheduleName || schedule.name,
|
|
210
|
+
scheduleTimezone: segment?.scheduleTimezone ?? null,
|
|
211
|
+
ownerTeamIds,
|
|
212
|
+
isCurrentUserOnRoster: data.rosterScheduleIds.has(schedule.id),
|
|
213
|
+
truncated: Boolean(segment?.truncated),
|
|
214
|
+
shifts,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const teams: Array<ScheduleTimelineTeamJson> = Array.from(usedTeamIds)
|
|
219
|
+
.map((teamId: string): ScheduleTimelineTeamJson => {
|
|
220
|
+
return {
|
|
221
|
+
teamId,
|
|
222
|
+
teamName: data.teamNames.get(teamId) || "Unnamed team",
|
|
223
|
+
isCurrentUserMember: data.memberTeamIds.has(teamId),
|
|
224
|
+
};
|
|
225
|
+
})
|
|
226
|
+
.sort((a: ScheduleTimelineTeamJson, b: ScheduleTimelineTeamJson) => {
|
|
227
|
+
return a.teamName.localeCompare(b.teamName);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
from: data.window.from.toISOString(),
|
|
232
|
+
to: data.window.to.toISOString(),
|
|
233
|
+
generatedAt: data.now.toISOString(),
|
|
234
|
+
truncated,
|
|
235
|
+
totalScheduleCount: Math.max(
|
|
236
|
+
data.totalScheduleCount,
|
|
237
|
+
data.schedules.length,
|
|
238
|
+
),
|
|
239
|
+
schedulesTruncated: data.totalScheduleCount > data.schedules.length,
|
|
240
|
+
schedules,
|
|
241
|
+
teams,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function toIdStrings(
|
|
246
|
+
ids: Array<ObjectID | null | undefined | string>,
|
|
247
|
+
): Array<string> {
|
|
248
|
+
const out: Array<string> = [];
|
|
249
|
+
|
|
250
|
+
for (const id of ids) {
|
|
251
|
+
const value: string = id ? id.toString() : "";
|
|
252
|
+
|
|
253
|
+
if (value && !out.includes(value)) {
|
|
254
|
+
out.push(value);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return out;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/*
|
|
262
|
+
* Schedule ids a team owns, read with the caller's props (the owner-team
|
|
263
|
+
* table has its own read permission, and the answer must respect it).
|
|
264
|
+
*/
|
|
265
|
+
async function findScheduleIdsOwnedByTeam(data: {
|
|
266
|
+
teamId: ObjectID;
|
|
267
|
+
projectId: ObjectID;
|
|
268
|
+
props: DatabaseCommonInteractionProps;
|
|
269
|
+
}): Promise<Array<string>> {
|
|
270
|
+
const rows: Array<OnCallDutyPolicyScheduleOwnerTeam> =
|
|
271
|
+
await OnCallDutyPolicyScheduleOwnerTeamService.findBy({
|
|
272
|
+
query: {
|
|
273
|
+
teamId: data.teamId,
|
|
274
|
+
projectId: data.projectId,
|
|
275
|
+
},
|
|
276
|
+
select: {
|
|
277
|
+
onCallDutyPolicyScheduleId: true,
|
|
278
|
+
},
|
|
279
|
+
limit: LIMIT_PER_PROJECT,
|
|
280
|
+
skip: 0,
|
|
281
|
+
props: data.props,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
return toIdStrings(
|
|
285
|
+
rows.map((row: OnCallDutyPolicyScheduleOwnerTeam) => {
|
|
286
|
+
return row.onCallDutyPolicyScheduleId;
|
|
287
|
+
}),
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/*
|
|
292
|
+
* Owner teams per schedule, with the caller's props. A caller whose role can
|
|
293
|
+
* read schedules but not their owner rows still gets a timeline, just an
|
|
294
|
+
* ungrouped one, so a permission error here is logged and swallowed.
|
|
295
|
+
*/
|
|
296
|
+
async function loadOwnerTeamIds(data: {
|
|
297
|
+
scheduleIds: Array<string>;
|
|
298
|
+
projectId: ObjectID;
|
|
299
|
+
props: DatabaseCommonInteractionProps;
|
|
300
|
+
}): Promise<Map<string, Array<string>>> {
|
|
301
|
+
const out: Map<string, Array<string>> = new Map();
|
|
302
|
+
|
|
303
|
+
if (data.scheduleIds.length === 0) {
|
|
304
|
+
return out;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let rows: Array<OnCallDutyPolicyScheduleOwnerTeam> = [];
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
rows = await OnCallDutyPolicyScheduleOwnerTeamService.findBy({
|
|
311
|
+
query: {
|
|
312
|
+
projectId: data.projectId,
|
|
313
|
+
onCallDutyPolicyScheduleId: QueryHelper.any(data.scheduleIds),
|
|
314
|
+
},
|
|
315
|
+
select: {
|
|
316
|
+
onCallDutyPolicyScheduleId: true,
|
|
317
|
+
teamId: true,
|
|
318
|
+
},
|
|
319
|
+
limit: LIMIT_PER_PROJECT,
|
|
320
|
+
skip: 0,
|
|
321
|
+
props: data.props,
|
|
322
|
+
});
|
|
323
|
+
} catch (err) {
|
|
324
|
+
/*
|
|
325
|
+
* Only a refusal is a reason to serve the timeline ungrouped. A database
|
|
326
|
+
* error must surface: silently dropping every team would look like a
|
|
327
|
+
* successful answer with all schedules under "No owner team".
|
|
328
|
+
*/
|
|
329
|
+
if (
|
|
330
|
+
!(err instanceof NotAuthorizedException) &&
|
|
331
|
+
!(err instanceof PaymentRequiredException)
|
|
332
|
+
) {
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
logger.debug(
|
|
337
|
+
"OnCallScheduleTimelineAPI: owner teams could not be read for this caller; the timeline is served ungrouped.",
|
|
338
|
+
);
|
|
339
|
+
logger.debug(err);
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
for (const row of rows) {
|
|
344
|
+
const scheduleId: string = row.onCallDutyPolicyScheduleId?.toString() || "";
|
|
345
|
+
const teamId: string = row.teamId?.toString() || "";
|
|
346
|
+
|
|
347
|
+
if (!scheduleId || !teamId) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const list: Array<string> = out.get(scheduleId) || [];
|
|
352
|
+
|
|
353
|
+
if (!list.includes(teamId)) {
|
|
354
|
+
list.push(teamId);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
out.set(scheduleId, list);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return out;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function loadTeamNames(data: {
|
|
364
|
+
teamIds: Array<string>;
|
|
365
|
+
projectId: ObjectID;
|
|
366
|
+
}): Promise<Map<string, string>> {
|
|
367
|
+
const out: Map<string, string> = new Map();
|
|
368
|
+
|
|
369
|
+
if (data.teamIds.length === 0) {
|
|
370
|
+
return out;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const rows: Array<Team> = await TeamService.findBy({
|
|
374
|
+
query: {
|
|
375
|
+
_id: QueryHelper.any(data.teamIds),
|
|
376
|
+
projectId: data.projectId,
|
|
377
|
+
},
|
|
378
|
+
select: {
|
|
379
|
+
_id: true,
|
|
380
|
+
name: true,
|
|
381
|
+
},
|
|
382
|
+
limit: LIMIT_PER_PROJECT,
|
|
383
|
+
skip: 0,
|
|
384
|
+
props: {
|
|
385
|
+
isRoot: true,
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
for (const row of rows) {
|
|
390
|
+
const teamId: string = row.id?.toString() || "";
|
|
391
|
+
|
|
392
|
+
if (teamId) {
|
|
393
|
+
out.set(teamId, row.name?.toString() || "Unnamed team");
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return out;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// The caller's own accepted memberships among `teamIds`.
|
|
401
|
+
async function loadMemberTeamIds(data: {
|
|
402
|
+
teamIds: Array<string>;
|
|
403
|
+
projectId: ObjectID;
|
|
404
|
+
userId: ObjectID;
|
|
405
|
+
}): Promise<Set<string>> {
|
|
406
|
+
if (data.teamIds.length === 0) {
|
|
407
|
+
return new Set<string>();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const rows: Array<TeamMember> = await TeamMemberService.findBy({
|
|
411
|
+
query: {
|
|
412
|
+
projectId: data.projectId,
|
|
413
|
+
userId: data.userId,
|
|
414
|
+
teamId: QueryHelper.any(data.teamIds),
|
|
415
|
+
hasAcceptedInvitation: true,
|
|
416
|
+
},
|
|
417
|
+
select: {
|
|
418
|
+
teamId: true,
|
|
419
|
+
},
|
|
420
|
+
limit: LIMIT_PER_PROJECT,
|
|
421
|
+
skip: 0,
|
|
422
|
+
props: {
|
|
423
|
+
isRoot: true,
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
return new Set<string>(
|
|
428
|
+
toIdStrings(
|
|
429
|
+
rows.map((row: TeamMember) => {
|
|
430
|
+
return row.teamId;
|
|
431
|
+
}),
|
|
432
|
+
),
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Which of `scheduleIds` the caller is assigned to on some layer.
|
|
437
|
+
async function loadRosterScheduleIds(data: {
|
|
438
|
+
scheduleIds: Array<string>;
|
|
439
|
+
projectId: ObjectID;
|
|
440
|
+
userId: ObjectID;
|
|
441
|
+
}): Promise<Set<string>> {
|
|
442
|
+
if (data.scheduleIds.length === 0) {
|
|
443
|
+
return new Set<string>();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const rows: Array<OnCallDutyPolicyScheduleLayerUser> =
|
|
447
|
+
await OnCallDutyPolicyScheduleLayerUserService.findBy({
|
|
448
|
+
query: {
|
|
449
|
+
projectId: data.projectId,
|
|
450
|
+
userId: data.userId,
|
|
451
|
+
onCallDutyPolicyScheduleId: QueryHelper.any(data.scheduleIds),
|
|
452
|
+
},
|
|
453
|
+
select: {
|
|
454
|
+
onCallDutyPolicyScheduleId: true,
|
|
455
|
+
},
|
|
456
|
+
limit: LIMIT_PER_PROJECT,
|
|
457
|
+
skip: 0,
|
|
458
|
+
props: {
|
|
459
|
+
isRoot: true,
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
return new Set<string>(
|
|
464
|
+
toIdStrings(
|
|
465
|
+
rows.map((row: OnCallDutyPolicyScheduleLayerUser) => {
|
|
466
|
+
return row.onCallDutyPolicyScheduleId;
|
|
467
|
+
}),
|
|
468
|
+
),
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/*
|
|
473
|
+
* Throws unless the caller could read `modelType` through its CRUD endpoint.
|
|
474
|
+
* That read has two halves and both are applied here: an Allow grant from the
|
|
475
|
+
* model's read list (assertPermittedInProject), and no unlabelled team BLOCK
|
|
476
|
+
* row on any permission in that list (checkTableLevelBlockPermissions) - a
|
|
477
|
+
* block overrides every Allow the team holds. Master admins bypass both, as
|
|
478
|
+
* they do in ReadPermission.
|
|
479
|
+
*/
|
|
480
|
+
export function assertCanReadTable(data: {
|
|
481
|
+
modelType: DatabaseBaseModelType;
|
|
482
|
+
props: DatabaseCommonInteractionProps;
|
|
483
|
+
errorMessage?: string | undefined;
|
|
484
|
+
}): void {
|
|
485
|
+
CommonAPI.assertPermittedInProject({
|
|
486
|
+
databaseProps: data.props,
|
|
487
|
+
allowedPermissions: new data.modelType().getReadPermissions(),
|
|
488
|
+
errorMessage: data.errorMessage,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
if (!data.props.isMasterAdmin) {
|
|
492
|
+
TablePermission.checkTableLevelBlockPermissions(
|
|
493
|
+
data.modelType,
|
|
494
|
+
data.props,
|
|
495
|
+
DatabaseRequestType.Read,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Whether the caller could read user overrides through their CRUD endpoint.
|
|
501
|
+
export function canReadUserOverrides(
|
|
502
|
+
props: DatabaseCommonInteractionProps,
|
|
503
|
+
): boolean {
|
|
504
|
+
try {
|
|
505
|
+
assertCanReadTable({ modelType: OnCallDutyPolicyUserOverride, props });
|
|
506
|
+
return true;
|
|
507
|
+
} catch {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function emptyResponse(
|
|
513
|
+
window: TimelineWindow,
|
|
514
|
+
now: Date,
|
|
515
|
+
): ScheduleTimelineResponse {
|
|
516
|
+
return buildTimelineResponse({
|
|
517
|
+
window,
|
|
518
|
+
now,
|
|
519
|
+
schedules: [],
|
|
520
|
+
segments: [],
|
|
521
|
+
ownerTeamIdsBySchedule: new Map(),
|
|
522
|
+
rosterScheduleIds: new Set(),
|
|
523
|
+
teamNames: new Map(),
|
|
524
|
+
memberTeamIds: new Set(),
|
|
525
|
+
totalScheduleCount: 0,
|
|
526
|
+
includeOverrides: false,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
router.get(
|
|
531
|
+
SCHEDULE_TIMELINE_ROUTE,
|
|
532
|
+
UserMiddleware.getUserMiddleware,
|
|
533
|
+
UserMiddleware.requireUserAuthentication,
|
|
534
|
+
async (
|
|
535
|
+
req: ExpressRequest,
|
|
536
|
+
res: ExpressResponse,
|
|
537
|
+
next: NextFunction,
|
|
538
|
+
): Promise<void> => {
|
|
539
|
+
try {
|
|
540
|
+
const props: DatabaseCommonInteractionProps =
|
|
541
|
+
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
542
|
+
|
|
543
|
+
const projectId: ObjectID =
|
|
544
|
+
CommonAPI.assertAuthenticatedProjectMember(props);
|
|
545
|
+
const userId: ObjectID = props.userId as ObjectID;
|
|
546
|
+
|
|
547
|
+
// The rosters are layer-user data: require what reading them requires.
|
|
548
|
+
assertCanReadTable({
|
|
549
|
+
modelType: OnCallDutyPolicyScheduleLayerUser,
|
|
550
|
+
props,
|
|
551
|
+
errorMessage:
|
|
552
|
+
"You do not have permission to read this project's on-call schedule layers.",
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
const includeOverrides: boolean = canReadUserOverrides(props);
|
|
556
|
+
|
|
557
|
+
const now: Date = OneUptimeDate.getCurrentDate();
|
|
558
|
+
const window: TimelineWindow = readTimelineWindow(req, now);
|
|
559
|
+
const teamId: ObjectID | null = readTimelineTeamFilter(req);
|
|
560
|
+
|
|
561
|
+
let teamScheduleIds: Array<string> | null = null;
|
|
562
|
+
|
|
563
|
+
if (teamId) {
|
|
564
|
+
teamScheduleIds = await findScheduleIdsOwnedByTeam({
|
|
565
|
+
teamId,
|
|
566
|
+
projectId,
|
|
567
|
+
props,
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
if (teamScheduleIds.length === 0) {
|
|
571
|
+
return Response.sendJsonObjectResponse(
|
|
572
|
+
req,
|
|
573
|
+
res,
|
|
574
|
+
emptyResponse(window, now) as unknown as JSONObject,
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/*
|
|
580
|
+
* THE gate: a non-root read, so permissions and label scoping decide
|
|
581
|
+
* which schedules exist for this caller. One row past the cap tells
|
|
582
|
+
* the page there is more than it was given.
|
|
583
|
+
*/
|
|
584
|
+
const scheduleQuery: {
|
|
585
|
+
projectId: ObjectID;
|
|
586
|
+
_id?: ReturnType<typeof QueryHelper.any>;
|
|
587
|
+
} = { projectId };
|
|
588
|
+
|
|
589
|
+
if (teamScheduleIds) {
|
|
590
|
+
scheduleQuery._id = QueryHelper.any(teamScheduleIds);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const visible: Array<OnCallDutyPolicySchedule> =
|
|
594
|
+
await OnCallDutyPolicyScheduleService.findBy({
|
|
595
|
+
query: scheduleQuery,
|
|
596
|
+
select: {
|
|
597
|
+
_id: true,
|
|
598
|
+
name: true,
|
|
599
|
+
},
|
|
600
|
+
sort: {
|
|
601
|
+
name: SortOrder.Ascending,
|
|
602
|
+
},
|
|
603
|
+
limit: TIMELINE_MAX_SCHEDULES + 1,
|
|
604
|
+
skip: 0,
|
|
605
|
+
props,
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
const readable: Array<{ id: string; name: string }> = [];
|
|
609
|
+
|
|
610
|
+
for (const row of visible) {
|
|
611
|
+
const id: string = row.id?.toString() || "";
|
|
612
|
+
|
|
613
|
+
if (id) {
|
|
614
|
+
readable.push({ id, name: row.name?.toString() || "" });
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (readable.length === 0) {
|
|
619
|
+
return Response.sendJsonObjectResponse(
|
|
620
|
+
req,
|
|
621
|
+
res,
|
|
622
|
+
emptyResponse(window, now) as unknown as JSONObject,
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
let totalScheduleCount: number = readable.length;
|
|
627
|
+
const schedules: Array<{ id: string; name: string }> = readable.slice(
|
|
628
|
+
0,
|
|
629
|
+
TIMELINE_MAX_SCHEDULES,
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
if (readable.length > TIMELINE_MAX_SCHEDULES) {
|
|
633
|
+
totalScheduleCount = (
|
|
634
|
+
await OnCallDutyPolicyScheduleService.countBy({
|
|
635
|
+
query: scheduleQuery,
|
|
636
|
+
props,
|
|
637
|
+
})
|
|
638
|
+
).toNumber();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const scheduleIds: Array<string> = schedules.map(
|
|
642
|
+
(schedule: { id: string; name: string }) => {
|
|
643
|
+
return schedule.id;
|
|
644
|
+
},
|
|
645
|
+
);
|
|
646
|
+
|
|
647
|
+
const ownerTeamIdsBySchedule: Map<
|
|
648
|
+
string,
|
|
649
|
+
Array<string>
|
|
650
|
+
> = await loadOwnerTeamIds({ scheduleIds, projectId, props });
|
|
651
|
+
|
|
652
|
+
const allTeamIds: Array<string> = toIdStrings(
|
|
653
|
+
Array.from(ownerTeamIdsBySchedule.values()).flat(),
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
const [teamNames, memberTeamIds, rosterScheduleIds]: [
|
|
657
|
+
Map<string, string>,
|
|
658
|
+
Set<string>,
|
|
659
|
+
Set<string>,
|
|
660
|
+
] = await Promise.all([
|
|
661
|
+
loadTeamNames({ teamIds: allTeamIds, projectId }),
|
|
662
|
+
loadMemberTeamIds({ teamIds: allTeamIds, projectId, userId }),
|
|
663
|
+
loadRosterScheduleIds({ scheduleIds, projectId, userId }),
|
|
664
|
+
]);
|
|
665
|
+
|
|
666
|
+
const infos: Array<ScheduleInfo> =
|
|
667
|
+
await OnCallCalendarFeedRenderer.loadSchedules(
|
|
668
|
+
scheduleIds.map((id: string) => {
|
|
669
|
+
return new ObjectID(id);
|
|
670
|
+
}),
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
if (
|
|
674
|
+
!OnCallCalendarFeedCache.tryAcquireRenderSlot({
|
|
675
|
+
leaveFreeSlots: Math.floor(
|
|
676
|
+
OnCallCalendarFeedCache.getRenderConcurrency() / 2,
|
|
677
|
+
),
|
|
678
|
+
})
|
|
679
|
+
) {
|
|
680
|
+
res.set("Retry-After", String(TIMELINE_RETRY_AFTER_SECONDS));
|
|
681
|
+
|
|
682
|
+
return Response.sendErrorResponse(
|
|
683
|
+
req,
|
|
684
|
+
res,
|
|
685
|
+
new ServiceUnavailableException(
|
|
686
|
+
"The schedule timeline is busy right now. Please try again in a few seconds.",
|
|
687
|
+
),
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
let segments: Array<CachedScheduleSegments>;
|
|
692
|
+
|
|
693
|
+
try {
|
|
694
|
+
const cacheWindow: { windowStart: Date; windowEnd: Date } =
|
|
695
|
+
ScheduleTimelineUtil.getCacheWindow(window);
|
|
696
|
+
|
|
697
|
+
segments = await OnCallCalendarFeedRenderer.loadScheduleSegmentsBatch({
|
|
698
|
+
schedules: infos,
|
|
699
|
+
windowStart: cacheWindow.windowStart,
|
|
700
|
+
windowEnd: cacheWindow.windowEnd,
|
|
701
|
+
now,
|
|
702
|
+
});
|
|
703
|
+
} finally {
|
|
704
|
+
OnCallCalendarFeedCache.releaseRenderSlot();
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const payload: ScheduleTimelineResponse = buildTimelineResponse({
|
|
708
|
+
window,
|
|
709
|
+
now,
|
|
710
|
+
schedules,
|
|
711
|
+
segments,
|
|
712
|
+
ownerTeamIdsBySchedule,
|
|
713
|
+
rosterScheduleIds,
|
|
714
|
+
teamNames,
|
|
715
|
+
memberTeamIds,
|
|
716
|
+
totalScheduleCount,
|
|
717
|
+
includeOverrides,
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
return Response.sendJsonObjectResponse(
|
|
721
|
+
req,
|
|
722
|
+
res,
|
|
723
|
+
payload as unknown as JSONObject,
|
|
724
|
+
);
|
|
725
|
+
} catch (err) {
|
|
726
|
+
next(err);
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
export default router;
|