@oneuptime/common 11.3.20 → 11.3.22
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/AnalyticsModels/AnalyticsBaseModel/CommonModel.ts +2 -0
- package/Models/AnalyticsModels/ExceptionInstance.ts +1 -0
- package/Models/AnalyticsModels/Log.ts +1 -0
- package/Models/AnalyticsModels/Metric.ts +1 -0
- package/Models/AnalyticsModels/MonitorLog.ts +1 -0
- package/Models/AnalyticsModels/Span.ts +1 -0
- package/Models/DatabaseModels/Alert.ts +100 -0
- package/Models/DatabaseModels/AlertInternalNote.ts +2 -0
- package/Models/DatabaseModels/AlertReminderRule.ts +597 -0
- package/Models/DatabaseModels/AlertSeverity.ts +2 -0
- package/Models/DatabaseModels/AlertState.ts +2 -0
- package/Models/DatabaseModels/AlertStateTimeline.ts +2 -0
- package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +173 -0
- package/Models/DatabaseModels/GlobalConfig.ts +19 -0
- package/Models/DatabaseModels/Incident.ts +100 -0
- package/Models/DatabaseModels/IncidentInternalNote.ts +2 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +2 -0
- package/Models/DatabaseModels/IncidentReminderRule.ts +597 -0
- package/Models/DatabaseModels/IncidentSeverity.ts +2 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +2 -0
- package/Models/DatabaseModels/Index.ts +8 -0
- package/Models/DatabaseModels/Label.ts +2 -0
- package/Models/DatabaseModels/MonitorStatusTimeline.ts +2 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +100 -0
- package/Models/DatabaseModels/ScheduledMaintenanceReminderRule.ts +585 -0
- package/Models/DatabaseModels/ScheduledMaintenanceState.ts +2 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +2 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +2 -0
- package/Server/API/DashboardAPI.ts +221 -0
- package/Server/API/EnterpriseLicenseAPI.ts +279 -12
- package/Server/API/GlobalConfigAPI.ts +81 -16
- package/Server/API/StatusPageAPI.ts +894 -791
- package/Server/EnvironmentConfig.ts +9 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783234451106-AddIncidentAlertReminderRules.ts +211 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783320000000-AddScheduledMaintenanceReminderRules.ts +113 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783330000000-AddRemindWhileScheduledToScheduledMaintenanceReminderRule.ts +20 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783340000000-AddLabelsToIncidentAndAlertReminderRules.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Services/AlertReminderRuleService.ts +253 -0
- package/Server/Services/AlertService.ts +106 -0
- package/Server/Services/EnterpriseLicenseInstanceService.ts +10 -0
- package/Server/Services/IncidentReminderRuleService.ts +256 -0
- package/Server/Services/IncidentService.ts +143 -0
- package/Server/Services/Index.ts +8 -0
- package/Server/Services/IoTDeviceService.ts +99 -4
- package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +236 -0
- package/Server/Services/ScheduledMaintenanceService.ts +127 -0
- package/Server/Services/StatusPageService.ts +104 -45
- package/Server/Services/TelemetryExceptionService.ts +123 -0
- package/Server/Services/UserNotificationSettingService.ts +24 -0
- package/Server/Utils/AnalyticsDatabase/Statement.ts +7 -3
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +35 -0
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +6 -4
- package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +7 -6
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +6 -4
- package/Server/Utils/Telemetry/IoTSnapshotScan.ts +45 -7
- package/Server/Utils/WhatsAppTemplateUtil.ts +10 -0
- package/ServiceRoute.ts +4 -0
- package/Tests/Server/Services/IoTDeviceService.test.ts +280 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +49 -0
- package/Tests/Server/Utils/Monitor/Criteria/TelemetryStepMetricAlias.test.ts +390 -0
- package/Tests/Server/Utils/Telemetry/IoTSnapshotScan.test.ts +202 -0
- package/Tests/Utils/EnterpriseLicenseUsage.test.ts +165 -0
- package/Types/Email/EmailTemplateType.ts +3 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +18 -0
- package/Types/Monitor/MonitorStep.ts +32 -0
- package/Types/NotificationSetting/NotificationSettingEventType.ts +3 -0
- package/Types/Permission.ts +144 -0
- package/Types/Reminder/ReminderStopState.ts +6 -0
- package/Types/Reminder/ScheduledMaintenanceReminderStopState.ts +6 -0
- package/Types/WhatsApp/WhatsAppTemplates.ts +14 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +172 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +125 -0
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/CommonModel.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +1 -0
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +1 -0
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Metric.js +1 -0
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MonitorLog.js +1 -0
- package/build/dist/Models/AnalyticsModels/MonitorLog.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +1 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Alert.js +103 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertReminderRule.js +605 -0
- package/build/dist/Models/DatabaseModels/AlertReminderRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AlertSeverity.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertSeverity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertState.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertState.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +197 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +18 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +103 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentReminderRule.js +605 -0
- package/build/dist/Models/DatabaseModels/IncidentReminderRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentSeverity.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentSeverity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +8 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Label.js +2 -0
- package/build/dist/Models/DatabaseModels/Label.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +103 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceReminderRule.js +596 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceReminderRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceState.js +2 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceState.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +166 -16
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +203 -13
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +62 -15
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +74 -31
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +6 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783234451106-AddIncidentAlertReminderRules.js +78 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783234451106-AddIncidentAlertReminderRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783320000000-AddScheduledMaintenanceReminderRules.js +44 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783320000000-AddScheduledMaintenanceReminderRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783330000000-AddRemindWhileScheduledToScheduledMaintenanceReminderRule.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783330000000-AddRemindWhileScheduledToScheduledMaintenanceReminderRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783340000000-AddLabelsToIncidentAndAlertReminderRules.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783340000000-AddLabelsToIncidentAndAlertReminderRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertReminderRuleService.js +228 -0
- package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -0
- package/build/dist/Server/Services/AlertService.js +87 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js +9 -0
- package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js.map +1 -0
- package/build/dist/Server/Services/IncidentReminderRuleService.js +231 -0
- package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +116 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +8 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +66 -4
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +214 -0
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +91 -43
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +101 -0
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +6 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +4 -2
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +29 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +4 -5
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +6 -5
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -9
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js +33 -2
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/WhatsAppTemplateUtil.js +6 -0
- package/build/dist/Server/Utils/WhatsAppTemplateUtil.js.map +1 -1
- package/build/dist/ServiceRoute.js +1 -0
- package/build/dist/ServiceRoute.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +3 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.js +2 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorStep.js +25 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
- package/build/dist/Types/Permission.js +126 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Reminder/ReminderStopState.js +7 -0
- package/build/dist/Types/Reminder/ReminderStopState.js.map +1 -0
- package/build/dist/Types/Reminder/ScheduledMaintenanceReminderStopState.js +7 -0
- package/build/dist/Types/Reminder/ScheduledMaintenanceReminderStopState.js.map +1 -0
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js +9 -0
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +92 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +83 -0
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -0
- package/package.json +1 -1
|
@@ -8,6 +8,8 @@ import ScheduledMaintenanceOwnerTeamService from "./ScheduledMaintenanceOwnerTea
|
|
|
8
8
|
import ScheduledMaintenanceOwnerUserService from "./ScheduledMaintenanceOwnerUserService";
|
|
9
9
|
import ScheduledMaintenanceStateService from "./ScheduledMaintenanceStateService";
|
|
10
10
|
import ScheduledMaintenanceStateTimelineService from "./ScheduledMaintenanceStateTimelineService";
|
|
11
|
+
import ScheduledMaintenanceReminderRuleService from "./ScheduledMaintenanceReminderRuleService";
|
|
12
|
+
import ScheduledMaintenanceReminderRule from "../../Models/DatabaseModels/ScheduledMaintenanceReminderRule";
|
|
11
13
|
import TeamMemberService from "./TeamMemberService";
|
|
12
14
|
import URL from "../../Types/API/URL";
|
|
13
15
|
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
@@ -809,6 +811,80 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
809
811
|
return { createBy, carryForward: null };
|
|
810
812
|
}
|
|
811
813
|
|
|
814
|
+
@CaptureSpan()
|
|
815
|
+
public async refreshReminderSchedule(data: {
|
|
816
|
+
scheduledMaintenanceId: ObjectID;
|
|
817
|
+
projectId: ObjectID;
|
|
818
|
+
}): Promise<void> {
|
|
819
|
+
const scheduledMaintenance: Model | null = await this.findOneById({
|
|
820
|
+
id: data.scheduledMaintenanceId,
|
|
821
|
+
select: {
|
|
822
|
+
enableReminders: true,
|
|
823
|
+
startsAt: true,
|
|
824
|
+
labels: {
|
|
825
|
+
_id: true,
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
props: {
|
|
829
|
+
isRoot: true,
|
|
830
|
+
},
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
if (!scheduledMaintenance) {
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
let nextReminderNotificationAt: Date | null = null;
|
|
838
|
+
|
|
839
|
+
if (scheduledMaintenance.enableReminders !== false) {
|
|
840
|
+
const matchingRule: ScheduledMaintenanceReminderRule | null =
|
|
841
|
+
await ScheduledMaintenanceReminderRuleService.findMatchingRule({
|
|
842
|
+
projectId: data.projectId,
|
|
843
|
+
labelIds: scheduledMaintenance.labels?.map((label: Label) => {
|
|
844
|
+
return label.id!;
|
|
845
|
+
}),
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
if (
|
|
849
|
+
matchingRule &&
|
|
850
|
+
matchingRule.reminderIntervalInMinutes &&
|
|
851
|
+
!(await this.isScheduledMaintenanceCompleted({
|
|
852
|
+
scheduledMaintenanceId: data.scheduledMaintenanceId,
|
|
853
|
+
}))
|
|
854
|
+
) {
|
|
855
|
+
/*
|
|
856
|
+
* When the rule does not remind while the event is still scheduled,
|
|
857
|
+
* defer the first reminder until after the event has started so that
|
|
858
|
+
* owners are not notified about an event that has not begun yet.
|
|
859
|
+
*/
|
|
860
|
+
let referenceDate: Date = OneUptimeDate.getCurrentDate();
|
|
861
|
+
|
|
862
|
+
if (
|
|
863
|
+
!matchingRule.remindWhileScheduled &&
|
|
864
|
+
scheduledMaintenance.startsAt &&
|
|
865
|
+
OneUptimeDate.isInTheFuture(scheduledMaintenance.startsAt)
|
|
866
|
+
) {
|
|
867
|
+
referenceDate = scheduledMaintenance.startsAt;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
nextReminderNotificationAt = OneUptimeDate.addRemoveMinutes(
|
|
871
|
+
referenceDate,
|
|
872
|
+
matchingRule.reminderIntervalInMinutes,
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
await this.updateOneById({
|
|
878
|
+
id: data.scheduledMaintenanceId,
|
|
879
|
+
data: {
|
|
880
|
+
nextReminderNotificationAt: nextReminderNotificationAt,
|
|
881
|
+
},
|
|
882
|
+
props: {
|
|
883
|
+
isRoot: true,
|
|
884
|
+
},
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
|
|
812
888
|
@CaptureSpan()
|
|
813
889
|
protected override async onCreateSuccess(
|
|
814
890
|
onCreate: OnCreate<Model>,
|
|
@@ -985,6 +1061,26 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
|
|
|
985
1061
|
);
|
|
986
1062
|
}
|
|
987
1063
|
})
|
|
1064
|
+
.then(async () => {
|
|
1065
|
+
// Schedule reminder notifications for scheduled maintenance if a matching reminder rule exists
|
|
1066
|
+
try {
|
|
1067
|
+
if (createdItem.projectId && createdItem.id) {
|
|
1068
|
+
await this.refreshReminderSchedule({
|
|
1069
|
+
scheduledMaintenanceId: createdItem.id,
|
|
1070
|
+
projectId: createdItem.projectId,
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
logger.error(
|
|
1075
|
+
`Reminder scheduling failed in ScheduledMaintenanceService.onCreateSuccess: ${error}`,
|
|
1076
|
+
{
|
|
1077
|
+
projectId: createdItem.projectId?.toString(),
|
|
1078
|
+
scheduledMaintenanceId: createdItem.id?.toString(),
|
|
1079
|
+
userId: createdItem.createdByUserId?.toString(),
|
|
1080
|
+
} as LogAttributes,
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
})
|
|
988
1084
|
.catch((error: Error) => {
|
|
989
1085
|
logger.error(
|
|
990
1086
|
`Critical error in ScheduledMaintenanceService sequential operations: ${error}`,
|
|
@@ -1577,6 +1673,37 @@ ${labels
|
|
|
1577
1673
|
},
|
|
1578
1674
|
);
|
|
1579
1675
|
}
|
|
1676
|
+
|
|
1677
|
+
/*
|
|
1678
|
+
* Re-evaluate reminder schedule when reminders are enabled or disabled,
|
|
1679
|
+
* or when labels change (which may change the matching reminder rule).
|
|
1680
|
+
*/
|
|
1681
|
+
if (
|
|
1682
|
+
onUpdate.updateBy.props.tenantId &&
|
|
1683
|
+
(Object.prototype.hasOwnProperty.call(
|
|
1684
|
+
onUpdate.updateBy.data,
|
|
1685
|
+
"enableReminders",
|
|
1686
|
+
) ||
|
|
1687
|
+
Object.prototype.hasOwnProperty.call(
|
|
1688
|
+
onUpdate.updateBy.data,
|
|
1689
|
+
"labels",
|
|
1690
|
+
))
|
|
1691
|
+
) {
|
|
1692
|
+
try {
|
|
1693
|
+
await this.refreshReminderSchedule({
|
|
1694
|
+
scheduledMaintenanceId: scheduledMaintenanceId,
|
|
1695
|
+
projectId: onUpdate.updateBy.props.tenantId as ObjectID,
|
|
1696
|
+
});
|
|
1697
|
+
} catch (reminderError) {
|
|
1698
|
+
logger.error(
|
|
1699
|
+
`Reminder rescheduling failed in ScheduledMaintenanceService.onUpdateSuccess: ${reminderError}`,
|
|
1700
|
+
{
|
|
1701
|
+
projectId: onUpdate.updateBy.props.tenantId?.toString(),
|
|
1702
|
+
scheduledMaintenanceId: scheduledMaintenanceId?.toString(),
|
|
1703
|
+
} as LogAttributes,
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1580
1707
|
}
|
|
1581
1708
|
}
|
|
1582
1709
|
|
|
@@ -37,6 +37,7 @@ import User from "../../Models/DatabaseModels/User";
|
|
|
37
37
|
import {
|
|
38
38
|
AllowedStatusPageCountInFreePlan,
|
|
39
39
|
IsBillingEnabled,
|
|
40
|
+
LetsEncryptAccountKey,
|
|
40
41
|
} from "../EnvironmentConfig";
|
|
41
42
|
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
|
|
42
43
|
import Recurring from "../../Types/Events/Recurring";
|
|
@@ -96,9 +97,9 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
96
97
|
* Caches the resolved status page URL per statusPageId. `getStatusPageURL`
|
|
97
98
|
* is called inside per-subscriber notification loops (see
|
|
98
99
|
* `StatusPageSubscriberService`), where a single batch can fire N×
|
|
99
|
-
* Postgres lookups against `StatusPageDomain`.
|
|
100
|
-
*
|
|
101
|
-
* window is acceptable. Callers that need stronger consistency can call
|
|
100
|
+
* Postgres lookups against `StatusPageDomain`. Usable custom domains
|
|
101
|
+
* change rarely (provisioning/verification takes minutes), so a 60s
|
|
102
|
+
* staleness window is acceptable. Callers that need stronger consistency can call
|
|
102
103
|
* `clearStatusPageUrlCache` after writes to the underlying domain.
|
|
103
104
|
*/
|
|
104
105
|
private statusPageUrlCache: InMemoryTTLCache<string> = new InMemoryTTLCache(
|
|
@@ -679,32 +680,107 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
679
680
|
return monitorStatusTimelines;
|
|
680
681
|
}
|
|
681
682
|
|
|
683
|
+
/*
|
|
684
|
+
* Picks the custom domain to use when linking to this status page from
|
|
685
|
+
* subscriber notifications (email/SMS/Slack/Teams/webhooks) and SSO
|
|
686
|
+
* redirects. A domain is usable when any of these holds, in order of
|
|
687
|
+
* preference:
|
|
688
|
+
* 1. `isSslProvisioned` — OneUptime ordered the certificate and confirmed
|
|
689
|
+
* the domain serves HTTPS.
|
|
690
|
+
* 2. `isCustomCertificate` + `isCnameVerified` — the user uploaded their
|
|
691
|
+
* own certificate (the SSL provisioning jobs skip these domains, so
|
|
692
|
+
* `isSslProvisioned` never becomes true for them) and the CNAME check
|
|
693
|
+
* confirmed the domain actually routes to this instance. CNAME alone
|
|
694
|
+
* guards against certs uploaded before DNS is pointed.
|
|
695
|
+
* 3. `isCnameVerified`, but only when this instance has no Let's Encrypt
|
|
696
|
+
* account key — self-hosted installs that terminate TLS at their own
|
|
697
|
+
* proxy can never provision SSL, so CNAME verification is the
|
|
698
|
+
* strongest available signal that the domain routes to this instance.
|
|
699
|
+
* (With Let's Encrypt configured, a verified-but-unprovisioned domain
|
|
700
|
+
* is just mid-provisioning and will be picked up by rule 1 shortly.)
|
|
701
|
+
* Without rules 2 and 3, such status pages fall back to the installation
|
|
702
|
+
* URL in every subscriber notification
|
|
703
|
+
* (https://github.com/OneUptime/oneuptime/issues/1951).
|
|
704
|
+
*/
|
|
682
705
|
@CaptureSpan()
|
|
683
|
-
public async
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
const domain: StatusPageDomain | null =
|
|
691
|
-
await StatusPageDomainService.findOneBy({
|
|
706
|
+
public async getUsableCustomDomain(
|
|
707
|
+
statusPageId: ObjectID,
|
|
708
|
+
): Promise<StatusPageDomain | null> {
|
|
709
|
+
const domains: Array<StatusPageDomain> =
|
|
710
|
+
await StatusPageDomainService.findBy({
|
|
692
711
|
query: {
|
|
693
712
|
statusPageId: statusPageId,
|
|
694
|
-
isSslProvisioned: true,
|
|
695
713
|
},
|
|
696
714
|
select: {
|
|
697
715
|
fullDomain: true,
|
|
716
|
+
isSslProvisioned: true,
|
|
717
|
+
isCustomCertificate: true,
|
|
718
|
+
isCnameVerified: true,
|
|
698
719
|
},
|
|
720
|
+
skip: 0,
|
|
721
|
+
limit: LIMIT_PER_PROJECT,
|
|
699
722
|
props: {
|
|
700
723
|
isRoot: true,
|
|
701
724
|
ignoreHooks: true,
|
|
702
725
|
},
|
|
703
726
|
});
|
|
704
727
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
728
|
+
const domainsWithFullDomain: Array<StatusPageDomain> = domains.filter(
|
|
729
|
+
(domain: StatusPageDomain) => {
|
|
730
|
+
return Boolean(domain.fullDomain);
|
|
731
|
+
},
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
return (
|
|
735
|
+
domainsWithFullDomain.find((domain: StatusPageDomain) => {
|
|
736
|
+
return domain.isSslProvisioned;
|
|
737
|
+
}) ||
|
|
738
|
+
domainsWithFullDomain.find((domain: StatusPageDomain) => {
|
|
739
|
+
return domain.isCustomCertificate && domain.isCnameVerified;
|
|
740
|
+
}) ||
|
|
741
|
+
(!LetsEncryptAccountKey
|
|
742
|
+
? domainsWithFullDomain.find((domain: StatusPageDomain) => {
|
|
743
|
+
return domain.isCnameVerified;
|
|
744
|
+
})
|
|
745
|
+
: undefined) ||
|
|
746
|
+
null
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/*
|
|
751
|
+
* SSL-provisioned and custom-certificate domains are known to serve
|
|
752
|
+
* HTTPS. A CNAME-verified-only domain (self-hosted instance behind the
|
|
753
|
+
* user's own proxy) follows the instance protocol instead — CNAME
|
|
754
|
+
* verification can succeed over plain HTTP, so an HTTP-only install
|
|
755
|
+
* gets http:// links that actually work.
|
|
756
|
+
*/
|
|
757
|
+
private async getProtocolForCustomDomain(
|
|
758
|
+
domain: StatusPageDomain,
|
|
759
|
+
): Promise<Protocol> {
|
|
760
|
+
if (domain.isSslProvisioned || domain.isCustomCertificate) {
|
|
761
|
+
return Protocol.HTTPS;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return DatabaseConfig.getHttpProtocol();
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
@CaptureSpan()
|
|
768
|
+
public async getStatusPageURL(statusPageId: ObjectID): Promise<string> {
|
|
769
|
+
const cacheKey: string = statusPageId.toString();
|
|
770
|
+
const cached: string | undefined = this.statusPageUrlCache.get(cacheKey);
|
|
771
|
+
if (cached !== undefined) {
|
|
772
|
+
return cached;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
const domain: StatusPageDomain | null =
|
|
776
|
+
await this.getUsableCustomDomain(statusPageId);
|
|
777
|
+
|
|
778
|
+
let statusPageURL: string = "";
|
|
779
|
+
|
|
780
|
+
if (domain?.fullDomain) {
|
|
781
|
+
const protocol: Protocol = await this.getProtocolForCustomDomain(domain);
|
|
782
|
+
statusPageURL = `${protocol}${domain.fullDomain}`;
|
|
783
|
+
}
|
|
708
784
|
|
|
709
785
|
if (!statusPageURL) {
|
|
710
786
|
const host: Hostname = await DatabaseConfig.getHost();
|
|
@@ -724,39 +800,22 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
724
800
|
|
|
725
801
|
@CaptureSpan()
|
|
726
802
|
public async getStatusPageFirstURL(statusPageId: ObjectID): Promise<string> {
|
|
727
|
-
const
|
|
728
|
-
await
|
|
729
|
-
query: {
|
|
730
|
-
statusPageId: statusPageId,
|
|
731
|
-
isSslProvisioned: true,
|
|
732
|
-
},
|
|
733
|
-
select: {
|
|
734
|
-
fullDomain: true,
|
|
735
|
-
},
|
|
736
|
-
skip: 0,
|
|
737
|
-
limit: LIMIT_PER_PROJECT,
|
|
738
|
-
props: {
|
|
739
|
-
isRoot: true,
|
|
740
|
-
ignoreHooks: true,
|
|
741
|
-
},
|
|
742
|
-
});
|
|
743
|
-
|
|
744
|
-
let statusPageURL: string = "";
|
|
803
|
+
const domain: StatusPageDomain | null =
|
|
804
|
+
await this.getUsableCustomDomain(statusPageId);
|
|
745
805
|
|
|
746
|
-
if (
|
|
747
|
-
const
|
|
806
|
+
if (domain?.fullDomain) {
|
|
807
|
+
const protocol: Protocol = await this.getProtocolForCustomDomain(domain);
|
|
808
|
+
return `${protocol}${domain.fullDomain}`;
|
|
809
|
+
}
|
|
748
810
|
|
|
749
|
-
|
|
811
|
+
const host: Hostname = await DatabaseConfig.getHost();
|
|
750
812
|
|
|
751
|
-
|
|
752
|
-
statusPageURL = new URL(httpProtocol, host)
|
|
753
|
-
.addRoute("/status-page/" + statusPageId.toString())
|
|
754
|
-
.toString();
|
|
755
|
-
} else {
|
|
756
|
-
statusPageURL = domains[0]?.fullDomain || "";
|
|
757
|
-
}
|
|
813
|
+
const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
|
|
758
814
|
|
|
759
|
-
|
|
815
|
+
// 'https://local.oneuptime.com/status-page/40092fb5-cc33-4995-b532-b4e49c441c98'
|
|
816
|
+
return new URL(httpProtocol, host)
|
|
817
|
+
.addRoute("/status-page/" + statusPageId.toString())
|
|
818
|
+
.toString();
|
|
760
819
|
}
|
|
761
820
|
|
|
762
821
|
@CaptureSpan()
|
|
@@ -15,6 +15,17 @@ import AIAgentTaskTelemetryExceptionService from "./AIAgentTaskTelemetryExceptio
|
|
|
15
15
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
16
16
|
import ModelPermission from "../Types/Database/Permissions/Index";
|
|
17
17
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
18
|
+
import Query from "../Types/Database/Query";
|
|
19
|
+
import Includes from "../../Types/BaseDatabase/Includes";
|
|
20
|
+
import logger from "../Utils/Logger";
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* Hard cap on the fingerprint NOT IN list handed to the ClickHouse count
|
|
24
|
+
* query. ClickHouse receives query params via the request URI (default
|
|
25
|
+
* http_max_uri_size 1 MiB ≈ 14k sha256 fingerprints); 5k keeps a wide
|
|
26
|
+
* safety margin while covering all realistic projects.
|
|
27
|
+
*/
|
|
28
|
+
export const EXCLUDED_FINGERPRINT_LIMIT: number = 5000;
|
|
18
29
|
|
|
19
30
|
export interface CreateAIAgentTaskForExceptionParams {
|
|
20
31
|
telemetryExceptionId: ObjectID;
|
|
@@ -263,6 +274,118 @@ export class Service extends DatabaseService<Model> {
|
|
|
263
274
|
};
|
|
264
275
|
}
|
|
265
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Fingerprints of exception groups marked resolved and/or archived —
|
|
279
|
+
* used by the exception monitor to exclude occurrences of those groups
|
|
280
|
+
* from the exception count. Resolution state lives on this Postgres
|
|
281
|
+
* model per (projectId, primaryEntityId, fingerprint) group, while
|
|
282
|
+
* occurrences live in ClickHouse; fingerprints are the join key.
|
|
283
|
+
* Fingerprints hash projectId + primaryEntityId into the digest, so a
|
|
284
|
+
* fingerprint never collides across services and excluding by
|
|
285
|
+
* fingerprint alone is exact.
|
|
286
|
+
*
|
|
287
|
+
* The result is capped at EXCLUDED_FINGERPRINT_LIMIT, keeping the most
|
|
288
|
+
* recently seen groups: the ClickHouse client ships query params in the
|
|
289
|
+
* request URI, so an unbounded NOT IN list would blow past ClickHouse's
|
|
290
|
+
* http_max_uri_size (~1 MiB, roughly 14k fingerprints) and hard-fail the
|
|
291
|
+
* monitor's count query. Overflow groups simply stay counted (fail-open:
|
|
292
|
+
* better a stale incident than a silently swallowed one), and the
|
|
293
|
+
* lastSeenAt-descending order means what gets dropped is the groups
|
|
294
|
+
* least likely to have occurrences in the monitor window.
|
|
295
|
+
*
|
|
296
|
+
* Deliberately NO lastSeenAt lower bound: ExceptionInstance.time is the
|
|
297
|
+
* client-reported occurrence timestamp while lastSeenAt is server-side
|
|
298
|
+
* ingest time, so any window-derived cutoff breaks under client clock
|
|
299
|
+
* skew (and under monitor configs with no time window at all).
|
|
300
|
+
*/
|
|
301
|
+
@CaptureSpan()
|
|
302
|
+
public async getResolvedOrArchivedFingerprints(data: {
|
|
303
|
+
projectId: ObjectID;
|
|
304
|
+
telemetryServiceIds?: Array<ObjectID> | undefined;
|
|
305
|
+
resolved: boolean;
|
|
306
|
+
archived: boolean;
|
|
307
|
+
}): Promise<Array<string>> {
|
|
308
|
+
const statusFilters: Array<Query<Model>> = [];
|
|
309
|
+
|
|
310
|
+
if (data.resolved) {
|
|
311
|
+
statusFilters.push({ isResolved: true });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (data.archived) {
|
|
315
|
+
statusFilters.push({ isArchived: true });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (statusFilters.length === 0) {
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// resolved-OR-archived needs two indexed lookups — findBy has no OR.
|
|
323
|
+
const resultsPerStatus: Array<Array<Model>> = await Promise.all(
|
|
324
|
+
statusFilters.map(
|
|
325
|
+
async (statusFilter: Query<Model>): Promise<Array<Model>> => {
|
|
326
|
+
const query: Query<Model> = {
|
|
327
|
+
projectId: data.projectId,
|
|
328
|
+
...statusFilter,
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
if (data.telemetryServiceIds && data.telemetryServiceIds.length > 0) {
|
|
332
|
+
query.primaryEntityId = new Includes(data.telemetryServiceIds);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/*
|
|
336
|
+
* Per-status limit equals the final cap: the merged most-recent
|
|
337
|
+
* EXCLUDED_FINGERPRINT_LIMIT groups are necessarily within the
|
|
338
|
+
* top EXCLUDED_FINGERPRINT_LIMIT of their own status query.
|
|
339
|
+
*/
|
|
340
|
+
return this.findBy({
|
|
341
|
+
query: query,
|
|
342
|
+
select: {
|
|
343
|
+
fingerprint: true,
|
|
344
|
+
lastSeenAt: true,
|
|
345
|
+
},
|
|
346
|
+
sort: {
|
|
347
|
+
lastSeenAt: SortOrder.Descending,
|
|
348
|
+
},
|
|
349
|
+
limit: EXCLUDED_FINGERPRINT_LIMIT,
|
|
350
|
+
skip: 0,
|
|
351
|
+
props: {
|
|
352
|
+
isRoot: true,
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
},
|
|
356
|
+
),
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
// Merge the status lists most-recently-seen first, dedupe, then cap.
|
|
360
|
+
const merged: Array<Model> = resultsPerStatus
|
|
361
|
+
.flat()
|
|
362
|
+
.sort((a: Model, b: Model): number => {
|
|
363
|
+
return (b.lastSeenAt?.getTime() || 0) - (a.lastSeenAt?.getTime() || 0);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const fingerprints: Set<string> = new Set<string>();
|
|
367
|
+
let truncated: boolean = false;
|
|
368
|
+
|
|
369
|
+
for (const exception of merged) {
|
|
370
|
+
if (!exception.fingerprint) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (fingerprints.size >= EXCLUDED_FINGERPRINT_LIMIT) {
|
|
374
|
+
truncated = true;
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
fingerprints.add(exception.fingerprint);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (truncated) {
|
|
381
|
+
logger.warn(
|
|
382
|
+
`TelemetryException fingerprint exclusion list for project ${data.projectId.toString()} exceeds ${EXCLUDED_FINGERPRINT_LIMIT}; least recently seen resolved/archived exception groups will still be counted by exception monitors.`,
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return Array.from(fingerprints);
|
|
387
|
+
}
|
|
388
|
+
|
|
266
389
|
@CaptureSpan()
|
|
267
390
|
private async aggregateUnresolvedByService(
|
|
268
391
|
projectId: ObjectID,
|
|
@@ -580,6 +580,18 @@ export class Service extends DatabaseService<UserNotificationSetting> {
|
|
|
580
580
|
await this.addAlertNotificationSettings(userId, projectId);
|
|
581
581
|
await this.addAlertEpisodeNotificationSettings(userId, projectId);
|
|
582
582
|
await this.addIncidentEpisodeNotificationSettings(userId, projectId);
|
|
583
|
+
await this.addScheduledMaintenanceNotificationSettings(userId, projectId);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
private async addScheduledMaintenanceNotificationSettings(
|
|
587
|
+
userId: ObjectID,
|
|
588
|
+
projectId: ObjectID,
|
|
589
|
+
): Promise<void> {
|
|
590
|
+
await this.addNotificationSettingIfNotExists(
|
|
591
|
+
userId,
|
|
592
|
+
projectId,
|
|
593
|
+
NotificationSettingEventType.SEND_SCHEDULED_MAINTENANCE_REMINDER_OWNER_NOTIFICATION,
|
|
594
|
+
);
|
|
583
595
|
}
|
|
584
596
|
|
|
585
597
|
private async addProbeOwnerNotificationSettings(
|
|
@@ -614,6 +626,12 @@ export class Service extends DatabaseService<UserNotificationSetting> {
|
|
|
614
626
|
projectId,
|
|
615
627
|
NotificationSettingEventType.SEND_INCIDENT_STATE_CHANGED_OWNER_NOTIFICATION,
|
|
616
628
|
);
|
|
629
|
+
|
|
630
|
+
await this.addNotificationSettingIfNotExists(
|
|
631
|
+
userId,
|
|
632
|
+
projectId,
|
|
633
|
+
NotificationSettingEventType.SEND_INCIDENT_REMINDER_OWNER_NOTIFICATION,
|
|
634
|
+
);
|
|
617
635
|
}
|
|
618
636
|
|
|
619
637
|
private async addMonitorNotificationSettings(
|
|
@@ -689,6 +707,12 @@ export class Service extends DatabaseService<UserNotificationSetting> {
|
|
|
689
707
|
projectId,
|
|
690
708
|
NotificationSettingEventType.SEND_ALERT_STATE_CHANGED_OWNER_NOTIFICATION,
|
|
691
709
|
);
|
|
710
|
+
|
|
711
|
+
await this.addNotificationSettingIfNotExists(
|
|
712
|
+
userId,
|
|
713
|
+
projectId,
|
|
714
|
+
NotificationSettingEventType.SEND_ALERT_REMINDER_OWNER_NOTIFICATION,
|
|
715
|
+
);
|
|
692
716
|
}
|
|
693
717
|
|
|
694
718
|
private async addAlertEpisodeNotificationSettings(
|
|
@@ -5,6 +5,7 @@ import TableColumnType from "../../../Types/AnalyticsDatabase/TableColumnType";
|
|
|
5
5
|
import GreaterThan from "../../../Types/BaseDatabase/GreaterThan";
|
|
6
6
|
import GreaterThanOrEqual from "../../../Types/BaseDatabase/GreaterThanOrEqual";
|
|
7
7
|
import Includes from "../../../Types/BaseDatabase/Includes";
|
|
8
|
+
import IncludesNone from "../../../Types/BaseDatabase/IncludesNone";
|
|
8
9
|
import LessThan from "../../../Types/BaseDatabase/LessThan";
|
|
9
10
|
import LessThanOrEqual from "../../../Types/BaseDatabase/LessThanOrEqual";
|
|
10
11
|
import LessThanOrNull from "../../../Types/BaseDatabase/LessThanOrNull";
|
|
@@ -108,7 +109,7 @@ export class Statement implements BaseQueryParams {
|
|
|
108
109
|
v.value instanceof NotEqual
|
|
109
110
|
) {
|
|
110
111
|
finalValue = v.value.value;
|
|
111
|
-
} else if (v.value instanceof Includes) {
|
|
112
|
+
} else if (v.value instanceof Includes || v.value instanceof IncludesNone) {
|
|
112
113
|
if (
|
|
113
114
|
v.type === TableColumnType.Text ||
|
|
114
115
|
v.type === TableColumnType.ObjectID
|
|
@@ -198,9 +199,12 @@ export class Statement implements BaseQueryParams {
|
|
|
198
199
|
[TableColumnType.UInt64]: "UInt64",
|
|
199
200
|
};
|
|
200
201
|
|
|
201
|
-
if (
|
|
202
|
+
if (
|
|
203
|
+
(statementParam as StatementParameter).value instanceof Includes ||
|
|
204
|
+
(statementParam as StatementParameter).value instanceof IncludesNone
|
|
205
|
+
) {
|
|
202
206
|
const includesValues: Array<string | number | ObjectID> = (
|
|
203
|
-
(statementParam as StatementParameter).value as Includes
|
|
207
|
+
(statementParam as StatementParameter).value as Includes | IncludesNone
|
|
204
208
|
).values as Array<string | number | ObjectID>;
|
|
205
209
|
|
|
206
210
|
const isNumberArray: boolean = includesValues.every(
|
|
@@ -675,6 +675,41 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
|
|
|
675
675
|
type: tableColumn.type,
|
|
676
676
|
}}`,
|
|
677
677
|
);
|
|
678
|
+
} else if (
|
|
679
|
+
value instanceof IncludesNone &&
|
|
680
|
+
tableColumn.type === TableColumnType.ArrayText
|
|
681
|
+
) {
|
|
682
|
+
/*
|
|
683
|
+
* Array(String) exclusion — the negation of the Includes/hasAny
|
|
684
|
+
* branch above. An empty IncludesNone excludes nothing, so it
|
|
685
|
+
* drops to no predicate (mirrors the empty-Includes behavior).
|
|
686
|
+
*/
|
|
687
|
+
const arrayExcludeValues: Array<string> =
|
|
688
|
+
((value as IncludesNone).values as Array<string>) || [];
|
|
689
|
+
if (arrayExcludeValues.length > 0) {
|
|
690
|
+
whereStatement.append(
|
|
691
|
+
SQL`AND NOT hasAny(${key}, ${{
|
|
692
|
+
value: arrayExcludeValues,
|
|
693
|
+
type: TableColumnType.ArrayText,
|
|
694
|
+
}})`,
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
} else if (value instanceof IncludesNone) {
|
|
698
|
+
/*
|
|
699
|
+
* Scalar exclusion ("is none of"): `col NOT IN (v1, v2, ...)`.
|
|
700
|
+
* An empty IncludesNone means "exclude nothing" — skip the
|
|
701
|
+
* predicate entirely rather than emitting `NOT IN ()`.
|
|
702
|
+
*/
|
|
703
|
+
const excludeValues: Array<string | ObjectID | number> =
|
|
704
|
+
((value as IncludesNone).values as Array<string | ObjectID>) || [];
|
|
705
|
+
if (excludeValues.length > 0) {
|
|
706
|
+
whereStatement.append(
|
|
707
|
+
SQL`AND ${key} NOT IN ${{
|
|
708
|
+
value: value,
|
|
709
|
+
type: tableColumn.type,
|
|
710
|
+
}}`,
|
|
711
|
+
);
|
|
712
|
+
}
|
|
678
713
|
} else if (value instanceof IsNull) {
|
|
679
714
|
if (tableColumn.type === TableColumnType.Text) {
|
|
680
715
|
whereStatement.append(SQL`AND (${key} IS NULL OR ${key} = '')`);
|
|
@@ -3,6 +3,7 @@ import AggregatedResult from "../../../../Types/BaseDatabase/AggregatedResult";
|
|
|
3
3
|
import MetricFormulaConfigData from "../../../../Types/Metrics/MetricFormulaConfigData";
|
|
4
4
|
import MetricQueryConfigData from "../../../../Types/Metrics/MetricQueryConfigData";
|
|
5
5
|
import MetricsAggregationType from "../../../../Types/Metrics/MetricsAggregationType";
|
|
6
|
+
import MetricsViewConfig from "../../../../Types/Metrics/MetricsViewConfig";
|
|
6
7
|
import MetricMonitorResponse from "../../../../Types/Monitor/MetricMonitor/MetricMonitorResponse";
|
|
7
8
|
import MetricCriteriaContext, {
|
|
8
9
|
MetricAnomalyBaseline,
|
|
@@ -114,12 +115,13 @@ export default class MetricMonitorCriteria {
|
|
|
114
115
|
const seriesBreakdown: Array<MetricSeriesResult> | undefined =
|
|
115
116
|
metricResponse.seriesBreakdown;
|
|
116
117
|
|
|
118
|
+
const metricViewConfig: MetricsViewConfig | undefined =
|
|
119
|
+
MonitorStep.getMetricsViewConfig(input.monitorStep);
|
|
120
|
+
|
|
117
121
|
const queryConfigs: Array<MetricQueryConfigData> =
|
|
118
|
-
|
|
119
|
-
[];
|
|
122
|
+
metricViewConfig?.queryConfigs || [];
|
|
120
123
|
const formulaConfigs: Array<MetricFormulaConfigData> =
|
|
121
|
-
|
|
122
|
-
[];
|
|
124
|
+
metricViewConfig?.formulaConfigs || [];
|
|
123
125
|
|
|
124
126
|
const nativeUnitsByMetricName: { [key: string]: string } | undefined =
|
|
125
127
|
metricResponse.nativeUnitsByMetricName;
|
|
@@ -14,6 +14,7 @@ import AggregatedResult from "../../../Types/BaseDatabase/AggregatedResult";
|
|
|
14
14
|
import AggregateModel from "../../../Types/BaseDatabase/AggregatedModel";
|
|
15
15
|
import MetricQueryConfigData from "../../../Types/Metrics/MetricQueryConfigData";
|
|
16
16
|
import MetricFormulaConfigData from "../../../Types/Metrics/MetricFormulaConfigData";
|
|
17
|
+
import MetricsViewConfig from "../../../Types/Metrics/MetricsViewConfig";
|
|
17
18
|
import ExceptionMonitorResponse from "../../../Types/Monitor/ExceptionMonitor/ExceptionMonitorResponse";
|
|
18
19
|
|
|
19
20
|
export default class MonitorCriteriaDataExtractor {
|
|
@@ -156,10 +157,12 @@ export default class MonitorCriteriaDataExtractor {
|
|
|
156
157
|
|
|
157
158
|
let result: AggregatedResult | undefined;
|
|
158
159
|
|
|
160
|
+
const metricViewConfig: MetricsViewConfig | undefined =
|
|
161
|
+
MonitorStep.getMetricsViewConfig(input.monitorStep);
|
|
162
|
+
|
|
159
163
|
if (alias) {
|
|
160
164
|
const queryConfigs: Array<MetricQueryConfigData> =
|
|
161
|
-
|
|
162
|
-
[];
|
|
165
|
+
metricViewConfig?.queryConfigs || [];
|
|
163
166
|
|
|
164
167
|
let aliasIndex: number = queryConfigs.findIndex(
|
|
165
168
|
(queryConfig: MetricQueryConfigData) => {
|
|
@@ -169,8 +172,7 @@ export default class MonitorCriteriaDataExtractor {
|
|
|
169
172
|
|
|
170
173
|
if (aliasIndex < 0) {
|
|
171
174
|
const formulaConfigs: Array<MetricFormulaConfigData> =
|
|
172
|
-
|
|
173
|
-
?.formulaConfigs || [];
|
|
175
|
+
metricViewConfig?.formulaConfigs || [];
|
|
174
176
|
|
|
175
177
|
const formulaIndex: number = formulaConfigs.findIndex(
|
|
176
178
|
(formulaConfig: MetricFormulaConfigData) => {
|
|
@@ -192,8 +194,7 @@ export default class MonitorCriteriaDataExtractor {
|
|
|
192
194
|
result = aggregatedResults[0];
|
|
193
195
|
if (!alias) {
|
|
194
196
|
const defaultAlias: string | undefined =
|
|
195
|
-
|
|
196
|
-
?.queryConfigs?.[0]?.metricAliasData?.metricVariable;
|
|
197
|
+
metricViewConfig?.queryConfigs?.[0]?.metricAliasData?.metricVariable;
|
|
197
198
|
alias = defaultAlias || null;
|
|
198
199
|
}
|
|
199
200
|
}
|