@oneuptime/common 11.3.26 → 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/IncidentEpisodeRoleMember.ts +1 -1
- package/Models/DatabaseModels/IncidentMember.ts +1 -1
- package/Models/DatabaseModels/IncomingCallPolicy.ts +13 -0
- package/Models/DatabaseModels/Index.ts +3 -0
- 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/AlertAPI.ts +1 -1
- package/Server/API/IncidentAPI.ts +2 -2
- package/Server/API/IncidentEpisodeAPI.ts +1 -1
- package/Server/API/LlmProviderAPI.ts +1 -0
- package/Server/API/ScheduledMaintenanceAPI.ts +1 -1
- 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 +8 -0
- package/Server/Infrastructure/PostgresDatabase.ts +11 -0
- package/Server/Services/AIService.ts +74 -2
- package/Server/Services/AnalyticsDatabaseService.ts +10 -1
- package/Server/Services/DatabaseService.ts +10 -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/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/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/Database/QueryUtil.ts +57 -0
- package/Server/Utils/AI/AlertAIContextBuilder.ts +6 -1
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +14 -2
- 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/IncidentTools.ts +183 -1
- package/Server/Utils/AI/Toolbox/Index.ts +4 -1
- package/Server/Utils/AI/Toolbox/RecentChangesTools.ts +208 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +42 -0
- package/Server/Utils/Database/MigrationFailureLog.ts +240 -0
- package/Server/Utils/IncomingCallPhoneNumber.ts +41 -0
- package/Server/Utils/LLM/LLMService.ts +110 -11
- package/Server/Utils/Telemetry/IoTSnapshotScan.ts +12 -9
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +12 -9
- 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/LLMServiceToolCalling.test.ts +78 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +68 -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/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/Date.ts +200 -15
- 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/ModelFormModal/ModelFormModal.tsx +8 -0
- package/UI/Components/RadioButtons/BasicRadioButtons.tsx +1 -1
- 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 +2 -0
- package/build/dist/Models/DatabaseModels/Index.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/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 +1 -0
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
- 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 +8 -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/AIService.js +53 -2
- 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 +10 -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/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/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/Database/QueryUtil.js +45 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- 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 +13 -2
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- 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/IncidentTools.js +152 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +4 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- 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 +40 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
- 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 +81 -5
- 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/Types/Date.js +153 -15
- package/build/dist/Types/Date.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/ModelFormModal/ModelFormModal.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/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logger from "../Utils/Logger";
|
|
2
2
|
import DatabaseDataSourceOptions from "./Postgres/DataSourceOptions";
|
|
3
|
+
import { recordSchemaMigrationFailureBestEffort } from "../Utils/Database/MigrationFailureLog";
|
|
3
4
|
import Sleep from "../../Types/Sleep";
|
|
4
5
|
import { DataSource, DataSourceOptions, QueryRunner } from "typeorm";
|
|
5
6
|
import { createDatabase, dropDatabase } from "typeorm-extension";
|
|
@@ -95,6 +96,16 @@ export default class Database {
|
|
|
95
96
|
} catch (err) {
|
|
96
97
|
logger.error("Postgres Database Connection Failed");
|
|
97
98
|
logger.error(err);
|
|
99
|
+
|
|
100
|
+
/*
|
|
101
|
+
* When this process runs schema migrations on boot (migrationsRun=true),
|
|
102
|
+
* connect() also fails if a migration threw. Record which migration
|
|
103
|
+
* failed and why — on a throwaway connection, since the DataSource above
|
|
104
|
+
* is unusable — so the admin health page can explain the pending schema.
|
|
105
|
+
* Best-effort and self-contained: it never throws and never masks `err`.
|
|
106
|
+
*/
|
|
107
|
+
await recordSchemaMigrationFailureBestEffort(dataSourceOptions, err);
|
|
108
|
+
|
|
98
109
|
throw err;
|
|
99
110
|
}
|
|
100
111
|
}
|
|
@@ -11,7 +11,10 @@ import LLMService, {
|
|
|
11
11
|
LLMMessage,
|
|
12
12
|
LLMToolCall,
|
|
13
13
|
LLMToolDefinition,
|
|
14
|
+
LLMUsage,
|
|
14
15
|
} from "../Utils/LLM/LLMService";
|
|
16
|
+
import LlmType from "../../Types/LLM/LlmType";
|
|
17
|
+
import { Span, trace } from "@opentelemetry/api";
|
|
15
18
|
import LlmProvider from "../../Models/DatabaseModels/LlmProvider";
|
|
16
19
|
import LlmLog from "../../Models/DatabaseModels/LlmLog";
|
|
17
20
|
import LlmLogStatus from "../../Types/LlmLogStatus";
|
|
@@ -129,9 +132,17 @@ export class Service extends BaseService {
|
|
|
129
132
|
logEntry.aiRunId = request.aiRunId;
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
|
|
135
|
+
/*
|
|
136
|
+
* Check if billing should apply. Only bill for the global (OneUptime-hosted)
|
|
137
|
+
* provider, and only when it actually has a per-token cost. A free global
|
|
138
|
+
* provider (costPerMillionTokensInUSDCents = 0, the default) consumes no
|
|
139
|
+
* balance, so it must not require or block on one either — otherwise a $0
|
|
140
|
+
* provider would still fail with "Insufficient AI balance".
|
|
141
|
+
*/
|
|
133
142
|
const shouldBill: boolean =
|
|
134
|
-
IsBillingEnabled &&
|
|
143
|
+
IsBillingEnabled &&
|
|
144
|
+
(llmProvider.isGlobalLlm || false) &&
|
|
145
|
+
(llmProvider.costPerMillionTokensInUSDCents || 0) > 0;
|
|
135
146
|
|
|
136
147
|
// Check balance if billing enabled and using global provider
|
|
137
148
|
if (shouldBill) {
|
|
@@ -234,6 +245,18 @@ export class Service extends BaseService {
|
|
|
234
245
|
}
|
|
235
246
|
}
|
|
236
247
|
|
|
248
|
+
/*
|
|
249
|
+
* Emit gen_ai.* semantic-convention attributes on the active span so
|
|
250
|
+
* OneUptime's own AI usage is a first-class LLM span in OneUptime's own
|
|
251
|
+
* telemetry (dogfooding — LlmSpanUtil detects these). Never fails the call.
|
|
252
|
+
*/
|
|
253
|
+
this.setGenAiSpanAttributes({
|
|
254
|
+
llmType: llmProvider.llmType,
|
|
255
|
+
modelName: llmConfig.modelName,
|
|
256
|
+
usage: response.usage,
|
|
257
|
+
costInUSDCents: logEntry.costInUSDCents,
|
|
258
|
+
});
|
|
259
|
+
|
|
237
260
|
// Save log entry
|
|
238
261
|
const savedLog: LlmLog = await LlmLogService.create({
|
|
239
262
|
data: logEntry,
|
|
@@ -261,6 +284,55 @@ export class Service extends BaseService {
|
|
|
261
284
|
throw error;
|
|
262
285
|
}
|
|
263
286
|
}
|
|
287
|
+
|
|
288
|
+
/*
|
|
289
|
+
* Set gen_ai.* attributes (OpenTelemetry GenAI semantic conventions) on the
|
|
290
|
+
* currently-active span. The @CaptureSpan()-wrapped caller owns that span, so
|
|
291
|
+
* LlmSpanUtil recognizes these calls as first-class LLM spans.
|
|
292
|
+
*/
|
|
293
|
+
private setGenAiSpanAttributes(data: {
|
|
294
|
+
llmType: LlmType;
|
|
295
|
+
modelName?: string | undefined;
|
|
296
|
+
usage?: LLMUsage | undefined;
|
|
297
|
+
costInUSDCents?: number | undefined;
|
|
298
|
+
}): void {
|
|
299
|
+
try {
|
|
300
|
+
const span: Span | undefined = trace.getActiveSpan();
|
|
301
|
+
if (!span) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
span.setAttribute("gen_ai.system", data.llmType.toString());
|
|
306
|
+
span.setAttribute("gen_ai.provider.name", data.llmType.toString());
|
|
307
|
+
span.setAttribute("gen_ai.operation.name", "chat");
|
|
308
|
+
|
|
309
|
+
if (data.modelName) {
|
|
310
|
+
span.setAttribute("gen_ai.request.model", data.modelName);
|
|
311
|
+
span.setAttribute("gen_ai.response.model", data.modelName);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (data.usage) {
|
|
315
|
+
span.setAttribute(
|
|
316
|
+
"gen_ai.usage.input_tokens",
|
|
317
|
+
data.usage.promptTokens || 0,
|
|
318
|
+
);
|
|
319
|
+
span.setAttribute(
|
|
320
|
+
"gen_ai.usage.output_tokens",
|
|
321
|
+
data.usage.completionTokens || 0,
|
|
322
|
+
);
|
|
323
|
+
span.setAttribute(
|
|
324
|
+
"gen_ai.usage.total_tokens",
|
|
325
|
+
data.usage.totalTokens || 0,
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (data.costInUSDCents) {
|
|
330
|
+
span.setAttribute("gen_ai.usage.cost_usd", data.costInUSDCents / 100);
|
|
331
|
+
}
|
|
332
|
+
} catch {
|
|
333
|
+
// Telemetry must never fail the LLM call.
|
|
334
|
+
}
|
|
335
|
+
}
|
|
264
336
|
}
|
|
265
337
|
|
|
266
338
|
export default new Service();
|
|
@@ -1899,7 +1899,16 @@ export default class AnalyticsDatabaseService<
|
|
|
1899
1899
|
return data;
|
|
1900
1900
|
}
|
|
1901
1901
|
|
|
1902
|
-
|
|
1902
|
+
/*
|
|
1903
|
+
* Rethrow hook for the catch blocks below. MUST stay synchronous and
|
|
1904
|
+
* `never`-returning: the call sites use `throw this.getException(error)`,
|
|
1905
|
+
* so if this were `async` it would return a Promise that `throw` then
|
|
1906
|
+
* throws verbatim (never awaited) — the real exception is lost, callers up
|
|
1907
|
+
* the stack catch a bare Promise (surfacing as "[object Promise]"), and the
|
|
1908
|
+
* un-awaited rejection becomes an unhandled rejection. Throwing directly
|
|
1909
|
+
* propagates the original exception on the synchronous throw path.
|
|
1910
|
+
*/
|
|
1911
|
+
protected getException(error: Exception): never {
|
|
1903
1912
|
throw error;
|
|
1904
1913
|
}
|
|
1905
1914
|
|
|
@@ -443,7 +443,16 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
443
443
|
return Promise.resolve(error);
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
-
|
|
446
|
+
/*
|
|
447
|
+
* Rethrow hook for the catch blocks below. MUST stay synchronous and
|
|
448
|
+
* `never`-returning: the call sites use `throw this.getException(error)`,
|
|
449
|
+
* so if this were `async` it would return a Promise that `throw` then
|
|
450
|
+
* throws verbatim (never awaited) — the real exception is lost, callers up
|
|
451
|
+
* the stack catch a bare Promise (surfacing as "[object Promise]"), and the
|
|
452
|
+
* un-awaited rejection becomes an unhandled rejection. Throwing directly
|
|
453
|
+
* propagates the original exception on the synchronous throw path.
|
|
454
|
+
*/
|
|
455
|
+
protected getException(error: Exception): never {
|
|
447
456
|
throw error;
|
|
448
457
|
}
|
|
449
458
|
|
|
@@ -3479,7 +3479,7 @@ ${incidentSeverity.name}
|
|
|
3479
3479
|
const response: LLMCompletionResponse = await LLMService.getCompletion({
|
|
3480
3480
|
llmProviderConfig: llmConfig,
|
|
3481
3481
|
messages: aiContext.messages,
|
|
3482
|
-
temperature: 0.
|
|
3482
|
+
temperature: 0.2,
|
|
3483
3483
|
});
|
|
3484
3484
|
|
|
3485
3485
|
return response.content;
|
|
@@ -135,6 +135,79 @@ export class Service extends DatabaseService<IncomingCallPolicyEscalationRule> {
|
|
|
135
135
|
protected override async onBeforeUpdate(
|
|
136
136
|
updateBy: UpdateBy<IncomingCallPolicyEscalationRule>,
|
|
137
137
|
): Promise<OnUpdate<IncomingCallPolicyEscalationRule>> {
|
|
138
|
+
/*
|
|
139
|
+
* Enforce user/schedule mutual exclusivity on update (parity with onBeforeCreate).
|
|
140
|
+
* Only runs when the update actually touches one of the routing-target fields, so
|
|
141
|
+
* internal updates (status/order via isRoot) are unaffected.
|
|
142
|
+
*/
|
|
143
|
+
const data: UpdateBy<IncomingCallPolicyEscalationRule>["data"] =
|
|
144
|
+
updateBy.data;
|
|
145
|
+
const isTouchingTarget: boolean =
|
|
146
|
+
data.userId !== undefined ||
|
|
147
|
+
data.onCallDutyPolicyScheduleId !== undefined;
|
|
148
|
+
|
|
149
|
+
if (isTouchingTarget && updateBy.query._id) {
|
|
150
|
+
const settingUser: boolean = Boolean(data.userId);
|
|
151
|
+
const settingSchedule: boolean = Boolean(data.onCallDutyPolicyScheduleId);
|
|
152
|
+
|
|
153
|
+
if (settingUser && settingSchedule) {
|
|
154
|
+
throw new BadDataException(
|
|
155
|
+
"Only one of User or On-Call Schedule can be specified, not both",
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Setting one target clears the other so a rule can never hold both.
|
|
160
|
+
const nullableData: {
|
|
161
|
+
userId?: ObjectID | null;
|
|
162
|
+
onCallDutyPolicyScheduleId?: ObjectID | null;
|
|
163
|
+
} = data as {
|
|
164
|
+
userId?: ObjectID | null;
|
|
165
|
+
onCallDutyPolicyScheduleId?: ObjectID | null;
|
|
166
|
+
};
|
|
167
|
+
if (settingUser) {
|
|
168
|
+
nullableData.onCallDutyPolicyScheduleId = null;
|
|
169
|
+
}
|
|
170
|
+
if (settingSchedule) {
|
|
171
|
+
nullableData.userId = null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const existing: IncomingCallPolicyEscalationRule | null =
|
|
175
|
+
await this.findOneBy({
|
|
176
|
+
query: {
|
|
177
|
+
_id: updateBy.query._id!,
|
|
178
|
+
},
|
|
179
|
+
select: {
|
|
180
|
+
userId: true,
|
|
181
|
+
onCallDutyPolicyScheduleId: true,
|
|
182
|
+
},
|
|
183
|
+
props: {
|
|
184
|
+
isRoot: true,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
/*
|
|
189
|
+
* Whether each target will be present AFTER this update, accounting for
|
|
190
|
+
* fields left untouched (keep existing) and the opposite-field clearing
|
|
191
|
+
* done above.
|
|
192
|
+
*/
|
|
193
|
+
const willHaveUser: boolean = settingUser
|
|
194
|
+
? true
|
|
195
|
+
: data.userId === undefined
|
|
196
|
+
? Boolean(existing?.userId)
|
|
197
|
+
: false;
|
|
198
|
+
const willHaveSchedule: boolean = settingSchedule
|
|
199
|
+
? true
|
|
200
|
+
: data.onCallDutyPolicyScheduleId === undefined
|
|
201
|
+
? Boolean(existing?.onCallDutyPolicyScheduleId)
|
|
202
|
+
: false;
|
|
203
|
+
|
|
204
|
+
if (!willHaveUser && !willHaveSchedule) {
|
|
205
|
+
throw new BadDataException(
|
|
206
|
+
"Either a User or an On-Call Schedule must be specified for the escalation rule",
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
138
211
|
if (updateBy.data.order && !updateBy.props.isRoot && updateBy.query._id) {
|
|
139
212
|
const resource: IncomingCallPolicyEscalationRule | null =
|
|
140
213
|
await this.findOneBy({
|
|
@@ -194,8 +267,8 @@ export class Service extends DatabaseService<IncomingCallPolicyEscalationRule> {
|
|
|
194
267
|
if (newOrder > currentOrder) {
|
|
195
268
|
// moving down.
|
|
196
269
|
for (const resource of resources) {
|
|
197
|
-
if (resource.order! <= newOrder) {
|
|
198
|
-
//
|
|
270
|
+
if (resource.order! > currentOrder && resource.order! <= newOrder) {
|
|
271
|
+
// decrement order to fill the gap left by the moved rule.
|
|
199
272
|
await this.updateOneBy({
|
|
200
273
|
query: {
|
|
201
274
|
_id: resource._id!,
|
|
@@ -2,7 +2,10 @@ import DatabaseService from "./DatabaseService";
|
|
|
2
2
|
import IncomingCallPolicy from "../../Models/DatabaseModels/IncomingCallPolicy";
|
|
3
3
|
import IncomingCallPolicyLabelRuleEngineService from "./IncomingCallPolicyLabelRuleEngineService";
|
|
4
4
|
import IncomingCallPolicyOwnerRuleEngineService from "./IncomingCallPolicyOwnerRuleEngineService";
|
|
5
|
-
import { OnCreate } from "../Types/Database/Hooks";
|
|
5
|
+
import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
|
6
|
+
import DeleteBy from "../Types/Database/DeleteBy";
|
|
7
|
+
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
8
|
+
import releaseIncomingCallPhoneNumber from "../Utils/IncomingCallPhoneNumber";
|
|
6
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
7
10
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
8
11
|
|
|
@@ -11,6 +14,44 @@ export class Service extends DatabaseService<IncomingCallPolicy> {
|
|
|
11
14
|
super(IncomingCallPolicy);
|
|
12
15
|
}
|
|
13
16
|
|
|
17
|
+
@CaptureSpan()
|
|
18
|
+
protected override async onBeforeDelete(
|
|
19
|
+
deleteBy: DeleteBy<IncomingCallPolicy>,
|
|
20
|
+
): Promise<OnDelete<IncomingCallPolicy>> {
|
|
21
|
+
/*
|
|
22
|
+
* Release any provisioned numbers before the policy rows are removed so we
|
|
23
|
+
* don't leave paid, orphaned numbers on the provider that can never be
|
|
24
|
+
* released again (the policy — and its SID — would be gone).
|
|
25
|
+
*/
|
|
26
|
+
const policies: Array<IncomingCallPolicy> = await this.findBy({
|
|
27
|
+
query: deleteBy.query,
|
|
28
|
+
select: {
|
|
29
|
+
_id: true,
|
|
30
|
+
callProviderPhoneNumberId: true,
|
|
31
|
+
projectCallSMSConfigId: true,
|
|
32
|
+
},
|
|
33
|
+
limit: LIMIT_MAX,
|
|
34
|
+
skip: 0,
|
|
35
|
+
props: {
|
|
36
|
+
isRoot: true,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
for (const policy of policies) {
|
|
41
|
+
if (policy.callProviderPhoneNumberId && policy.projectCallSMSConfigId) {
|
|
42
|
+
await releaseIncomingCallPhoneNumber({
|
|
43
|
+
projectCallSMSConfigId: policy.projectCallSMSConfigId,
|
|
44
|
+
callProviderPhoneNumberId: policy.callProviderPhoneNumberId,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
deleteBy,
|
|
51
|
+
carryForward: null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
14
55
|
@CaptureSpan()
|
|
15
56
|
protected override async onCreateSuccess(
|
|
16
57
|
_onCreate: OnCreate<IncomingCallPolicy>,
|
package/Server/Services/Index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import BillingService from "./BillingService";
|
|
|
12
12
|
import CallLogService from "./CallLogService";
|
|
13
13
|
import CallService from "./CallService";
|
|
14
14
|
import DataMigrationService from "./DataMigrationService";
|
|
15
|
+
import MigrationFailureService from "./MigrationFailureService";
|
|
15
16
|
import DomainService from "./DomainService";
|
|
16
17
|
import EmailLogService from "./EmailLogService";
|
|
17
18
|
import EmailVerificationTokenService from "./EmailVerificationTokenService";
|
|
@@ -273,6 +274,7 @@ const services: Array<BaseService> = [
|
|
|
273
274
|
CallService,
|
|
274
275
|
|
|
275
276
|
DataMigrationService,
|
|
277
|
+
MigrationFailureService,
|
|
276
278
|
DomainService,
|
|
277
279
|
|
|
278
280
|
EmailLogService,
|
|
@@ -310,8 +310,14 @@ export class Service extends DatabaseService<MonitorProbe> {
|
|
|
310
310
|
_onCreate: OnCreate<MonitorProbe>,
|
|
311
311
|
createdItem: MonitorProbe,
|
|
312
312
|
): Promise<MonitorProbe> {
|
|
313
|
-
|
|
314
|
-
|
|
313
|
+
/*
|
|
314
|
+
* Refresh only the monitor this probe was just added to. Do NOT call
|
|
315
|
+
* refreshProbeStatus(probeId) here: a global/shared probe can be attached
|
|
316
|
+
* to thousands of monitors, and refreshing every one of them on a single
|
|
317
|
+
* create storms the database and can 500 the request.
|
|
318
|
+
*/
|
|
319
|
+
if (createdItem.monitorId) {
|
|
320
|
+
await this.refreshMonitorStatusSafely(createdItem.monitorId);
|
|
315
321
|
}
|
|
316
322
|
|
|
317
323
|
return Promise.resolve(createdItem);
|
|
@@ -321,7 +327,7 @@ export class Service extends DatabaseService<MonitorProbe> {
|
|
|
321
327
|
onUpdate: OnUpdate<MonitorProbe>,
|
|
322
328
|
updatedItemIds: ObjectID[],
|
|
323
329
|
): Promise<OnUpdate<MonitorProbe>> {
|
|
324
|
-
// if isEnabled is updated, refresh the
|
|
330
|
+
// if isEnabled is updated, refresh the status of the affected monitors.
|
|
325
331
|
if (onUpdate.updateBy.data.isEnabled !== undefined) {
|
|
326
332
|
const monitorProbes: Array<MonitorProbe> = await this.findBy({
|
|
327
333
|
query: {
|
|
@@ -329,8 +335,6 @@ export class Service extends DatabaseService<MonitorProbe> {
|
|
|
329
335
|
},
|
|
330
336
|
select: {
|
|
331
337
|
monitorId: true,
|
|
332
|
-
probeId: true,
|
|
333
|
-
nextPingAt: true,
|
|
334
338
|
},
|
|
335
339
|
limit: LIMIT_PER_PROJECT,
|
|
336
340
|
skip: 0,
|
|
@@ -340,16 +344,38 @@ export class Service extends DatabaseService<MonitorProbe> {
|
|
|
340
344
|
});
|
|
341
345
|
|
|
342
346
|
for (const monitorProbe of monitorProbes) {
|
|
343
|
-
if (!monitorProbe.
|
|
347
|
+
if (!monitorProbe.monitorId) {
|
|
344
348
|
continue;
|
|
345
349
|
}
|
|
346
350
|
|
|
347
|
-
|
|
351
|
+
/*
|
|
352
|
+
* Refresh only the monitor whose probe row changed. Do NOT call
|
|
353
|
+
* refreshProbeStatus(probeId) here: a global/shared probe can be
|
|
354
|
+
* attached to thousands of monitors, and refreshing all of them on a
|
|
355
|
+
* single enable/disable toggle storms the database and 500s the
|
|
356
|
+
* request (the row itself is already saved by this point).
|
|
357
|
+
*/
|
|
358
|
+
await this.refreshMonitorStatusSafely(monitorProbe.monitorId);
|
|
348
359
|
}
|
|
349
360
|
}
|
|
350
361
|
|
|
351
362
|
return onUpdate;
|
|
352
363
|
}
|
|
364
|
+
|
|
365
|
+
/*
|
|
366
|
+
* Refresh a single monitor's aggregate probe status without letting a
|
|
367
|
+
* failure bubble up. These refreshes run in post-commit hooks
|
|
368
|
+
* (onCreateSuccess / onUpdateSuccess) after the MonitorProbe row is already
|
|
369
|
+
* persisted, so a status-refresh error must not turn a successful save into
|
|
370
|
+
* a 500 for the user — log it instead.
|
|
371
|
+
*/
|
|
372
|
+
private async refreshMonitorStatusSafely(monitorId: ObjectID): Promise<void> {
|
|
373
|
+
try {
|
|
374
|
+
await MonitorService.refreshMonitorProbeStatus(monitorId);
|
|
375
|
+
} catch (err) {
|
|
376
|
+
logger.error(err);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
353
379
|
}
|
|
354
380
|
|
|
355
381
|
export default new Service();
|
|
@@ -1620,14 +1620,26 @@ ${createdItem.description?.trim() || "No description provided."}
|
|
|
1620
1620
|
|
|
1621
1621
|
/*
|
|
1622
1622
|
* Each monitor appears at most once for a given probeId (composite
|
|
1623
|
-
* unique on MonitorProbe), so concurrent refreshes operate on
|
|
1624
|
-
*
|
|
1623
|
+
* unique on MonitorProbe), so concurrent refreshes operate on disjoint
|
|
1624
|
+
* rows and are safe to run in parallel. A global/shared probe can be
|
|
1625
|
+
* attached to thousands of monitors, though, so refresh in bounded
|
|
1626
|
+
* batches instead of firing every refresh at once — an unbounded
|
|
1627
|
+
* Promise.all here would exhaust the database connection pool.
|
|
1625
1628
|
*/
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1629
|
+
const refreshConcurrency: number = 50;
|
|
1630
|
+
|
|
1631
|
+
for (let i: number = 0; i < monitorProbes.length; i += refreshConcurrency) {
|
|
1632
|
+
const batch: Array<MonitorProbe> = monitorProbes.slice(
|
|
1633
|
+
i,
|
|
1634
|
+
i + refreshConcurrency,
|
|
1635
|
+
);
|
|
1636
|
+
|
|
1637
|
+
await Promise.all(
|
|
1638
|
+
batch.map((monitorProbe: MonitorProbe) => {
|
|
1639
|
+
return this.refreshMonitorProbeStatus(monitorProbe.monitorId!);
|
|
1640
|
+
}),
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1631
1643
|
}
|
|
1632
1644
|
|
|
1633
1645
|
@CaptureSpan()
|
|
@@ -22,6 +22,9 @@ import PushNotificationMessage from "../../Types/PushNotification/PushNotificati
|
|
|
22
22
|
import PushNotificationUtil from "../Utils/PushNotificationUtil";
|
|
23
23
|
import { createWhatsAppMessageFromTemplate } from "../Utils/WhatsAppTemplateUtil";
|
|
24
24
|
import { WhatsAppMessagePayload } from "../../Types/WhatsApp/WhatsAppMessage";
|
|
25
|
+
import OnCallDutyPolicyTimeLogService from "./OnCallDutyPolicyTimeLogService";
|
|
26
|
+
import OneUptimeDate from "../../Types/Date";
|
|
27
|
+
import logger, { LogAttributes } from "../Utils/Logger";
|
|
25
28
|
|
|
26
29
|
export class Service extends DatabaseService<Model> {
|
|
27
30
|
public constructor() {
|
|
@@ -86,6 +89,39 @@ export class Service extends DatabaseService<Model> {
|
|
|
86
89
|
return createdItem;
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
/*
|
|
93
|
+
* Open the roster user's on-call time log for this schedule/rule/policy.
|
|
94
|
+
* Mirrors onDeleteSuccess's endTimeLogForUser (detaching a schedule closes
|
|
95
|
+
* the log, so attaching must open it). Without this, attaching a schedule
|
|
96
|
+
* whose roster user is ALREADY established never opened a log — the roster
|
|
97
|
+
* refresh only opens one when the current user CHANGES — so the whole stint
|
|
98
|
+
* went unrecorded in on-call reporting and the later detach's
|
|
99
|
+
* endTimeLogForUser was a no-op (audit M1). startTimeLogForUser is idempotent
|
|
100
|
+
* (open-log dedup), so a later natural handoff re-opening the same log is a
|
|
101
|
+
* no-op.
|
|
102
|
+
*/
|
|
103
|
+
if (
|
|
104
|
+
createdModel.onCallDutyPolicy?.id &&
|
|
105
|
+
createdModel.onCallDutyPolicyEscalationRule?.id &&
|
|
106
|
+
createdModel.projectId &&
|
|
107
|
+
createdModel.onCallDutyPolicyScheduleId
|
|
108
|
+
) {
|
|
109
|
+
OnCallDutyPolicyTimeLogService.startTimeLogForUser({
|
|
110
|
+
projectId: createdModel.projectId,
|
|
111
|
+
onCallDutyPolicyId: createdModel.onCallDutyPolicy.id,
|
|
112
|
+
onCallDutyPolicyEscalationRuleId:
|
|
113
|
+
createdModel.onCallDutyPolicyEscalationRule.id,
|
|
114
|
+
userId: userOnSchedule,
|
|
115
|
+
onCallDutyPolicyScheduleId: createdModel.onCallDutyPolicyScheduleId,
|
|
116
|
+
startsAt: OneUptimeDate.getCurrentDate(),
|
|
117
|
+
}).catch((error: Error) => {
|
|
118
|
+
logger.error(error, {
|
|
119
|
+
projectId: createdModel.projectId?.toString(),
|
|
120
|
+
userId: userOnSchedule?.toString(),
|
|
121
|
+
} as LogAttributes);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
89
125
|
const scheduleName: string =
|
|
90
126
|
createdModel.onCallDutyPolicySchedule?.name || "No name provided";
|
|
91
127
|
|
|
@@ -289,6 +325,35 @@ export class Service extends DatabaseService<Model> {
|
|
|
289
325
|
continue;
|
|
290
326
|
}
|
|
291
327
|
|
|
328
|
+
/*
|
|
329
|
+
* Close the roster user's open on-call time log for this schedule/rule/
|
|
330
|
+
* policy. Roster handoffs open a time log keyed by schedule + rule + policy
|
|
331
|
+
* (OnCallDutyPolicyScheduleService.startTimeLogForUser); the user-link and
|
|
332
|
+
* team-link delete handlers close theirs, but detaching a SCHEDULE from the
|
|
333
|
+
* rule left the log open forever, so the roster user kept showing as
|
|
334
|
+
* on-call in reporting (audit F18).
|
|
335
|
+
*/
|
|
336
|
+
if (
|
|
337
|
+
deletedItem.onCallDutyPolicy?.id &&
|
|
338
|
+
deletedItem.onCallDutyPolicyEscalationRule?.id &&
|
|
339
|
+
deletedItem.projectId
|
|
340
|
+
) {
|
|
341
|
+
OnCallDutyPolicyTimeLogService.endTimeLogForUser({
|
|
342
|
+
projectId: deletedItem.projectId,
|
|
343
|
+
onCallDutyPolicyId: deletedItem.onCallDutyPolicy.id,
|
|
344
|
+
onCallDutyPolicyEscalationRuleId:
|
|
345
|
+
deletedItem.onCallDutyPolicyEscalationRule.id,
|
|
346
|
+
userId: userOnSchedule,
|
|
347
|
+
onCallDutyPolicyScheduleId: deletedItem.onCallDutyPolicyScheduleId!,
|
|
348
|
+
endsAt: OneUptimeDate.getCurrentDate(),
|
|
349
|
+
}).catch((error: Error) => {
|
|
350
|
+
logger.error(error, {
|
|
351
|
+
projectId: deletedItem.projectId?.toString(),
|
|
352
|
+
userId: userOnSchedule?.toString(),
|
|
353
|
+
} as LogAttributes);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
292
357
|
const sendEmailToUserId: ObjectID | undefined | null = userOnSchedule;
|
|
293
358
|
|
|
294
359
|
if (!sendEmailToUserId) {
|