@oneuptime/common 11.3.25 → 11.3.28
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/AIConversation.ts +322 -0
- package/Models/DatabaseModels/AIConversationMessage.ts +495 -0
- package/Models/DatabaseModels/AIRun.ts +584 -0
- package/Models/DatabaseModels/AIRunEvent.ts +443 -0
- package/Models/DatabaseModels/IncidentEpisodeRoleMember.ts +1 -1
- package/Models/DatabaseModels/IncidentMember.ts +1 -1
- package/Models/DatabaseModels/IncomingCallPolicy.ts +13 -0
- package/Models/DatabaseModels/Index.ts +11 -0
- package/Models/DatabaseModels/LlmLog.ts +26 -0
- package/Models/DatabaseModels/LlmProvider.ts +2 -2
- package/Models/DatabaseModels/MigrationFailure.ts +170 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +51 -2
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +44 -0
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +6 -0
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.ts +6 -0
- package/Models/DatabaseModels/OnCallDutyPolicyTimeLog.ts +1 -1
- package/Models/DatabaseModels/OnCallDutyPolicyUserOverride.ts +2 -2
- package/Server/API/AIChatAPI.ts +693 -0
- package/Server/API/AlertAPI.ts +1 -1
- package/Server/API/IncidentAPI.ts +2 -2
- package/Server/API/IncidentEpisodeAPI.ts +1 -1
- package/Server/API/LlmProviderAPI.ts +170 -0
- package/Server/API/MicrosoftTeamsAPI.ts +5 -0
- package/Server/API/ScheduledMaintenanceAPI.ts +1 -1
- package/Server/API/SlackAPI.ts +663 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.ts +245 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.ts +36 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.ts +46 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.ts +119 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.ts +81 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
- package/Server/Infrastructure/PostgresDatabase.ts +11 -0
- package/Server/Services/AIConversationMessageService.ts +39 -0
- package/Server/Services/AIConversationService.ts +63 -0
- package/Server/Services/AIRunEventService.ts +39 -0
- package/Server/Services/AIRunService.ts +39 -0
- package/Server/Services/AIService.ts +127 -33
- package/Server/Services/AnalyticsDatabaseService.ts +10 -1
- package/Server/Services/DatabaseService.ts +29 -1
- package/Server/Services/IncidentService.ts +1 -1
- package/Server/Services/IncomingCallPolicyEscalationRuleService.ts +75 -2
- package/Server/Services/IncomingCallPolicyService.ts +42 -1
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/LlmProviderService.ts +110 -0
- package/Server/Services/MigrationFailureService.ts +9 -0
- package/Server/Services/MonitorProbeService.ts +33 -7
- package/Server/Services/MonitorService.ts +19 -7
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +185 -41
- package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +23 -15
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +39 -0
- package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +11 -2
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +502 -26
- package/Server/Services/OnCallDutyPolicyTimeLogService.ts +49 -7
- package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +218 -4
- package/Server/Services/ProjectCallSMSConfigService.ts +82 -1
- package/Server/Services/ProjectService.ts +24 -0
- package/Server/Services/TeamMemberService.ts +6 -0
- package/Server/Services/UserNotificationRuleService.ts +13 -42
- package/Server/Services/UserOnCallLogService.ts +90 -0
- package/Server/Services/UserOnCallLogTimelineService.ts +43 -1
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +74 -9
- package/Server/Types/Database/QueryUtil.ts +57 -0
- package/Server/Utils/AI/AIChatPrivacyFilter.ts +28 -0
- package/Server/Utils/AI/AlertAIContextBuilder.ts +6 -1
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +1066 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +239 -0
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +51 -0
- package/Server/Utils/AI/IncidentAIContextBuilder.ts +18 -3
- package/Server/Utils/AI/IncidentEpisodeAIContextBuilder.ts +6 -1
- package/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.ts +12 -2
- package/Server/Utils/AI/Toolbox/AlertTools.ts +201 -0
- package/Server/Utils/AI/Toolbox/AlertWriteTools.ts +174 -0
- package/Server/Utils/AI/Toolbox/ContextTools.ts +189 -0
- package/Server/Utils/AI/Toolbox/ExceptionTools.ts +149 -0
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +386 -0
- package/Server/Utils/AI/Toolbox/IncidentWriteTools.ts +350 -0
- package/Server/Utils/AI/Toolbox/Index.ts +231 -0
- package/Server/Utils/AI/Toolbox/LogTools.ts +339 -0
- package/Server/Utils/AI/Toolbox/MetricTools.ts +179 -0
- package/Server/Utils/AI/Toolbox/MonitorTools.ts +193 -0
- package/Server/Utils/AI/Toolbox/RecentChangesTools.ts +208 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +299 -0
- package/Server/Utils/AI/Toolbox/ToolTypes.ts +249 -0
- package/Server/Utils/AI/Toolbox/TraceTools.ts +402 -0
- package/Server/Utils/AI/Toolbox/WidgetBuilder.ts +189 -0
- package/Server/Utils/Database/MigrationFailureLog.ts +240 -0
- package/Server/Utils/IncomingCallPhoneNumber.ts +41 -0
- package/Server/Utils/LLM/LLMService.ts +605 -96
- package/Server/Utils/Telemetry/IoTSnapshotScan.ts +12 -9
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +12 -9
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +512 -19
- package/Server/Utils/Workspace/Slack/Slack.ts +80 -0
- package/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +0 -8
- package/Tests/Server/Services/OnCallDutyPolicyScheduleLayerUserReorder.test.ts +233 -0
- package/Tests/Server/Services/OnCallDutyPolicyTimeLogServiceScoping.test.ts +242 -0
- package/Tests/Server/Services/OnCallDutyPolicyUserOverrideEdit.test.ts +198 -0
- package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +495 -0
- package/Tests/Server/Types/Database/QueryUtil.test.ts +74 -0
- package/Tests/Server/Utils/AI/AIChatModelACL.test.ts +42 -0
- package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +89 -0
- package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +139 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +386 -0
- package/Tests/Server/Utils/AI/ToolArgsGetTimeRange.test.ts +62 -0
- package/Tests/Server/Utils/AI/ToolArgsScopeServiceIds.test.ts +79 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +223 -0
- package/Tests/Types/Billing/SubscriptionStatus.test.ts +127 -0
- package/Tests/Types/DateExhaustiveTimezone.test.ts +700 -0
- package/Tests/Types/DateTimezoneWallClock.test.ts +99 -0
- package/Tests/Types/Events/Recurring.test.ts +22 -9
- package/Tests/Types/Metrics/RecordingRuleDefinition.test.ts +213 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixes.test.ts +291 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixesRound2.test.ts +398 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeekendGap.test.ts +114 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeeklyDayShift.test.ts +76 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyMutation.test.ts +128 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyProbe.test.ts +203 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilDiffFuzz.test.ts +322 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilEdgeCases.test.ts +280 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveCurrentUser.test.ts +863 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveDaily.test.ts +900 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveMultiLayer.test.ts +1104 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +957 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveWeekly.test.ts +1095 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilInvariants.test.ts +232 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +227 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMonthYearAudit.test.ts +255 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerAudit.test.ts +388 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerEdge.test.ts +300 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilOvernightDSTAudit.test.ts +158 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapFix.test.ts +253 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapRepro.test.ts +157 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilRotationFixes.test.ts +414 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilTimezone.test.ts +243 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilWeekendGapRepro.test.ts +112 -0
- package/Tests/Types/OnCallDutyPolicy/OverrideLensAudit.test.ts +117 -0
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimes.test.ts +8 -6
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimesDefaultDayAudit.test.ts +112 -0
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimesExhaustive.test.ts +821 -0
- package/Tests/Types/OnCallDutyPolicy/RestrictionTimesMutationInvariant.test.ts +886 -0
- package/Tests/Types/OnCallDutyPolicy/UserOverrideUtil.test.ts +255 -0
- package/Tests/Types/OnCallDutyPolicy/UserOverrideUtilExhaustive.test.ts +1126 -0
- package/Types/AI/AIChatMessageRole.ts +6 -0
- package/Types/AI/AIChatMessageStatus.ts +32 -0
- package/Types/AI/AIChatPermissionMode.ts +69 -0
- package/Types/AI/AIChatTypes.ts +231 -0
- package/Types/AI/AIRunEventType.ts +17 -0
- package/Types/AI/AIRunStatus.ts +28 -0
- package/Types/AI/AIRunType.ts +6 -0
- package/Types/Date.ts +200 -15
- package/Types/LLM/LlmType.ts +6 -0
- package/Types/OnCallDutyPolicy/Layer.ts +790 -260
- package/Types/OnCallDutyPolicy/RestrictionTimes.ts +39 -13
- package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +21 -5
- package/UI/Components/Events/RecurringFieldElement.tsx +16 -0
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +22 -12
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +35 -3
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +8 -0
- package/UI/Components/Page/Page.tsx +14 -0
- package/UI/Components/RadioButtons/BasicRadioButtons.tsx +1 -1
- package/UI/Utils/LlmTypeDropdownOptions.ts +40 -0
- package/UI/Utils/TestLLMProvider.ts +59 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js +345 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js +521 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +619 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AIRunEvent.js +469 -0
- package/build/dist/Models/DatabaseModels/AIRunEvent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentMember.js +1 -1
- package/build/dist/Models/DatabaseModels/IncidentMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js +10 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +10 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LlmLog.js +28 -0
- package/build/dist/Models/DatabaseModels/LlmLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LlmProvider.js +2 -2
- package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MigrationFailure.js +196 -0
- package/build/dist/Models/DatabaseModels/MigrationFailure.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +52 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +45 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +6 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js +6 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +498 -0
- package/build/dist/Server/API/AIChatAPI.js.map +1 -0
- package/build/dist/Server/API/AlertAPI.js +1 -1
- package/build/dist/Server/API/IncidentAPI.js +2 -2
- package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +108 -1
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +4 -0
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
- package/build/dist/Server/API/SlackAPI.js +442 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js +96 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js +25 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js +46 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/PostgresDatabase.js +9 -0
- package/build/dist/Server/Infrastructure/PostgresDatabase.js.map +1 -1
- package/build/dist/Server/Services/AIConversationMessageService.js +34 -0
- package/build/dist/Server/Services/AIConversationMessageService.js.map +1 -0
- package/build/dist/Server/Services/AIConversationService.js +42 -0
- package/build/dist/Server/Services/AIConversationService.js.map +1 -0
- package/build/dist/Server/Services/AIRunEventService.js +34 -0
- package/build/dist/Server/Services/AIRunEventService.js.map +1 -0
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -0
- package/build/dist/Server/Services/AIService.js +84 -25
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +10 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +20 -1
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js +55 -2
- package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyService.js +40 -0
- package/build/dist/Server/Services/IncomingCallPolicyService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +108 -0
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
- package/build/dist/Server/Services/MigrationFailureService.js +9 -0
- package/build/dist/Server/Services/MigrationFailureService.js.map +1 -0
- package/build/dist/Server/Services/MonitorProbeService.js +33 -7
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +12 -5
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +81 -22
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +134 -31
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +21 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +36 -0
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +121 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +11 -2
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +409 -36
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js +52 -8
- package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +170 -3
- package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
- package/build/dist/Server/Services/ProjectCallSMSConfigService.js +72 -0
- package/build/dist/Server/Services/ProjectCallSMSConfigService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +6 -0
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +12 -30
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +75 -0
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogTimelineService.js +31 -1
- package/build/dist/Server/Services/UserOnCallLogTimelineService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +50 -7
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +45 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +18 -0
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -0
- package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js +6 -1
- package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +756 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +165 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +44 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -0
- package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js +18 -3
- package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js +6 -1
- package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js +12 -2
- package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +167 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js +136 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js +141 -0
- package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js +117 -0
- package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +318 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js +280 -0
- package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +153 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/LogTools.js +246 -0
- package/build/dist/Server/Utils/AI/Toolbox/LogTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +120 -0
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +158 -0
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js +165 -0
- package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +228 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js +142 -0
- package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js +309 -0
- package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js +120 -0
- package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js.map +1 -0
- package/build/dist/Server/Utils/Database/MigrationFailureLog.js +174 -0
- package/build/dist/Server/Utils/Database/MigrationFailureLog.js.map +1 -0
- package/build/dist/Server/Utils/IncomingCallPhoneNumber.js +30 -0
- package/build/dist/Server/Utils/IncomingCallPhoneNumber.js.map +1 -0
- package/build/dist/Server/Utils/LLM/LLMService.js +452 -92
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js +11 -8
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +11 -8
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +378 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +59 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
- package/build/dist/Types/AI/AIChatMessageRole.js +7 -0
- package/build/dist/Types/AI/AIChatMessageRole.js.map +1 -0
- package/build/dist/Types/AI/AIChatMessageStatus.js +27 -0
- package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -0
- package/build/dist/Types/AI/AIChatPermissionMode.js +53 -0
- package/build/dist/Types/AI/AIChatPermissionMode.js.map +1 -0
- package/build/dist/Types/AI/AIChatTypes.js +74 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -0
- package/build/dist/Types/AI/AIRunEventType.js +18 -0
- package/build/dist/Types/AI/AIRunEventType.js.map +1 -0
- package/build/dist/Types/AI/AIRunStatus.js +26 -0
- package/build/dist/Types/AI/AIRunStatus.js.map +1 -0
- package/build/dist/Types/AI/AIRunType.js +7 -0
- package/build/dist/Types/AI/AIRunType.js.map +1 -0
- package/build/dist/Types/Date.js +153 -15
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/LLM/LlmType.js +6 -0
- package/build/dist/Types/LLM/LlmType.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +602 -171
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js +27 -13
- package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +20 -5
- package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
- package/build/dist/UI/Components/Events/RecurringFieldElement.js +13 -0
- package/build/dist/UI/Components/Events/RecurringFieldElement.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +10 -5
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +18 -4
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/Page/Page.js +3 -1
- package/build/dist/UI/Components/Page/Page.js.map +1 -1
- package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +1 -1
- package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
- package/build/dist/UI/Utils/LlmTypeDropdownOptions.js +38 -0
- package/build/dist/UI/Utils/LlmTypeDropdownOptions.js.map +1 -0
- package/build/dist/UI/Utils/TestLLMProvider.js +37 -0
- package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,886 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RESTRICTION-TIMES MUTATION HYGIENE & CROSS-CALL INVARIANTS (hardening lock-in).
|
|
3
|
+
*
|
|
4
|
+
* The audit flagged an in-place mutation defect: the Daily branch of
|
|
5
|
+
* LayerUtil.trimStartAndEndTimesBasedOnRestrictionTimes used to write the
|
|
6
|
+
* day-moved start/end back into the SHARED RestrictionTimes.dayRestrictionTimes,
|
|
7
|
+
* corrupting the caller's object across events / layers / calls and making
|
|
8
|
+
* resolution order-dependent. The fix computes a LOCAL moved copy
|
|
9
|
+
* (keepTimeButMoveDay) and never touches the shared object.
|
|
10
|
+
*
|
|
11
|
+
* This suite locks that fix in three ways:
|
|
12
|
+
* (1) NON-MUTATION: after any number of getEvents calls over different
|
|
13
|
+
* calendar windows/days, the ORIGINAL RestrictionTimes' dayRestrictionTimes
|
|
14
|
+
* (Daily) / weeklyRestrictionTimes (Weekly) are byte-for-byte unchanged —
|
|
15
|
+
* same millisecond values AND the same Date/object instances (no reassign).
|
|
16
|
+
* (2) INDEPENDENCE / NO CROSS-CALL CORRUPTION: two getEvents calls that share
|
|
17
|
+
* one RestrictionTimes instance are idempotent (re-running an earlier
|
|
18
|
+
* window after an intervening different window reproduces the earlier
|
|
19
|
+
* result exactly) AND agree with a pristine deep-clone that was never
|
|
20
|
+
* passed through getEvents (correctness, not just stability).
|
|
21
|
+
* (3) LAYER INDEPENDENCE: an unrestricted layer and a restricted layer that
|
|
22
|
+
* share nothing resolve independently, including when expanded through the
|
|
23
|
+
* same LayerUtil instance (its per-call `timezone` never leaks across
|
|
24
|
+
* calls) and when merged via getMultiLayerEvents.
|
|
25
|
+
*
|
|
26
|
+
* All assertions compare a run against itself or an independent identical clone,
|
|
27
|
+
* so they hold regardless of the process TZ the suite runs under.
|
|
28
|
+
*/
|
|
29
|
+
import LayerUtil, { LayerProps } from "../../../Types/OnCallDutyPolicy/Layer";
|
|
30
|
+
import CalendarEvent from "../../../Types/Calendar/CalendarEvent";
|
|
31
|
+
import RestrictionTimes, {
|
|
32
|
+
RestrictionType,
|
|
33
|
+
WeeklyResctriction,
|
|
34
|
+
} from "../../../Types/OnCallDutyPolicy/RestrictionTimes";
|
|
35
|
+
import Recurring from "../../../Types/Events/Recurring";
|
|
36
|
+
import OneUptimeDate from "../../../Types/Date";
|
|
37
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
38
|
+
import EventInterval from "../../../Types/Events/EventInterval";
|
|
39
|
+
import PositiveNumber from "../../../Types/PositiveNumber";
|
|
40
|
+
import DayOfWeek from "../../../Types/Day/DayOfWeek";
|
|
41
|
+
import StartAndEndTime from "../../../Types/Time/StartAndEndTime";
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* ---------------------------------------------------------------------------
|
|
45
|
+
* Builders / helpers (mirrors sibling LayerUtil test style).
|
|
46
|
+
* ---------------------------------------------------------------------------
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
function user(id: string): User {
|
|
50
|
+
return {
|
|
51
|
+
id: {
|
|
52
|
+
toString: (): string => {
|
|
53
|
+
return id;
|
|
54
|
+
},
|
|
55
|
+
} as any,
|
|
56
|
+
} as User;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function noRestriction(): RestrictionTimes {
|
|
60
|
+
const r: RestrictionTimes = new RestrictionTimes();
|
|
61
|
+
r.restictionType = RestrictionType.None;
|
|
62
|
+
r.dayRestrictionTimes = null;
|
|
63
|
+
r.weeklyRestrictionTimes = [];
|
|
64
|
+
return r;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function dailyRestriction(
|
|
68
|
+
startHour: number,
|
|
69
|
+
startMinute: number,
|
|
70
|
+
endHour: number,
|
|
71
|
+
endMinute: number,
|
|
72
|
+
): RestrictionTimes {
|
|
73
|
+
const r: RestrictionTimes = new RestrictionTimes();
|
|
74
|
+
r.restictionType = RestrictionType.Daily;
|
|
75
|
+
r.dayRestrictionTimes = {
|
|
76
|
+
startTime: OneUptimeDate.getDateWithCustomTime({
|
|
77
|
+
hours: startHour,
|
|
78
|
+
minutes: startMinute,
|
|
79
|
+
seconds: 0,
|
|
80
|
+
}),
|
|
81
|
+
endTime: OneUptimeDate.getDateWithCustomTime({
|
|
82
|
+
hours: endHour,
|
|
83
|
+
minutes: endMinute,
|
|
84
|
+
seconds: 0,
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
return r;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface WeeklyEntrySpec {
|
|
91
|
+
startDay: DayOfWeek;
|
|
92
|
+
endDay: DayOfWeek;
|
|
93
|
+
startTimeIso: string;
|
|
94
|
+
endTimeIso: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function weeklyRestriction(entries: WeeklyEntrySpec[]): RestrictionTimes {
|
|
98
|
+
const r: RestrictionTimes = new RestrictionTimes();
|
|
99
|
+
r.restictionType = RestrictionType.Weekly;
|
|
100
|
+
r.dayRestrictionTimes = null;
|
|
101
|
+
r.weeklyRestrictionTimes = entries.map(
|
|
102
|
+
(e: WeeklyEntrySpec): WeeklyResctriction => {
|
|
103
|
+
return {
|
|
104
|
+
startDay: e.startDay,
|
|
105
|
+
endDay: e.endDay,
|
|
106
|
+
startTime: OneUptimeDate.fromString(e.startTimeIso),
|
|
107
|
+
endTime: OneUptimeDate.fromString(e.endTimeIso),
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
return r;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface LayerConfig {
|
|
115
|
+
users: string[];
|
|
116
|
+
intervalType: EventInterval;
|
|
117
|
+
intervalCount: number;
|
|
118
|
+
restriction: RestrictionTimes;
|
|
119
|
+
timezone?: string | undefined;
|
|
120
|
+
start: Date;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function buildLayer(c: LayerConfig): LayerProps {
|
|
124
|
+
const rot: Recurring = new Recurring();
|
|
125
|
+
rot.intervalType = c.intervalType;
|
|
126
|
+
rot.intervalCount = new PositiveNumber(c.intervalCount);
|
|
127
|
+
return {
|
|
128
|
+
users: c.users.map(user),
|
|
129
|
+
startDateTimeOfLayer: c.start,
|
|
130
|
+
restrictionTimes: c.restriction,
|
|
131
|
+
handOffTime: Recurring.getNextDateInterval(c.start, rot),
|
|
132
|
+
rotation: rot,
|
|
133
|
+
timezone: c.timezone,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function expand(
|
|
138
|
+
layer: LayerProps,
|
|
139
|
+
start: Date,
|
|
140
|
+
end: Date,
|
|
141
|
+
util?: LayerUtil,
|
|
142
|
+
options?: { getNumberOfEvents?: number } | undefined,
|
|
143
|
+
): Array<CalendarEvent> {
|
|
144
|
+
const u: LayerUtil = util ?? new LayerUtil();
|
|
145
|
+
return u.getEvents(
|
|
146
|
+
{
|
|
147
|
+
...layer,
|
|
148
|
+
calendarStartDate: start,
|
|
149
|
+
calendarEndDate: end,
|
|
150
|
+
},
|
|
151
|
+
options,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Stable, comparable serialization of an event list.
|
|
156
|
+
function eventKey(events: Array<CalendarEvent>): string {
|
|
157
|
+
return events
|
|
158
|
+
.map((e: CalendarEvent): string => {
|
|
159
|
+
return `${e.title}@${OneUptimeDate.toString(e.start)}->${OneUptimeDate.toString(e.end)}`;
|
|
160
|
+
})
|
|
161
|
+
.join("|");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Deep clone with identical millisecond values but fully independent objects.
|
|
165
|
+
function cloneRestriction(r: RestrictionTimes): RestrictionTimes {
|
|
166
|
+
const c: RestrictionTimes = new RestrictionTimes();
|
|
167
|
+
c.restictionType = r.restictionType;
|
|
168
|
+
c.dayRestrictionTimes = r.dayRestrictionTimes
|
|
169
|
+
? {
|
|
170
|
+
startTime: new Date(r.dayRestrictionTimes.startTime.getTime()),
|
|
171
|
+
endTime: new Date(r.dayRestrictionTimes.endTime.getTime()),
|
|
172
|
+
}
|
|
173
|
+
: null;
|
|
174
|
+
c.weeklyRestrictionTimes = r.weeklyRestrictionTimes.map(
|
|
175
|
+
(w: WeeklyResctriction): WeeklyResctriction => {
|
|
176
|
+
return {
|
|
177
|
+
startDay: w.startDay,
|
|
178
|
+
endDay: w.endDay,
|
|
179
|
+
startTime: new Date(w.startTime.getTime()),
|
|
180
|
+
endTime: new Date(w.endTime.getTime()),
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
);
|
|
184
|
+
return c;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ---- Daily snapshot / assertion --------------------------------------------
|
|
188
|
+
|
|
189
|
+
interface DailySnapshot {
|
|
190
|
+
startMs: number;
|
|
191
|
+
endMs: number;
|
|
192
|
+
startRef: Date;
|
|
193
|
+
endRef: Date;
|
|
194
|
+
objRef: StartAndEndTime;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function snapshotDaily(r: RestrictionTimes): DailySnapshot {
|
|
198
|
+
const d: StartAndEndTime | null = r.dayRestrictionTimes;
|
|
199
|
+
if (!d) {
|
|
200
|
+
throw new Error("expected a Daily restriction with dayRestrictionTimes");
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
startMs: d.startTime.getTime(),
|
|
204
|
+
endMs: d.endTime.getTime(),
|
|
205
|
+
startRef: d.startTime,
|
|
206
|
+
endRef: d.endTime,
|
|
207
|
+
objRef: d,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function assertDailyUnchanged(r: RestrictionTimes, snap: DailySnapshot): void {
|
|
212
|
+
const now: DailySnapshot = snapshotDaily(r);
|
|
213
|
+
// values (millisecond precision) must be identical.
|
|
214
|
+
expect(now.startMs).toBe(snap.startMs);
|
|
215
|
+
expect(now.endMs).toBe(snap.endMs);
|
|
216
|
+
// instances must be identical (no in-place mutation, no reassignment).
|
|
217
|
+
expect(now.startRef).toBe(snap.startRef);
|
|
218
|
+
expect(now.endRef).toBe(snap.endRef);
|
|
219
|
+
expect(now.objRef).toBe(snap.objRef);
|
|
220
|
+
// type preserved.
|
|
221
|
+
expect(r.restictionType).toBe(RestrictionType.Daily);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ---- Weekly snapshot / assertion -------------------------------------------
|
|
225
|
+
|
|
226
|
+
interface WeeklyEntrySnapshot {
|
|
227
|
+
startMs: number;
|
|
228
|
+
endMs: number;
|
|
229
|
+
startDay: DayOfWeek;
|
|
230
|
+
endDay: DayOfWeek;
|
|
231
|
+
startRef: Date;
|
|
232
|
+
endRef: Date;
|
|
233
|
+
ref: WeeklyResctriction;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
interface WeeklySnapshot {
|
|
237
|
+
length: number;
|
|
238
|
+
arrRef: Array<WeeklyResctriction>;
|
|
239
|
+
entries: WeeklyEntrySnapshot[];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function snapshotWeekly(r: RestrictionTimes): WeeklySnapshot {
|
|
243
|
+
const arr: Array<WeeklyResctriction> = r.weeklyRestrictionTimes;
|
|
244
|
+
return {
|
|
245
|
+
length: arr.length,
|
|
246
|
+
arrRef: arr,
|
|
247
|
+
entries: arr.map((w: WeeklyResctriction): WeeklyEntrySnapshot => {
|
|
248
|
+
return {
|
|
249
|
+
startMs: w.startTime.getTime(),
|
|
250
|
+
endMs: w.endTime.getTime(),
|
|
251
|
+
startDay: w.startDay,
|
|
252
|
+
endDay: w.endDay,
|
|
253
|
+
startRef: w.startTime,
|
|
254
|
+
endRef: w.endTime,
|
|
255
|
+
ref: w,
|
|
256
|
+
};
|
|
257
|
+
}),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function assertWeeklyUnchanged(
|
|
262
|
+
r: RestrictionTimes,
|
|
263
|
+
snap: WeeklySnapshot,
|
|
264
|
+
): void {
|
|
265
|
+
const now: WeeklySnapshot = snapshotWeekly(r);
|
|
266
|
+
expect(now.length).toBe(snap.length);
|
|
267
|
+
// the array instance itself must be unchanged.
|
|
268
|
+
expect(now.arrRef).toBe(snap.arrRef);
|
|
269
|
+
for (let i: number = 0; i < snap.entries.length; i++) {
|
|
270
|
+
const a: WeeklyEntrySnapshot = snap.entries[i]!;
|
|
271
|
+
const b: WeeklyEntrySnapshot = now.entries[i]!;
|
|
272
|
+
expect(b.startMs).toBe(a.startMs);
|
|
273
|
+
expect(b.endMs).toBe(a.endMs);
|
|
274
|
+
expect(b.startDay).toBe(a.startDay);
|
|
275
|
+
expect(b.endDay).toBe(a.endDay);
|
|
276
|
+
// instance identity of both the entry object and its Date fields.
|
|
277
|
+
expect(b.ref).toBe(a.ref);
|
|
278
|
+
expect(b.startRef).toBe(a.startRef);
|
|
279
|
+
expect(b.endRef).toBe(a.endRef);
|
|
280
|
+
}
|
|
281
|
+
expect(r.restictionType).toBe(RestrictionType.Weekly);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/*
|
|
285
|
+
* ---------------------------------------------------------------------------
|
|
286
|
+
* Shared fixtures.
|
|
287
|
+
* ---------------------------------------------------------------------------
|
|
288
|
+
*/
|
|
289
|
+
|
|
290
|
+
const MON_JAN6: Date = OneUptimeDate.fromString("2025-01-06T00:00:00.000Z");
|
|
291
|
+
const NY: string = "America/New_York";
|
|
292
|
+
const KOLKATA: string = "Asia/Kolkata";
|
|
293
|
+
|
|
294
|
+
function addDays(base: Date, n: number): Date {
|
|
295
|
+
return OneUptimeDate.addRemoveDays(base, n);
|
|
296
|
+
}
|
|
297
|
+
function addHours(base: Date, n: number): Date {
|
|
298
|
+
return OneUptimeDate.addRemoveHours(base, n);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
interface Window {
|
|
302
|
+
name: string;
|
|
303
|
+
start: Date;
|
|
304
|
+
end: Date;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/*
|
|
308
|
+
* A spread of windows on DIFFERENT calendar days / seasons so that any in-place
|
|
309
|
+
* day-moving mutation would be exposed by a later window seeing a corrupted base.
|
|
310
|
+
*/
|
|
311
|
+
const WINDOWS: Window[] = [
|
|
312
|
+
{ name: "jan-10d", start: MON_JAN6, end: addDays(MON_JAN6, 10) },
|
|
313
|
+
{
|
|
314
|
+
name: "spring-10d",
|
|
315
|
+
start: addDays(MON_JAN6, 90),
|
|
316
|
+
end: addDays(MON_JAN6, 100),
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: "summer-10d",
|
|
320
|
+
start: addDays(MON_JAN6, 180),
|
|
321
|
+
end: addDays(MON_JAN6, 190),
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "midday-start",
|
|
325
|
+
start: addHours(addDays(MON_JAN6, 1), 14),
|
|
326
|
+
end: addDays(MON_JAN6, 6),
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: "single-day",
|
|
330
|
+
start: addDays(MON_JAN6, 3),
|
|
331
|
+
end: addDays(MON_JAN6, 4),
|
|
332
|
+
},
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
interface IntervalSpec {
|
|
336
|
+
name: string;
|
|
337
|
+
type: EventInterval;
|
|
338
|
+
count: number;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
interface DailySpec {
|
|
342
|
+
name: string;
|
|
343
|
+
sh: number;
|
|
344
|
+
sm: number;
|
|
345
|
+
eh: number;
|
|
346
|
+
em: number;
|
|
347
|
+
}
|
|
348
|
+
const DAILY_SPECS: DailySpec[] = [
|
|
349
|
+
{ name: "09:00-17:00", sh: 9, sm: 0, eh: 17, em: 0 },
|
|
350
|
+
{ name: "22:00-06:00(overnight)", sh: 22, sm: 0, eh: 6, em: 0 },
|
|
351
|
+
{ name: "00:00-01:00", sh: 0, sm: 0, eh: 1, em: 0 },
|
|
352
|
+
{ name: "08:30-17:45", sh: 8, sm: 30, eh: 17, em: 45 },
|
|
353
|
+
];
|
|
354
|
+
|
|
355
|
+
const TIMEZONES: (string | undefined)[] = [undefined, NY, KOLKATA];
|
|
356
|
+
|
|
357
|
+
/*
|
|
358
|
+
* ---------------------------------------------------------------------------
|
|
359
|
+
* (1) DAILY non-mutation across many windows.
|
|
360
|
+
* ---------------------------------------------------------------------------
|
|
361
|
+
*/
|
|
362
|
+
|
|
363
|
+
describe("(1) Daily: getEvents never mutates the shared RestrictionTimes", () => {
|
|
364
|
+
/*
|
|
365
|
+
* day-x1 (same-day-period trim) and week-x1 (multi-day-period trim) cover both
|
|
366
|
+
* shapes of the daily-restriction trim path; block (2) exercises day-x2.
|
|
367
|
+
*/
|
|
368
|
+
const dailyHammerIntervals: IntervalSpec[] = [
|
|
369
|
+
{ name: "day-x1", type: EventInterval.Day, count: 1 },
|
|
370
|
+
{ name: "week-x1", type: EventInterval.Week, count: 1 },
|
|
371
|
+
];
|
|
372
|
+
for (const spec of DAILY_SPECS) {
|
|
373
|
+
for (const tz of TIMEZONES) {
|
|
374
|
+
for (const iv of dailyHammerIntervals) {
|
|
375
|
+
const label: string = `daily ${spec.name} tz=${tz ?? "local"} ${iv.name}`;
|
|
376
|
+
it(`${label}: dayRestrictionTimes intact after hammering all windows`, () => {
|
|
377
|
+
// Build ONE restriction and share it across every getEvents call.
|
|
378
|
+
const restriction: RestrictionTimes = dailyRestriction(
|
|
379
|
+
spec.sh,
|
|
380
|
+
spec.sm,
|
|
381
|
+
spec.eh,
|
|
382
|
+
spec.em,
|
|
383
|
+
);
|
|
384
|
+
const snap: DailySnapshot = snapshotDaily(restriction);
|
|
385
|
+
|
|
386
|
+
for (const w of WINDOWS) {
|
|
387
|
+
const layer: LayerProps = buildLayer({
|
|
388
|
+
users: ["A", "B", "C"],
|
|
389
|
+
intervalType: iv.type,
|
|
390
|
+
intervalCount: iv.count,
|
|
391
|
+
restriction,
|
|
392
|
+
timezone: tz,
|
|
393
|
+
start: MON_JAN6,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// full expansion.
|
|
397
|
+
expand(layer, w.start, w.end);
|
|
398
|
+
// live "who is on call now/next" windowed resolution.
|
|
399
|
+
expand(layer, w.start, addDays(w.start, 20), new LayerUtil(), {
|
|
400
|
+
getNumberOfEvents: 1,
|
|
401
|
+
});
|
|
402
|
+
// assert intact after EACH window (localizes any leak).
|
|
403
|
+
assertDailyUnchanged(restriction, snap);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// final belt-and-braces check.
|
|
407
|
+
assertDailyUnchanged(restriction, snap);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
it("reusing ONE LayerUtil instance across many daily-window calls leaves the restriction intact", () => {
|
|
414
|
+
const restriction: RestrictionTimes = dailyRestriction(9, 0, 17, 0);
|
|
415
|
+
const snap: DailySnapshot = snapshotDaily(restriction);
|
|
416
|
+
const util: LayerUtil = new LayerUtil();
|
|
417
|
+
const layer: LayerProps = buildLayer({
|
|
418
|
+
users: ["A", "B"],
|
|
419
|
+
intervalType: EventInterval.Day,
|
|
420
|
+
intervalCount: 1,
|
|
421
|
+
restriction,
|
|
422
|
+
timezone: NY,
|
|
423
|
+
start: MON_JAN6,
|
|
424
|
+
});
|
|
425
|
+
for (const w of WINDOWS) {
|
|
426
|
+
expand(layer, w.start, w.end, util);
|
|
427
|
+
assertDailyUnchanged(restriction, snap);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
/*
|
|
433
|
+
* ---------------------------------------------------------------------------
|
|
434
|
+
* (2) DAILY independence: idempotency + agreement with a pristine clone.
|
|
435
|
+
* ---------------------------------------------------------------------------
|
|
436
|
+
*/
|
|
437
|
+
|
|
438
|
+
describe("(2) Daily: shared instance across calls is idempotent and correct", () => {
|
|
439
|
+
const idempotencySpecs: DailySpec[] = [
|
|
440
|
+
{ name: "09:00-17:00", sh: 9, sm: 0, eh: 17, em: 0 },
|
|
441
|
+
{ name: "22:00-06:00(overnight)", sh: 22, sm: 0, eh: 6, em: 0 },
|
|
442
|
+
];
|
|
443
|
+
const idempotencyIntervals: IntervalSpec[] = [
|
|
444
|
+
{ name: "day-x1", type: EventInterval.Day, count: 1 },
|
|
445
|
+
{ name: "day-x2", type: EventInterval.Day, count: 2 },
|
|
446
|
+
];
|
|
447
|
+
|
|
448
|
+
const windowA: Window = {
|
|
449
|
+
name: "A",
|
|
450
|
+
start: MON_JAN6,
|
|
451
|
+
end: addDays(MON_JAN6, 12),
|
|
452
|
+
};
|
|
453
|
+
const windowB: Window = {
|
|
454
|
+
name: "B",
|
|
455
|
+
start: addDays(MON_JAN6, 120),
|
|
456
|
+
end: addDays(MON_JAN6, 160),
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
for (const spec of idempotencySpecs) {
|
|
460
|
+
for (const tz of [undefined, NY]) {
|
|
461
|
+
for (const iv of idempotencyIntervals) {
|
|
462
|
+
const label: string = `daily ${spec.name} tz=${tz ?? "local"} ${iv.name}`;
|
|
463
|
+
|
|
464
|
+
it(`${label}: window A -> B -> A reproduces A exactly (no cross-call corruption)`, () => {
|
|
465
|
+
const restriction: RestrictionTimes = dailyRestriction(
|
|
466
|
+
spec.sh,
|
|
467
|
+
spec.sm,
|
|
468
|
+
spec.eh,
|
|
469
|
+
spec.em,
|
|
470
|
+
);
|
|
471
|
+
const layer: LayerProps = buildLayer({
|
|
472
|
+
users: ["A", "B", "C"],
|
|
473
|
+
intervalType: iv.type,
|
|
474
|
+
intervalCount: iv.count,
|
|
475
|
+
restriction,
|
|
476
|
+
timezone: tz,
|
|
477
|
+
start: MON_JAN6,
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
const a1: string = eventKey(
|
|
481
|
+
expand(layer, windowA.start, windowA.end),
|
|
482
|
+
);
|
|
483
|
+
const b1: string = eventKey(
|
|
484
|
+
expand(layer, windowB.start, windowB.end),
|
|
485
|
+
);
|
|
486
|
+
const a2: string = eventKey(
|
|
487
|
+
expand(layer, windowA.start, windowA.end),
|
|
488
|
+
);
|
|
489
|
+
const b2: string = eventKey(
|
|
490
|
+
expand(layer, windowB.start, windowB.end),
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
// A is unaffected by the intervening B call, and vice-versa.
|
|
494
|
+
expect(a2).toBe(a1);
|
|
495
|
+
expect(b2).toBe(b1);
|
|
496
|
+
// A and B are genuinely different windows (sanity: the fixture bites).
|
|
497
|
+
expect(a1).not.toBe(b1);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it(`${label}: shared instance agrees with a pristine independent clone`, () => {
|
|
501
|
+
const restriction: RestrictionTimes = dailyRestriction(
|
|
502
|
+
spec.sh,
|
|
503
|
+
spec.sm,
|
|
504
|
+
spec.eh,
|
|
505
|
+
spec.em,
|
|
506
|
+
);
|
|
507
|
+
const layer: LayerProps = buildLayer({
|
|
508
|
+
users: ["A", "B", "C"],
|
|
509
|
+
intervalType: iv.type,
|
|
510
|
+
intervalCount: iv.count,
|
|
511
|
+
restriction,
|
|
512
|
+
timezone: tz,
|
|
513
|
+
start: MON_JAN6,
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Hammer the shared instance with an unrelated window first.
|
|
517
|
+
expand(layer, windowB.start, windowB.end);
|
|
518
|
+
const shared: string = eventKey(
|
|
519
|
+
expand(layer, windowA.start, windowA.end),
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
// A clone with identical values that was NEVER passed through getEvents.
|
|
523
|
+
const cloneLayer: LayerProps = buildLayer({
|
|
524
|
+
users: ["A", "B", "C"],
|
|
525
|
+
intervalType: iv.type,
|
|
526
|
+
intervalCount: iv.count,
|
|
527
|
+
restriction: cloneRestriction(restriction),
|
|
528
|
+
timezone: tz,
|
|
529
|
+
start: MON_JAN6,
|
|
530
|
+
});
|
|
531
|
+
const pristine: string = eventKey(
|
|
532
|
+
expand(cloneLayer, windowA.start, windowA.end),
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
expect(shared).toBe(pristine);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it(`${label}: same result whether expanded on a fresh or a reused LayerUtil`, () => {
|
|
539
|
+
const restriction: RestrictionTimes = dailyRestriction(
|
|
540
|
+
spec.sh,
|
|
541
|
+
spec.sm,
|
|
542
|
+
spec.eh,
|
|
543
|
+
spec.em,
|
|
544
|
+
);
|
|
545
|
+
const layer: LayerProps = buildLayer({
|
|
546
|
+
users: ["A", "B", "C"],
|
|
547
|
+
intervalType: iv.type,
|
|
548
|
+
intervalCount: iv.count,
|
|
549
|
+
restriction,
|
|
550
|
+
timezone: tz,
|
|
551
|
+
start: MON_JAN6,
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
const freshUtil: string = eventKey(
|
|
555
|
+
expand(layer, windowA.start, windowA.end, new LayerUtil()),
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
const reused: LayerUtil = new LayerUtil();
|
|
559
|
+
// prime the instance with a different window/mode first.
|
|
560
|
+
expand(layer, windowB.start, windowB.end, reused, {
|
|
561
|
+
getNumberOfEvents: 1,
|
|
562
|
+
});
|
|
563
|
+
const reusedUtil: string = eventKey(
|
|
564
|
+
expand(layer, windowA.start, windowA.end, reused),
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
expect(reusedUtil).toBe(freshUtil);
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
/*
|
|
575
|
+
* ---------------------------------------------------------------------------
|
|
576
|
+
* (3) WEEKLY non-mutation + independence.
|
|
577
|
+
* ---------------------------------------------------------------------------
|
|
578
|
+
*/
|
|
579
|
+
|
|
580
|
+
interface WeeklyConfig {
|
|
581
|
+
name: string;
|
|
582
|
+
entries: WeeklyEntrySpec[];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Concrete anchors in Jan 2025 (UTC): Fri=Jan3/Jan10, Mon=Jan6, Wed=Jan8.
|
|
586
|
+
const WEEKLY_CONFIGS: WeeklyConfig[] = [
|
|
587
|
+
{
|
|
588
|
+
name: "mon-fri-working-hours(non-wrap)",
|
|
589
|
+
entries: [
|
|
590
|
+
{
|
|
591
|
+
startDay: DayOfWeek.Monday,
|
|
592
|
+
endDay: DayOfWeek.Friday,
|
|
593
|
+
startTimeIso: "2025-01-06T09:00:00.000Z",
|
|
594
|
+
endTimeIso: "2025-01-10T17:00:00.000Z",
|
|
595
|
+
},
|
|
596
|
+
],
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
name: "weekend-wrap(fri-20-to-mon-08)",
|
|
600
|
+
entries: [
|
|
601
|
+
{
|
|
602
|
+
startDay: DayOfWeek.Friday,
|
|
603
|
+
endDay: DayOfWeek.Monday,
|
|
604
|
+
startTimeIso: "2025-01-03T20:00:00.000Z",
|
|
605
|
+
endTimeIso: "2025-01-06T08:00:00.000Z",
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
name: "two-entry(mon-wed + thu-fri)",
|
|
611
|
+
entries: [
|
|
612
|
+
{
|
|
613
|
+
startDay: DayOfWeek.Monday,
|
|
614
|
+
endDay: DayOfWeek.Wednesday,
|
|
615
|
+
startTimeIso: "2025-01-06T08:00:00.000Z",
|
|
616
|
+
endTimeIso: "2025-01-08T18:00:00.000Z",
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
startDay: DayOfWeek.Thursday,
|
|
620
|
+
endDay: DayOfWeek.Friday,
|
|
621
|
+
startTimeIso: "2025-01-09T08:00:00.000Z",
|
|
622
|
+
endTimeIso: "2025-01-10T18:00:00.000Z",
|
|
623
|
+
},
|
|
624
|
+
],
|
|
625
|
+
},
|
|
626
|
+
];
|
|
627
|
+
|
|
628
|
+
describe("(3) Weekly: getEvents never mutates the shared RestrictionTimes", () => {
|
|
629
|
+
for (const cfg of WEEKLY_CONFIGS) {
|
|
630
|
+
for (const tz of [undefined, NY]) {
|
|
631
|
+
const label: string = `weekly ${cfg.name} tz=${tz ?? "local"}`;
|
|
632
|
+
it(`${label}: weeklyRestrictionTimes intact after hammering all windows`, () => {
|
|
633
|
+
const restriction: RestrictionTimes = weeklyRestriction(cfg.entries);
|
|
634
|
+
const snap: WeeklySnapshot = snapshotWeekly(restriction);
|
|
635
|
+
|
|
636
|
+
/*
|
|
637
|
+
* Day and Week rotations exercise both the daily-tiling and weekly-span
|
|
638
|
+
* trim paths; that is enough to expose any weekly-restriction mutation.
|
|
639
|
+
*/
|
|
640
|
+
const weeklyIntervals: IntervalSpec[] = [
|
|
641
|
+
{ name: "day-x1", type: EventInterval.Day, count: 1 },
|
|
642
|
+
{ name: "week-x1", type: EventInterval.Week, count: 1 },
|
|
643
|
+
];
|
|
644
|
+
for (const iv of weeklyIntervals) {
|
|
645
|
+
const layer: LayerProps = buildLayer({
|
|
646
|
+
users: ["A", "B"],
|
|
647
|
+
intervalType: iv.type,
|
|
648
|
+
intervalCount: iv.count,
|
|
649
|
+
restriction,
|
|
650
|
+
timezone: tz,
|
|
651
|
+
start: MON_JAN6,
|
|
652
|
+
});
|
|
653
|
+
for (const w of WINDOWS) {
|
|
654
|
+
expand(layer, w.start, w.end);
|
|
655
|
+
expand(layer, w.start, addDays(w.start, 20), new LayerUtil(), {
|
|
656
|
+
getNumberOfEvents: 1,
|
|
657
|
+
});
|
|
658
|
+
assertWeeklyUnchanged(restriction, snap);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
assertWeeklyUnchanged(restriction, snap);
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
describe("(3) Weekly: shared instance across calls is idempotent and correct", () => {
|
|
669
|
+
const windowA: Window = {
|
|
670
|
+
name: "A",
|
|
671
|
+
start: MON_JAN6,
|
|
672
|
+
end: addDays(MON_JAN6, 21),
|
|
673
|
+
};
|
|
674
|
+
const windowB: Window = {
|
|
675
|
+
name: "B",
|
|
676
|
+
start: addDays(MON_JAN6, 140),
|
|
677
|
+
end: addDays(MON_JAN6, 175),
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
for (const cfg of WEEKLY_CONFIGS) {
|
|
681
|
+
for (const tz of [undefined, NY]) {
|
|
682
|
+
const label: string = `weekly ${cfg.name} tz=${tz ?? "local"}`;
|
|
683
|
+
|
|
684
|
+
it(`${label}: window A -> B -> A reproduces A exactly`, () => {
|
|
685
|
+
const restriction: RestrictionTimes = weeklyRestriction(cfg.entries);
|
|
686
|
+
const layer: LayerProps = buildLayer({
|
|
687
|
+
users: ["A", "B", "C"],
|
|
688
|
+
intervalType: EventInterval.Week,
|
|
689
|
+
intervalCount: 1,
|
|
690
|
+
restriction,
|
|
691
|
+
timezone: tz,
|
|
692
|
+
start: MON_JAN6,
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
const a1: string = eventKey(expand(layer, windowA.start, windowA.end));
|
|
696
|
+
expand(layer, windowB.start, windowB.end);
|
|
697
|
+
const a2: string = eventKey(expand(layer, windowA.start, windowA.end));
|
|
698
|
+
expect(a2).toBe(a1);
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
it(`${label}: shared instance agrees with a pristine independent clone`, () => {
|
|
702
|
+
const restriction: RestrictionTimes = weeklyRestriction(cfg.entries);
|
|
703
|
+
const layer: LayerProps = buildLayer({
|
|
704
|
+
users: ["A", "B", "C"],
|
|
705
|
+
intervalType: EventInterval.Day,
|
|
706
|
+
intervalCount: 1,
|
|
707
|
+
restriction,
|
|
708
|
+
timezone: tz,
|
|
709
|
+
start: MON_JAN6,
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
expand(layer, windowB.start, windowB.end);
|
|
713
|
+
const shared: string = eventKey(
|
|
714
|
+
expand(layer, windowA.start, windowA.end),
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
const cloneLayer: LayerProps = buildLayer({
|
|
718
|
+
users: ["A", "B", "C"],
|
|
719
|
+
intervalType: EventInterval.Day,
|
|
720
|
+
intervalCount: 1,
|
|
721
|
+
restriction: cloneRestriction(restriction),
|
|
722
|
+
timezone: tz,
|
|
723
|
+
start: MON_JAN6,
|
|
724
|
+
});
|
|
725
|
+
const pristine: string = eventKey(
|
|
726
|
+
expand(cloneLayer, windowA.start, windowA.end),
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
expect(shared).toBe(pristine);
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
/*
|
|
736
|
+
* ---------------------------------------------------------------------------
|
|
737
|
+
* (4) LAYER INDEPENDENCE: unrestricted vs restricted, sharing nothing.
|
|
738
|
+
* ---------------------------------------------------------------------------
|
|
739
|
+
*/
|
|
740
|
+
|
|
741
|
+
describe("(4) Independence: unrestricted and restricted layers resolve independently", () => {
|
|
742
|
+
const window: Window = {
|
|
743
|
+
name: "indep",
|
|
744
|
+
start: MON_JAN6,
|
|
745
|
+
end: addDays(MON_JAN6, 14),
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
it("interleaving restricted (tz) and unrestricted (no tz) calls on ONE LayerUtil never leaks", () => {
|
|
749
|
+
const unrestricted: RestrictionTimes = noRestriction();
|
|
750
|
+
const restricted: RestrictionTimes = dailyRestriction(9, 0, 17, 0);
|
|
751
|
+
const restrictedSnap: DailySnapshot = snapshotDaily(restricted);
|
|
752
|
+
|
|
753
|
+
const uLayer: LayerProps = buildLayer({
|
|
754
|
+
users: ["U1", "U2"],
|
|
755
|
+
intervalType: EventInterval.Day,
|
|
756
|
+
intervalCount: 1,
|
|
757
|
+
restriction: unrestricted,
|
|
758
|
+
timezone: undefined,
|
|
759
|
+
start: MON_JAN6,
|
|
760
|
+
});
|
|
761
|
+
const rLayer: LayerProps = buildLayer({
|
|
762
|
+
users: ["R1", "R2"],
|
|
763
|
+
intervalType: EventInterval.Day,
|
|
764
|
+
intervalCount: 1,
|
|
765
|
+
restriction: restricted,
|
|
766
|
+
timezone: NY,
|
|
767
|
+
start: MON_JAN6,
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
const util: LayerUtil = new LayerUtil();
|
|
771
|
+
|
|
772
|
+
// Baselines on fresh utils.
|
|
773
|
+
const uBaseline: string = eventKey(
|
|
774
|
+
expand(uLayer, window.start, window.end, new LayerUtil()),
|
|
775
|
+
);
|
|
776
|
+
const rBaseline: string = eventKey(
|
|
777
|
+
expand(rLayer, window.start, window.end, new LayerUtil()),
|
|
778
|
+
);
|
|
779
|
+
|
|
780
|
+
// Interleave on the SHARED util: R(tz) then U(no-tz) then R then U ...
|
|
781
|
+
const uAfterR: string = ((): string => {
|
|
782
|
+
expand(rLayer, window.start, window.end, util); // sets util.timezone = NY
|
|
783
|
+
return eventKey(expand(uLayer, window.start, window.end, util)); // must reset to undefined
|
|
784
|
+
})();
|
|
785
|
+
const rAfterU: string = ((): string => {
|
|
786
|
+
expand(uLayer, window.start, window.end, util); // sets util.timezone = undefined
|
|
787
|
+
return eventKey(expand(rLayer, window.start, window.end, util)); // must reset to NY
|
|
788
|
+
})();
|
|
789
|
+
|
|
790
|
+
// No cross-call timezone leak: each layer matches its own fresh-util baseline.
|
|
791
|
+
expect(uAfterR).toBe(uBaseline);
|
|
792
|
+
expect(rAfterU).toBe(rBaseline);
|
|
793
|
+
|
|
794
|
+
// The two layers are genuinely different rosters.
|
|
795
|
+
expect(uBaseline).not.toBe(rBaseline);
|
|
796
|
+
|
|
797
|
+
// The unrestricted restriction is still None with null day times.
|
|
798
|
+
expect(unrestricted.restictionType).toBe(RestrictionType.None);
|
|
799
|
+
expect(unrestricted.dayRestrictionTimes).toBeNull();
|
|
800
|
+
|
|
801
|
+
// The restricted restriction is byte-for-byte intact.
|
|
802
|
+
assertDailyUnchanged(restricted, restrictedSnap);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it("getMultiLayerEvents([restricted, unrestricted]) is idempotent and mutates neither restriction", () => {
|
|
806
|
+
const restricted: RestrictionTimes = dailyRestriction(9, 0, 17, 0);
|
|
807
|
+
const unrestricted: RestrictionTimes = noRestriction();
|
|
808
|
+
const restrictedSnap: DailySnapshot = snapshotDaily(restricted);
|
|
809
|
+
|
|
810
|
+
const rLayer: LayerProps = buildLayer({
|
|
811
|
+
users: ["R1", "R2"],
|
|
812
|
+
intervalType: EventInterval.Day,
|
|
813
|
+
intervalCount: 1,
|
|
814
|
+
restriction: restricted,
|
|
815
|
+
timezone: undefined,
|
|
816
|
+
start: MON_JAN6,
|
|
817
|
+
});
|
|
818
|
+
const uLayer: LayerProps = buildLayer({
|
|
819
|
+
users: ["U1", "U2"],
|
|
820
|
+
intervalType: EventInterval.Week,
|
|
821
|
+
intervalCount: 1,
|
|
822
|
+
restriction: unrestricted,
|
|
823
|
+
timezone: undefined,
|
|
824
|
+
start: MON_JAN6,
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
const util: LayerUtil = new LayerUtil();
|
|
828
|
+
const merged1: Array<CalendarEvent> = util.getMultiLayerEvents({
|
|
829
|
+
layers: [rLayer, uLayer],
|
|
830
|
+
calendarStartDate: window.start,
|
|
831
|
+
calendarEndDate: window.end,
|
|
832
|
+
});
|
|
833
|
+
const merged2: Array<CalendarEvent> = util.getMultiLayerEvents({
|
|
834
|
+
layers: [rLayer, uLayer],
|
|
835
|
+
calendarStartDate: window.start,
|
|
836
|
+
calendarEndDate: window.end,
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
// Merged output is stable across calls (no corruption of either restriction).
|
|
840
|
+
expect(eventKey(merged2)).toBe(eventKey(merged1));
|
|
841
|
+
// The higher-priority restricted layer produced at least some coverage.
|
|
842
|
+
expect(merged1.length).toBeGreaterThan(0);
|
|
843
|
+
|
|
844
|
+
// Neither restriction object was touched.
|
|
845
|
+
assertDailyUnchanged(restricted, restrictedSnap);
|
|
846
|
+
expect(unrestricted.restictionType).toBe(RestrictionType.None);
|
|
847
|
+
expect(unrestricted.dayRestrictionTimes).toBeNull();
|
|
848
|
+
expect(unrestricted.weeklyRestrictionTimes).toEqual([]);
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
it("resolving a restricted layer does not perturb an unrelated unrestricted layer's roster", () => {
|
|
852
|
+
const restricted: RestrictionTimes = dailyRestriction(22, 0, 6, 0);
|
|
853
|
+
const unrestricted: RestrictionTimes = noRestriction();
|
|
854
|
+
|
|
855
|
+
const uLayer: LayerProps = buildLayer({
|
|
856
|
+
users: ["U1", "U2", "U3"],
|
|
857
|
+
intervalType: EventInterval.Day,
|
|
858
|
+
intervalCount: 1,
|
|
859
|
+
restriction: unrestricted,
|
|
860
|
+
timezone: undefined,
|
|
861
|
+
start: MON_JAN6,
|
|
862
|
+
});
|
|
863
|
+
const rLayer: LayerProps = buildLayer({
|
|
864
|
+
users: ["R1", "R2", "R3"],
|
|
865
|
+
intervalType: EventInterval.Hour,
|
|
866
|
+
intervalCount: 6,
|
|
867
|
+
restriction: restricted,
|
|
868
|
+
timezone: KOLKATA,
|
|
869
|
+
start: MON_JAN6,
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
const uBefore: string = eventKey(expand(uLayer, window.start, window.end));
|
|
873
|
+
// Do a bunch of restricted resolutions in between.
|
|
874
|
+
for (const w of WINDOWS.slice(0, 3)) {
|
|
875
|
+
expand(rLayer, w.start, w.end);
|
|
876
|
+
expand(rLayer, w.start, addDays(w.start, 12), new LayerUtil(), {
|
|
877
|
+
getNumberOfEvents: 1,
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
const uAfter: string = eventKey(expand(uLayer, window.start, window.end));
|
|
881
|
+
|
|
882
|
+
expect(uAfter).toBe(uBefore);
|
|
883
|
+
// unrestricted 24/7 rotation must have full coverage (non-empty).
|
|
884
|
+
expect(uAfter.length).toBeGreaterThan(0);
|
|
885
|
+
});
|
|
886
|
+
});
|