@oneuptime/common 12.0.28 → 12.0.30
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/DashboardDomain.ts +43 -0
- package/Models/DatabaseModels/Index.ts +11 -0
- package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +43 -0
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +662 -0
- package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +584 -0
- package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
- package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +546 -0
- package/Models/DatabaseModels/UserOnCallShiftReminder.ts +231 -0
- package/Models/DatabaseModels/UserOnCallShiftReminderLog.ts +331 -0
- package/Server/API/DashboardDomainAPI.ts +74 -0
- package/Server/API/MicrosoftTeamsAPI.ts +81 -180
- package/Server/API/OnCallCalendarAPI.ts +2194 -0
- package/Server/API/StatusPageDomainAPI.ts +74 -0
- package/Server/EnvironmentConfig.ts +138 -0
- package/Server/Infrastructure/OnCallCalendarFeedCache.ts +1050 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.ts +253 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/OnCallCalendarFeedRateLimit.ts +487 -0
- package/Server/Middleware/ProjectAuthorization.ts +6 -1
- package/Server/Services/DashboardDomainService.ts +131 -0
- package/Server/Services/Index.ts +12 -0
- package/Server/Services/LogPipelineProcessorService.ts +88 -0
- package/Server/Services/MonitorTemplateService.ts +70 -11
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +32 -0
- package/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.ts +260 -0
- package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +70 -3
- package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +138 -3
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +1465 -134
- package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +167 -31
- package/Server/Services/ProjectOnCallCalendarFeedService.ts +192 -0
- package/Server/Services/StatusPageDomainService.ts +163 -3
- package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
- package/Server/Services/TeamMemberService.ts +489 -1
- package/Server/Services/TeamService.ts +50 -0
- package/Server/Services/UserNotificationSettingService.ts +44 -1
- package/Server/Services/UserOnCallCalendarFeedService.ts +159 -0
- package/Server/Services/UserOnCallShiftReminderLogService.ts +151 -0
- package/Server/Services/UserOnCallShiftReminderService.ts +112 -0
- package/Server/Types/Domain.ts +17 -0
- package/Server/Utils/APIKey/AccessPermission.ts +24 -0
- package/Server/Utils/Greenlock/Greenlock.ts +156 -58
- package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
- package/Server/Utils/OnCall/CalendarFeedToken.ts +241 -0
- package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1488 -0
- package/Server/Utils/OnCall/OnCallCalendarFeedUrls.ts +230 -0
- package/Server/Utils/OnCall/OnCallShiftChangeListeners.ts +227 -0
- package/Server/Utils/OnCall/OnCallShiftMaterializer.ts +1127 -0
- package/Server/Utils/OnCall/OnCallShiftReminderListener.ts +174 -0
- package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +2584 -0
- package/Server/Utils/Response.ts +134 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
- package/Server/Utils/StartServer.ts +10 -0
- package/Server/Utils/Telemetry/AppMetrics.ts +51 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
- package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedLinks.test.tsx +229 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +489 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +149 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedUtil.test.ts +855 -0
- package/Tests/App/Dashboard/OnCallPersonalCalendarFeed.test.tsx +1209 -0
- package/Tests/App/Dashboard/OnCallSharedCalendarFeedCard.test.tsx +1019 -0
- package/Tests/App/Dashboard/OnCallShiftRemindersAndUpcomingShifts.test.tsx +655 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +73 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
- package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
- package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
- package/Tests/Models/OnCallCalendarFeedModels.test.ts +642 -0
- package/Tests/Models/OnCallDutyPolicyScheduleCalendarFeed.test.ts +288 -0
- package/Tests/Models/OnCallDutyPolicyScheduleShiftConfigVersion.test.ts +88 -0
- package/Tests/Models/UserOnCallShiftReminderModels.test.ts +368 -0
- package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
- package/Tests/Server/API/Helpers.ts +8 -0
- package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
- package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
- package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
- package/Tests/Server/API/OnCallCalendarAPI.test.ts +4461 -0
- package/Tests/Server/EnvironmentConfigOnCallCalendarFeed.test.ts +357 -0
- package/Tests/Server/Infrastructure/OnCallCalendarFeedCache.test.ts +1589 -0
- package/Tests/Server/Infrastructure/Postgres/AddOnCallCalendarFeedsMigration.test.ts +369 -0
- package/Tests/Server/Middleware/OnCallCalendarFeedRateLimit.test.ts +1365 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
- package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
- package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +70 -0
- package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
- package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
- package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
- package/Tests/Server/Services/OnCallCalendarFeedServices.test.ts +1103 -0
- package/Tests/Server/Services/OnCallDutyPolicyScheduleResolver.test.ts +1037 -0
- package/Tests/Server/Services/OnCallShiftConfigPropagation.test.ts +1270 -0
- package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
- package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
- package/Tests/Server/Services/TeamDeleteMemberCleanup.test.ts +156 -0
- package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +755 -0
- package/Tests/Server/Services/UserNotificationSettingShiftReminderDefaults.test.ts +267 -0
- package/Tests/Server/Services/UserOnCallShiftReminderServices.test.ts +583 -0
- package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
- package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
- package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +254 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
- package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
- package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
- package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
- package/Tests/Server/Utils/OnCall/CalendarFeedToken.test.ts +467 -0
- package/Tests/Server/Utils/OnCall/OnCallCalendarFeedRenderer.test.ts +2142 -0
- package/Tests/Server/Utils/OnCall/OnCallCalendarFeedUrls.test.ts +278 -0
- package/Tests/Server/Utils/OnCall/OnCallResolverTestHarness.ts +440 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftChangeListeners.test.ts +314 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftMaterializer.test.ts +1502 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftReminderListener.test.ts +491 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftReminderRunner.test.ts +3088 -0
- package/Tests/Server/Utils/OnCall/OnCallShiftReminderTestHarness.ts +910 -0
- package/Tests/Server/Utils/ResponseSendCalendarResponse.test.ts +561 -0
- package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
- package/Tests/Server/Utils/StartServerEncryptionSecretWarning.test.ts +64 -0
- package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +29 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
- package/Tests/Types/API/HTTPResponse.test.ts +175 -0
- package/Tests/Types/API/Route.test.ts +36 -4
- package/Tests/Types/Calendar/ICalendar.test.ts +488 -0
- package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
- package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
- package/Tests/Types/DateLocalTimeString.test.ts +259 -0
- package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
- package/Tests/Types/NotificationSetting/NotificationSettingEventType.test.ts +53 -0
- package/Tests/Types/OnCallDutyPolicy/CalendarFeedTestFixtures.ts +310 -0
- package/Tests/Types/OnCallDutyPolicy/CalendarFeedWindow.test.ts +205 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilLayerMetaAndIterationCap.test.ts +458 -0
- package/Tests/Types/OnCallDutyPolicy/MaterializedShift.test.ts +197 -0
- package/Tests/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.test.ts +1986 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtilGroupKey.test.ts +678 -0
- package/Tests/Types/OnCallDutyPolicy/ShiftSeamUtil.test.ts +417 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
- package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
- package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
- package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -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/Navigation.test.ts +82 -0
- package/Tests/Utils/Array.test.ts +100 -0
- package/Tests/Utils/CertificateReissue.test.ts +225 -0
- package/Tests/Utils/Grok/Grok.test.ts +492 -0
- package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
- package/Types/API/Route.ts +20 -5
- package/Types/Calendar/ICalendar.ts +442 -0
- package/Types/Date.ts +143 -13
- package/Types/Email/EmailTemplateType.ts +2 -0
- package/Types/NotificationSetting/NotificationSettingEventType.ts +4 -0
- package/Types/OnCallDutyPolicy/CalendarFeedWindow.ts +140 -0
- package/Types/OnCallDutyPolicy/Layer.ts +190 -35
- package/Types/OnCallDutyPolicy/MaterializedShift.ts +274 -0
- package/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.ts +1415 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +159 -4
- package/Types/OnCallDutyPolicy/ShiftSeamUtil.ts +106 -0
- package/Types/Rum/SessionReplay.ts +62 -1
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
- package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
- package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
- package/UI/Utils/Navigation.ts +42 -0
- package/Utils/Array.ts +37 -0
- package/Utils/CertificateReissue.ts +128 -0
- package/Utils/Grok/Grok.ts +461 -0
- package/Utils/Grok/GrokPatterns.ts +118 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +11 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +44 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +699 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +620 -0
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +585 -0
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js +247 -0
- package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js +355 -0
- package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js.map +1 -0
- package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
- package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallCalendarAPI.js +1371 -0
- package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +102 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js +663 -0
- package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js +103 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.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/OnCallCalendarFeedRateLimit.js +342 -0
- package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +104 -0
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +12 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +20 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js +219 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +47 -3
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +112 -3
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +955 -9
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +93 -6
- package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
- package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js +157 -0
- package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +396 -1
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TeamService.js +43 -0
- package/build/dist/Server/Services/TeamService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +26 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallCalendarFeedService.js +140 -0
- package/build/dist/Server/Services/UserOnCallCalendarFeedService.js.map +1 -0
- package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js +116 -0
- package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js.map +1 -0
- package/build/dist/Server/Services/UserOnCallShiftReminderService.js +92 -0
- package/build/dist/Server/Services/UserOnCallShiftReminderService.js.map +1 -0
- package/build/dist/Server/Types/Domain.js +16 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
- package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js +161 -0
- package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js +983 -0
- package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js +155 -0
- package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js +145 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js +752 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js +92 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1786 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -0
- package/build/dist/Server/Utils/Response.js +97 -0
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +9 -1
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js +41 -0
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Route.js +16 -5
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/Calendar/ICalendar.js +290 -0
- package/build/dist/Types/Calendar/ICalendar.js.map +1 -0
- package/build/dist/Types/Date.js +115 -13
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +2 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js +97 -0
- package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +113 -19
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js +134 -0
- package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js +800 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +97 -3
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js +77 -0
- package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplay.js +38 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +30 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/Utils/Array.js +25 -0
- package/build/dist/Utils/Array.js.map +1 -1
- package/build/dist/Utils/CertificateReissue.js +97 -0
- package/build/dist/Utils/CertificateReissue.js.map +1 -0
- package/build/dist/Utils/Grok/Grok.js +324 -0
- package/build/dist/Utils/Grok/Grok.js.map +1 -0
- package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
- package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/package.json +1 -1
- package/UI/Utils/JsonWebToken.ts +0 -14
- package/build/dist/UI/Utils/JsonWebToken.js +0 -10
- package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterAll,
|
|
3
|
+
afterEach,
|
|
4
|
+
beforeAll,
|
|
5
|
+
beforeEach,
|
|
6
|
+
describe,
|
|
7
|
+
expect,
|
|
8
|
+
jest,
|
|
9
|
+
test,
|
|
10
|
+
} from "@jest/globals";
|
|
11
|
+
import { ExpressRequest, ExpressResponse } from "../../../Server/Utils/Express";
|
|
12
|
+
import ResponseUtil, {
|
|
13
|
+
CALENDAR_RESPONSE_CONTENT_TYPE,
|
|
14
|
+
CALENDAR_RESPONSE_FILE_NAME,
|
|
15
|
+
CALENDAR_RESPONSE_MAX_AGE_SECONDS,
|
|
16
|
+
} from "../../../Server/Utils/Response";
|
|
17
|
+
import express from "express";
|
|
18
|
+
import http from "http";
|
|
19
|
+
import { AddressInfo } from "net";
|
|
20
|
+
import { createHash } from "crypto";
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* sendCalendarResponse is what every on-call calendar feed goes out through,
|
|
24
|
+
* and calendar clients are unforgiving about headers: Google refuses a feed
|
|
25
|
+
* whose Content-Type is not text/calendar, Outlook re-fetches on every open
|
|
26
|
+
* when it sees Pragma: no-cache next to a max-age, and a missing ETag or
|
|
27
|
+
* Last-Modified turns every hourly poll into a full download. These tests pin
|
|
28
|
+
* the header set, the one header that must be ABSENT, and the conditional
|
|
29
|
+
* request behaviour Express provides on top.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const SAMPLE_BODY: string =
|
|
33
|
+
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//OneUptime//On-Call//EN\r\nEND:VCALENDAR\r\n";
|
|
34
|
+
|
|
35
|
+
const SAMPLE_LAST_MODIFIED: Date = new Date("2026-08-30T10:11:12.345Z");
|
|
36
|
+
|
|
37
|
+
interface CapturedResponse {
|
|
38
|
+
headers: Record<string, string>;
|
|
39
|
+
removedHeaders: Array<string>;
|
|
40
|
+
statusCode: number | null;
|
|
41
|
+
body: unknown;
|
|
42
|
+
logBody: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function buildFakeResponse(): {
|
|
46
|
+
response: ExpressResponse;
|
|
47
|
+
captured: CapturedResponse;
|
|
48
|
+
} {
|
|
49
|
+
const captured: CapturedResponse = {
|
|
50
|
+
headers: {},
|
|
51
|
+
removedHeaders: [],
|
|
52
|
+
statusCode: null,
|
|
53
|
+
body: undefined,
|
|
54
|
+
logBody: undefined,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const response: Record<string, unknown> = {
|
|
58
|
+
set: (key: string, value: string): void => {
|
|
59
|
+
captured.headers[key] = value;
|
|
60
|
+
},
|
|
61
|
+
setHeader: (key: string, value: string): void => {
|
|
62
|
+
captured.headers[key] = value;
|
|
63
|
+
},
|
|
64
|
+
removeHeader: (key: string): void => {
|
|
65
|
+
captured.removedHeaders.push(key);
|
|
66
|
+
delete captured.headers[key];
|
|
67
|
+
},
|
|
68
|
+
status: (code: number): unknown => {
|
|
69
|
+
captured.statusCode = code;
|
|
70
|
+
return response;
|
|
71
|
+
},
|
|
72
|
+
send: (body: unknown): void => {
|
|
73
|
+
captured.body = body;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
Object.defineProperty(response, "logBody", {
|
|
78
|
+
set: (value: unknown): void => {
|
|
79
|
+
captured.logBody = value;
|
|
80
|
+
},
|
|
81
|
+
get: (): unknown => {
|
|
82
|
+
return captured.logBody;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
response: response as unknown as ExpressResponse,
|
|
88
|
+
captured,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
describe("Response.getCalendarETag", () => {
|
|
93
|
+
test("is the quoted first 32 hex characters of the body's SHA-256", () => {
|
|
94
|
+
const expected: string = createHash("sha256")
|
|
95
|
+
.update(SAMPLE_BODY, "utf8")
|
|
96
|
+
.digest("hex")
|
|
97
|
+
.slice(0, 32);
|
|
98
|
+
|
|
99
|
+
expect(ResponseUtil.getCalendarETag(SAMPLE_BODY)).toBe(`"${expected}"`);
|
|
100
|
+
expect(ResponseUtil.getCalendarETag(SAMPLE_BODY)).toHaveLength(34);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("is a strong tag, never W/-prefixed", () => {
|
|
104
|
+
expect(ResponseUtil.getCalendarETag(SAMPLE_BODY).startsWith("W/")).toBe(
|
|
105
|
+
false,
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("is deterministic for the same bytes", () => {
|
|
110
|
+
expect(ResponseUtil.getCalendarETag(SAMPLE_BODY)).toBe(
|
|
111
|
+
ResponseUtil.getCalendarETag(SAMPLE_BODY),
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("changes when a single byte of the body changes", () => {
|
|
116
|
+
expect(ResponseUtil.getCalendarETag(SAMPLE_BODY)).not.toBe(
|
|
117
|
+
ResponseUtil.getCalendarETag(SAMPLE_BODY.replace("2.0", "2.1")),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("hashes UTF-8 bytes, so two bodies differing only in a multi-byte character differ", () => {
|
|
122
|
+
expect(ResponseUtil.getCalendarETag("SUMMARY:Åsa")).not.toBe(
|
|
123
|
+
ResponseUtil.getCalendarETag("SUMMARY:Asa"),
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("Response.sendCalendarResponse (unit)", () => {
|
|
129
|
+
let nowSpy: ReturnType<typeof jest.spyOn>;
|
|
130
|
+
const NOW: number = Date.parse("2026-08-31T12:00:00.000Z");
|
|
131
|
+
|
|
132
|
+
beforeEach(() => {
|
|
133
|
+
nowSpy = jest.spyOn(Date, "now").mockImplementation(() => {
|
|
134
|
+
return NOW;
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
afterEach(() => {
|
|
139
|
+
nowSpy.mockRestore();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
function send(overrides: {
|
|
143
|
+
body?: string;
|
|
144
|
+
etag?: string;
|
|
145
|
+
lastModified?: Date;
|
|
146
|
+
prepare?: (response: ExpressResponse) => void;
|
|
147
|
+
}): CapturedResponse {
|
|
148
|
+
const { response, captured } = buildFakeResponse();
|
|
149
|
+
|
|
150
|
+
if (overrides.prepare) {
|
|
151
|
+
overrides.prepare(response);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
ResponseUtil.sendCalendarResponse({} as ExpressRequest, response, {
|
|
155
|
+
body: overrides.body ?? SAMPLE_BODY,
|
|
156
|
+
etag: overrides.etag ?? ResponseUtil.getCalendarETag(SAMPLE_BODY),
|
|
157
|
+
lastModified: overrides.lastModified ?? SAMPLE_LAST_MODIFIED,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return captured;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
test("sends the body with a 200", () => {
|
|
164
|
+
const captured: CapturedResponse = send({});
|
|
165
|
+
|
|
166
|
+
expect(captured.statusCode).toBe(200);
|
|
167
|
+
expect(captured.body).toBe(SAMPLE_BODY);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("declares text/calendar with a UTF-8 charset", () => {
|
|
171
|
+
const captured: CapturedResponse = send({});
|
|
172
|
+
|
|
173
|
+
expect(captured.headers["Content-Type"]).toBe(
|
|
174
|
+
"text/calendar; charset=utf-8",
|
|
175
|
+
);
|
|
176
|
+
expect(CALENDAR_RESPONSE_CONTENT_TYPE).toBe("text/calendar; charset=utf-8");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("is inline with the documented .ics filename", () => {
|
|
180
|
+
const captured: CapturedResponse = send({});
|
|
181
|
+
|
|
182
|
+
expect(captured.headers["Content-Disposition"]).toBe(
|
|
183
|
+
'inline; filename="oneuptime-on-call.ics"',
|
|
184
|
+
);
|
|
185
|
+
expect(CALENDAR_RESPONSE_FILE_NAME).toBe("oneuptime-on-call.ics");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("is private with a 300 second max-age", () => {
|
|
189
|
+
const captured: CapturedResponse = send({});
|
|
190
|
+
|
|
191
|
+
expect(captured.headers["Cache-Control"]).toBe("private, max-age=300");
|
|
192
|
+
expect(CALENDAR_RESPONSE_MAX_AGE_SECONDS).toBe(300);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("never marks the body public or shared-cacheable", () => {
|
|
196
|
+
const captured: CapturedResponse = send({});
|
|
197
|
+
|
|
198
|
+
expect(captured.headers["Cache-Control"]).not.toMatch(/public/);
|
|
199
|
+
expect(captured.headers["Cache-Control"]).not.toMatch(/s-maxage/);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("sets Expires 300 seconds from now, in RFC 1123 form", () => {
|
|
203
|
+
const captured: CapturedResponse = send({});
|
|
204
|
+
|
|
205
|
+
expect(captured.headers["Expires"]).toBe(
|
|
206
|
+
new Date(NOW + 300 * 1000).toUTCString(),
|
|
207
|
+
);
|
|
208
|
+
expect(captured.headers["Expires"]).toBe("Mon, 31 Aug 2026 12:05:00 GMT");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("sets Last-Modified from the supplied date, in RFC 1123 form", () => {
|
|
212
|
+
const captured: CapturedResponse = send({});
|
|
213
|
+
|
|
214
|
+
expect(captured.headers["Last-Modified"]).toBe(
|
|
215
|
+
"Sun, 30 Aug 2026 10:11:12 GMT",
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("passes a quoted strong ETag through unchanged", () => {
|
|
220
|
+
const etag: string = ResponseUtil.getCalendarETag(SAMPLE_BODY);
|
|
221
|
+
const captured: CapturedResponse = send({ etag });
|
|
222
|
+
|
|
223
|
+
expect(captured.headers["ETag"]).toBe(etag);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("quotes a bare ETag", () => {
|
|
227
|
+
const captured: CapturedResponse = send({ etag: "abcdef0123456789" });
|
|
228
|
+
|
|
229
|
+
expect(captured.headers["ETag"]).toBe('"abcdef0123456789"');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("strengthens a weak ETag", () => {
|
|
233
|
+
const captured: CapturedResponse = send({ etag: 'W/"abcdef"' });
|
|
234
|
+
|
|
235
|
+
expect(captured.headers["ETag"]).toBe('"abcdef"');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("strips characters that would break the ETag header", () => {
|
|
239
|
+
const captured: CapturedResponse = send({ etag: 'ab"c\r\nd' });
|
|
240
|
+
|
|
241
|
+
expect(captured.headers["ETag"]).toBe('"abcd"');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("forbids sniffing and indexing", () => {
|
|
245
|
+
const captured: CapturedResponse = send({});
|
|
246
|
+
|
|
247
|
+
expect(captured.headers["X-Content-Type-Options"]).toBe("nosniff");
|
|
248
|
+
expect(captured.headers["X-Robots-Tag"]).toBe("noindex");
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("sends no Pragma header", () => {
|
|
252
|
+
const captured: CapturedResponse = send({});
|
|
253
|
+
|
|
254
|
+
expect(captured.headers["Pragma"]).toBeUndefined();
|
|
255
|
+
expect(captured.removedHeaders).toContain("Pragma");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
/*
|
|
259
|
+
* The reason this helper exists rather than sendCustomResponse: a
|
|
260
|
+
* no-cache middleware upstream may already have stamped the trio, and the
|
|
261
|
+
* feed must undo all three, not just two.
|
|
262
|
+
*/
|
|
263
|
+
test("removes a Pragma set earlier and overrides an earlier no-store", () => {
|
|
264
|
+
const captured: CapturedResponse = send({
|
|
265
|
+
prepare: (response: ExpressResponse): void => {
|
|
266
|
+
ResponseUtil.setNoCacheHeaders(response);
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
expect(captured.headers["Pragma"]).toBeUndefined();
|
|
271
|
+
expect(captured.headers["Cache-Control"]).toBe("private, max-age=300");
|
|
272
|
+
expect(captured.headers["Expires"]).not.toBe("0");
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("sends exactly the documented header set", () => {
|
|
276
|
+
const captured: CapturedResponse = send({});
|
|
277
|
+
|
|
278
|
+
expect(Object.keys(captured.headers).sort()).toEqual(
|
|
279
|
+
[
|
|
280
|
+
"Cache-Control",
|
|
281
|
+
"Content-Disposition",
|
|
282
|
+
"Content-Type",
|
|
283
|
+
"ETag",
|
|
284
|
+
"Expires",
|
|
285
|
+
"Last-Modified",
|
|
286
|
+
"X-Content-Type-Options",
|
|
287
|
+
"X-Robots-Tag",
|
|
288
|
+
].sort(),
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("logs the size and type of what went out, never the body", () => {
|
|
293
|
+
const captured: CapturedResponse = send({});
|
|
294
|
+
|
|
295
|
+
expect(captured.logBody).toEqual({
|
|
296
|
+
contentType: "text/calendar; charset=utf-8",
|
|
297
|
+
bytes: Buffer.byteLength(SAMPLE_BODY, "utf8"),
|
|
298
|
+
});
|
|
299
|
+
expect(JSON.stringify(captured.logBody)).not.toContain("VCALENDAR");
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("counts bytes, not characters, in the log", () => {
|
|
303
|
+
const body: string = "SUMMARY:Åsa Ö\r\n";
|
|
304
|
+
const captured: CapturedResponse = send({ body });
|
|
305
|
+
|
|
306
|
+
expect((captured.logBody as { bytes: number }).bytes).toBe(
|
|
307
|
+
Buffer.byteLength(body, "utf8"),
|
|
308
|
+
);
|
|
309
|
+
expect((captured.logBody as { bytes: number }).bytes).toBeGreaterThan(
|
|
310
|
+
body.length,
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("does not inspect the request", () => {
|
|
315
|
+
const { response, captured } = buildFakeResponse();
|
|
316
|
+
|
|
317
|
+
expect(() => {
|
|
318
|
+
ResponseUtil.sendCalendarResponse(
|
|
319
|
+
undefined as unknown as ExpressRequest,
|
|
320
|
+
response,
|
|
321
|
+
{
|
|
322
|
+
body: SAMPLE_BODY,
|
|
323
|
+
etag: "x",
|
|
324
|
+
lastModified: SAMPLE_LAST_MODIFIED,
|
|
325
|
+
},
|
|
326
|
+
);
|
|
327
|
+
}).not.toThrow();
|
|
328
|
+
|
|
329
|
+
expect(captured.statusCode).toBe(200);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
/*
|
|
334
|
+
* The conditional-request and HEAD behaviour is Express's, driven by the
|
|
335
|
+
* headers the helper sets. That only shows up against a real response object,
|
|
336
|
+
* so these run a throwaway Express app on an ephemeral port.
|
|
337
|
+
*/
|
|
338
|
+
describe("Response.sendCalendarResponse (through Express)", () => {
|
|
339
|
+
let server: http.Server;
|
|
340
|
+
let baseUrl: string;
|
|
341
|
+
|
|
342
|
+
const etag: string = ResponseUtil.getCalendarETag(SAMPLE_BODY);
|
|
343
|
+
|
|
344
|
+
beforeAll(async () => {
|
|
345
|
+
const app: express.Express = express();
|
|
346
|
+
|
|
347
|
+
app.get("/feed.ics", (req: express.Request, res: express.Response) => {
|
|
348
|
+
ResponseUtil.sendCalendarResponse(
|
|
349
|
+
req as unknown as ExpressRequest,
|
|
350
|
+
res as unknown as ExpressResponse,
|
|
351
|
+
{
|
|
352
|
+
body: SAMPLE_BODY,
|
|
353
|
+
etag,
|
|
354
|
+
lastModified: SAMPLE_LAST_MODIFIED,
|
|
355
|
+
},
|
|
356
|
+
);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
/* A route that stamps the no-cache trio first, as a middleware might. */
|
|
360
|
+
app.get(
|
|
361
|
+
"/feed-after-nocache.ics",
|
|
362
|
+
(req: express.Request, res: express.Response) => {
|
|
363
|
+
ResponseUtil.setNoCacheHeaders(res as unknown as ExpressResponse);
|
|
364
|
+
ResponseUtil.sendCalendarResponse(
|
|
365
|
+
req as unknown as ExpressRequest,
|
|
366
|
+
res as unknown as ExpressResponse,
|
|
367
|
+
{
|
|
368
|
+
body: SAMPLE_BODY,
|
|
369
|
+
etag,
|
|
370
|
+
lastModified: SAMPLE_LAST_MODIFIED,
|
|
371
|
+
},
|
|
372
|
+
);
|
|
373
|
+
},
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
server = http.createServer(app);
|
|
377
|
+
|
|
378
|
+
await new Promise<void>((resolve: () => void) => {
|
|
379
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
const address: AddressInfo = server.address() as AddressInfo;
|
|
383
|
+
baseUrl = `http://127.0.0.1:${address.port}`;
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
afterAll(async () => {
|
|
387
|
+
await new Promise<void>((resolve: () => void) => {
|
|
388
|
+
server.close(() => {
|
|
389
|
+
resolve();
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
interface HttpResult {
|
|
395
|
+
status: number;
|
|
396
|
+
headers: Headers;
|
|
397
|
+
body: string;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/*
|
|
401
|
+
* A header bag with the `get()` shape of the Fetch API's Headers, over
|
|
402
|
+
* Node's lower-cased IncomingHttpHeaders. The jest environment here is
|
|
403
|
+
* jsdom, which has no fetch, so the request goes out through http.request.
|
|
404
|
+
*/
|
|
405
|
+
class Headers {
|
|
406
|
+
public constructor(private raw: http.IncomingHttpHeaders) {}
|
|
407
|
+
|
|
408
|
+
public get(name: string): string | null {
|
|
409
|
+
const value: string | Array<string> | undefined =
|
|
410
|
+
this.raw[name.toLowerCase()];
|
|
411
|
+
|
|
412
|
+
if (value === undefined) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return Array.isArray(value) ? value.join(", ") : value;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function request(
|
|
421
|
+
path: string,
|
|
422
|
+
init?: { method?: string; headers?: Record<string, string> },
|
|
423
|
+
): Promise<HttpResult> {
|
|
424
|
+
return new Promise<HttpResult>(
|
|
425
|
+
(
|
|
426
|
+
resolve: (result: HttpResult) => void,
|
|
427
|
+
reject: (error: Error) => void,
|
|
428
|
+
) => {
|
|
429
|
+
const clientRequest: http.ClientRequest = http.request(
|
|
430
|
+
`${baseUrl}${path}`,
|
|
431
|
+
{ method: init?.method || "GET", headers: init?.headers || {} },
|
|
432
|
+
(response: http.IncomingMessage) => {
|
|
433
|
+
const chunks: Array<Buffer> = [];
|
|
434
|
+
|
|
435
|
+
response.on("data", (chunk: Buffer) => {
|
|
436
|
+
chunks.push(chunk);
|
|
437
|
+
});
|
|
438
|
+
response.on("end", () => {
|
|
439
|
+
resolve({
|
|
440
|
+
status: response.statusCode || 0,
|
|
441
|
+
headers: new Headers(response.headers),
|
|
442
|
+
body: Buffer.concat(chunks).toString("utf8"),
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
response.on("error", reject);
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
clientRequest.on("error", reject);
|
|
450
|
+
clientRequest.end();
|
|
451
|
+
},
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
test("a plain GET is a 200 with the body and the calendar headers", async () => {
|
|
456
|
+
const result: HttpResult = await request("/feed.ics");
|
|
457
|
+
|
|
458
|
+
expect(result.status).toBe(200);
|
|
459
|
+
expect(result.body).toBe(SAMPLE_BODY);
|
|
460
|
+
expect(result.headers.get("content-type")).toBe(
|
|
461
|
+
"text/calendar; charset=utf-8",
|
|
462
|
+
);
|
|
463
|
+
expect(result.headers.get("etag")).toBe(etag);
|
|
464
|
+
expect(result.headers.get("last-modified")).toBe(
|
|
465
|
+
"Sun, 30 Aug 2026 10:11:12 GMT",
|
|
466
|
+
);
|
|
467
|
+
expect(result.headers.get("cache-control")).toBe("private, max-age=300");
|
|
468
|
+
expect(result.headers.get("content-disposition")).toBe(
|
|
469
|
+
'inline; filename="oneuptime-on-call.ics"',
|
|
470
|
+
);
|
|
471
|
+
expect(result.headers.get("x-content-type-options")).toBe("nosniff");
|
|
472
|
+
expect(result.headers.get("x-robots-tag")).toBe("noindex");
|
|
473
|
+
expect(result.headers.get("pragma")).toBeNull();
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
test("Express keeps our strong ETag rather than generating its own weak one", async () => {
|
|
477
|
+
const result: HttpResult = await request("/feed.ics");
|
|
478
|
+
|
|
479
|
+
expect(result.headers.get("etag")).toBe(etag);
|
|
480
|
+
expect(result.headers.get("etag")?.startsWith("W/")).toBe(false);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test("If-None-Match with the current ETag is answered 304 with no body", async () => {
|
|
484
|
+
const result: HttpResult = await request("/feed.ics", {
|
|
485
|
+
headers: { "If-None-Match": etag },
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
expect(result.status).toBe(304);
|
|
489
|
+
expect(result.body).toBe("");
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
test("a weak If-None-Match still matches (clients such as Outlook send W/)", async () => {
|
|
493
|
+
const result: HttpResult = await request("/feed.ics", {
|
|
494
|
+
headers: { "If-None-Match": `W/${etag}` },
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
expect(result.status).toBe(304);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test("If-None-Match with a stale ETag gets the full body", async () => {
|
|
501
|
+
const result: HttpResult = await request("/feed.ics", {
|
|
502
|
+
headers: { "If-None-Match": '"stale"' },
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
expect(result.status).toBe(200);
|
|
506
|
+
expect(result.body).toBe(SAMPLE_BODY);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("If-Modified-Since at or after Last-Modified is answered 304", async () => {
|
|
510
|
+
const result: HttpResult = await request("/feed.ics", {
|
|
511
|
+
headers: { "If-Modified-Since": "Sun, 30 Aug 2026 10:11:12 GMT" },
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
expect(result.status).toBe(304);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
test("If-Modified-Since before Last-Modified gets the full body", async () => {
|
|
518
|
+
const result: HttpResult = await request("/feed.ics", {
|
|
519
|
+
headers: { "If-Modified-Since": "Sat, 29 Aug 2026 00:00:00 GMT" },
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
expect(result.status).toBe(200);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
/*
|
|
526
|
+
* Google's fetcher sends Cache-Control: no-cache with its If-None-Match,
|
|
527
|
+
* and Express's `fresh` treats that as "do not answer 304". Pinned so the
|
|
528
|
+
* ETag tests above are not read as a promise about Google.
|
|
529
|
+
*/
|
|
530
|
+
test("a client that sends Cache-Control: no-cache gets the full body even with a matching ETag", async () => {
|
|
531
|
+
const result: HttpResult = await request("/feed.ics", {
|
|
532
|
+
headers: { "If-None-Match": etag, "Cache-Control": "no-cache" },
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
expect(result.status).toBe(200);
|
|
536
|
+
expect(result.body).toBe(SAMPLE_BODY);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test("HEAD is answered natively with the headers and no body", async () => {
|
|
540
|
+
const result: HttpResult = await request("/feed.ics", { method: "HEAD" });
|
|
541
|
+
|
|
542
|
+
expect(result.status).toBe(200);
|
|
543
|
+
expect(result.body).toBe("");
|
|
544
|
+
expect(result.headers.get("content-type")).toBe(
|
|
545
|
+
"text/calendar; charset=utf-8",
|
|
546
|
+
);
|
|
547
|
+
expect(result.headers.get("etag")).toBe(etag);
|
|
548
|
+
expect(result.headers.get("content-length")).toBe(
|
|
549
|
+
String(Buffer.byteLength(SAMPLE_BODY, "utf8")),
|
|
550
|
+
);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test("a Pragma stamped by an earlier middleware does not reach the wire", async () => {
|
|
554
|
+
const result: HttpResult = await request("/feed-after-nocache.ics");
|
|
555
|
+
|
|
556
|
+
expect(result.status).toBe(200);
|
|
557
|
+
expect(result.headers.get("pragma")).toBeNull();
|
|
558
|
+
expect(result.headers.get("cache-control")).toBe("private, max-age=300");
|
|
559
|
+
expect(result.headers.get("expires")).not.toBe("0");
|
|
560
|
+
});
|
|
561
|
+
});
|
|
@@ -329,10 +329,16 @@ describe("ConnectorErrorMessage.toMessage on the real Google SecOps client error
|
|
|
329
329
|
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
+
/*
|
|
333
|
+
* A real Google token host, because the client now allowlists token_uri
|
|
334
|
+
* and rejects anything else at construction. Nothing here reaches the
|
|
335
|
+
* network — the fetch is injected — so the host is only ever a string
|
|
336
|
+
* the validator has to accept.
|
|
337
|
+
*/
|
|
332
338
|
const SERVICE_ACCOUNT_JSON: string = JSON.stringify({
|
|
333
339
|
client_email: "poller@example.iam.gserviceaccount.com",
|
|
334
340
|
private_key: privateKey,
|
|
335
|
-
token_uri: "https://oauth2.
|
|
341
|
+
token_uri: "https://oauth2.googleapis.com/token",
|
|
336
342
|
});
|
|
337
343
|
|
|
338
344
|
const INSTANCE: string =
|
|
@@ -410,14 +416,29 @@ describe("ConnectorErrorMessage.toMessage on the real Google SecOps client error
|
|
|
410
416
|
expect(rawMessage.startsWith(prefix)).toBe(true);
|
|
411
417
|
|
|
412
418
|
/*
|
|
413
|
-
* The template is a 46-character prefix plus responseText.slice(0, 500)
|
|
414
|
-
* 546 > 500, so under
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* null while the connector
|
|
419
|
+
* The template is a 46-character prefix plus responseText.slice(0, 500),
|
|
420
|
+
* and now an operator hint behind that. 546 was already > 500, so under
|
|
421
|
+
* the old TableColumnType.LongText declaration checkMaxLengthOfFields
|
|
422
|
+
* threw BadDataException on the poller's own recovery write — which is
|
|
423
|
+
* how lastPolledAt and lastError both stayed null while the connector
|
|
424
|
+
* silently stopped polling. The hint only makes the overflow worse.
|
|
425
|
+
*
|
|
426
|
+
* Both halves are pinned by content rather than by total length: the
|
|
427
|
+
* echo is exactly the first 500 characters of the body, and the tail is
|
|
428
|
+
* exactly what describeHttpFailure derives for this status and body. A
|
|
429
|
+
* length-only check would go on passing if the echo and the hint ever
|
|
430
|
+
* traded characters with each other.
|
|
418
431
|
*/
|
|
432
|
+
const guidance: string = GoogleSecOpsClient.describeHttpFailure(
|
|
433
|
+
403,
|
|
434
|
+
HUGE_ERROR_BODY,
|
|
435
|
+
);
|
|
436
|
+
|
|
419
437
|
expect(prefix.length).toBe(46);
|
|
420
|
-
expect(
|
|
438
|
+
expect(guidance.length).toBeGreaterThan(0);
|
|
439
|
+
expect(rawMessage).toBe(
|
|
440
|
+
`${prefix}${HUGE_ERROR_BODY.slice(0, 500)}${guidance}`,
|
|
441
|
+
);
|
|
421
442
|
expect(rawMessage.length).toBeGreaterThan(OLD_LAST_ERROR_COLUMN_MAX_LENGTH);
|
|
422
443
|
expect(rawMessage.length).toBeGreaterThan(
|
|
423
444
|
getMaxLengthFromTableColumnType(TableColumnType.LongText)!,
|
|
@@ -428,7 +449,16 @@ describe("ConnectorErrorMessage.toMessage on the real Google SecOps client error
|
|
|
428
449
|
expect(stored.length).toBeLessThanOrEqual(
|
|
429
450
|
MAX_CONNECTOR_ERROR_MESSAGE_LENGTH,
|
|
430
451
|
);
|
|
431
|
-
|
|
452
|
+
|
|
453
|
+
/*
|
|
454
|
+
* Prefix + echo + hint still fits inside 1000, so the whole diagnostic
|
|
455
|
+
* survives the clamp. Asserted rather than assumed: if a future hint
|
|
456
|
+
* pushes the worst case past the clamp, the operator starts reading a
|
|
457
|
+
* truncated message and this test should be the thing that says so.
|
|
458
|
+
*/
|
|
459
|
+
expect(rawMessage.length).toBeLessThanOrEqual(
|
|
460
|
+
MAX_CONNECTOR_ERROR_MESSAGE_LENGTH,
|
|
461
|
+
);
|
|
432
462
|
expect(stored).toBe(rawMessage);
|
|
433
463
|
});
|
|
434
464
|
|
|
@@ -442,7 +472,16 @@ describe("ConnectorErrorMessage.toMessage on the real Google SecOps client error
|
|
|
442
472
|
|
|
443
473
|
expect(rawMessage.startsWith(prefix)).toBe(true);
|
|
444
474
|
|
|
445
|
-
|
|
475
|
+
/*
|
|
476
|
+
* Prefix + 500 characters of echoed body, and nothing behind it. The
|
|
477
|
+
* token exchange deliberately carries no Chronicle hint: every hint
|
|
478
|
+
* describeHttpFailure knows how to give is about the instance resource
|
|
479
|
+
* name, the chronicle.viewer role or the region prefix, none of which
|
|
480
|
+
* explains a refusal from the OAuth token endpoint. Pinned exactly, so
|
|
481
|
+
* that appending guidance here becomes a decision rather than a
|
|
482
|
+
* side effect of editing the alerts path.
|
|
483
|
+
*/
|
|
484
|
+
expect(rawMessage).toBe(`${prefix}${HUGE_ERROR_BODY.slice(0, 500)}`);
|
|
446
485
|
expect(rawMessage.length).toBe(prefix.length + 500);
|
|
447
486
|
expect(rawMessage.length).toBeGreaterThan(OLD_LAST_ERROR_COLUMN_MAX_LENGTH);
|
|
448
487
|
|