@oneuptime/common 11.7.1 → 11.7.3
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/KubernetesCostAllocation.ts +103 -0
- package/Models/DatabaseModels/DashboardDomain.ts +1 -0
- package/Models/DatabaseModels/Incident.ts +2 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/Monitor.ts +1 -0
- package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
- package/Models/DatabaseModels/NetworkSite.ts +1 -0
- package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +1 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -0
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +2 -0
- package/Models/DatabaseModels/Probe.ts +13 -13
- package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
- package/Models/DatabaseModels/StatusPage.ts +1 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
- package/Models/DatabaseModels/StatusPageDomain.ts +1 -0
- package/Models/DatabaseModels/TableView.ts +40 -0
- package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +1 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +11 -0
- package/Server/API/BaseAPI.ts +17 -1
- package/Server/API/CommonAPI.ts +32 -0
- package/Server/API/MicrosoftTeamsAPI.ts +99 -2
- package/Server/API/ProbeAPI.ts +2 -0
- package/Server/API/SlackAPI.ts +62 -0
- package/Server/API/StatusPageAPI.ts +157 -192
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.ts +61 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.ts +280 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Services/DatabaseService.ts +11 -3
- package/Server/Services/MonitorGroupService.ts +145 -0
- package/Server/Services/MonitorService.ts +231 -30
- package/Server/Services/ProbeService.ts +319 -16
- package/Server/Services/StatusPageService.ts +72 -42
- package/Server/Services/WorkspaceNotificationRuleService.ts +232 -2
- package/Server/Utils/Database/PostgresErrorTranslator.ts +131 -0
- package/Server/Utils/Monitor/MonitorResource.ts +88 -35
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +21 -1
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +706 -6
- package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
- package/Tests/Models/DatabaseModels/ProbeColumnAccessControl.test.ts +103 -0
- package/Tests/Server/API/BaseAPIUpdateNoOp.test.ts +135 -0
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +317 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +228 -0
- package/Tests/Server/API/ProbeAPI.test.ts +6 -0
- package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +167 -0
- package/Tests/Server/Services/AddHotQueryIndexesMigration.test.ts +255 -0
- package/Tests/Server/Services/AddHotQueryIndexesSecondPassMigration.test.ts +258 -0
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +1 -1
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +121 -0
- package/Tests/Server/Services/HeartbeatWriteFastPathSafety.test.ts +200 -0
- package/Tests/Server/Services/HookFreeWriteSafetySecondPass.test.ts +181 -0
- package/Tests/Server/Services/MonitorGroupServiceBatchedStatuses.test.ts +513 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +492 -0
- package/Tests/Server/Services/MonitorServiceProbeSelection.test.ts +401 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +453 -0
- package/Tests/Server/Services/ProbeAuthKeyCacheAndHeartbeat.test.ts +679 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +3 -3
- package/Tests/Server/Services/StatusPageDomainResolutionCache.test.ts +245 -0
- package/Tests/Server/Services/StatusPageOverviewTimelineWindow.test.ts +283 -0
- package/Tests/Server/Services/StatusPageServiceMcp.test.ts +17 -1
- package/Tests/Server/Services/StatusPageServiceMonitorGroupBatching.test.ts +331 -0
- package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +12 -12
- package/Tests/Server/Services/WorkspaceNotificationRuleChats.test.ts +1390 -0
- package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +4 -4
- package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +1 -3
- package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +1 -3
- package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +1 -1
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +5 -5
- package/Tests/Server/Utils/Alert/AlertPrivacyFilter.test.ts +187 -0
- package/Tests/Server/Utils/AlertEpisode/AlertEpisodePrivacyFilter.test.ts +170 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +125 -0
- package/Tests/Server/Utils/Incident/IncidentPrivacyFilter.test.ts +264 -0
- package/Tests/Server/Utils/IncidentEpisode/IncidentEpisodePrivacyFilter.test.ts +175 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +181 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +2 -2
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +341 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +2175 -0
- package/Tests/Server/Utils/Workspace/SlackChannelsList.test.ts +585 -0
- package/Tests/Types/Billing/MeteredPlan.test.ts +46 -0
- package/Tests/Types/JSONFunctions.test.ts +103 -1
- package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +219 -1
- package/Tests/UI/Components/BulkUpdateForm.test.tsx +174 -0
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +315 -0
- package/Tests/UI/Components/ModelDetailSelect.test.tsx +229 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1258 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +572 -0
- package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
- package/Tests/UI/Components/ModelTableExportFromColumns.test.ts +153 -0
- package/Tests/UI/Components/MonacoLoader.test.ts +67 -0
- package/Tests/UI/Components/MonacoRuntime.test.ts +121 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +212 -3
- package/Tests/UI/Utils/Project.test.ts +809 -0
- package/Tests/UI/Utils/TableColumnsToCsv.test.ts +360 -0
- package/Tests/Utils/Alerts/AlertMetricType.test.ts +143 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +219 -0
- package/Tests/Utils/Incident/IncidentMetricType.test.ts +174 -0
- package/Tests/Utils/Monitor/MonitorProbeSelectionUtil.test.ts +145 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +263 -0
- package/Tests/Utils/UserPreferences.test.ts +563 -0
- package/Types/JSONFunctions.ts +7 -2
- package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
- package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +4 -0
- package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +25 -10
- package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +63 -8
- package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
- package/UI/Components/CodeEditor/MonacoLoader.ts +36 -0
- package/UI/Components/Forms/ModelForm.tsx +41 -18
- package/UI/Components/MasterPage/MasterPage.tsx +65 -0
- package/UI/Components/ModelDetail/CardModelDetail.tsx +23 -1
- package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +687 -34
- package/UI/Components/ModelTable/Column.ts +29 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +420 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
- package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
- package/UI/Components/ModelTable/TableView.tsx +24 -2
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
- package/UI/Components/Navbar/NavBar.tsx +66 -1
- package/UI/Components/SideMenu/SideMenu.tsx +24 -4
- package/UI/Components/Table/Table.tsx +66 -14
- package/UI/Components/Table/TableRow.tsx +180 -175
- package/UI/Components/Table/Types/Column.ts +20 -0
- package/UI/Utils/Project.ts +214 -18
- package/UI/Utils/TableColumnsToCsv.ts +86 -6
- package/UI/esbuild-config.js +34 -0
- package/Utils/Monitor/MonitorProbeSelectionUtil.ts +72 -0
- package/Utils/UserPreferences.ts +53 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +1 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +2 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +1 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSite.js +1 -0
- package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +2 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Probe.js +13 -13
- package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TableView.js +40 -0
- package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +12 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +21 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +72 -2
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProbeAPI.js +2 -0
- package/build/dist/Server/API/ProbeAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +44 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +98 -152
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js +254 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.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/Services/DatabaseService.js +10 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/MonitorGroupService.js +118 -0
- package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +198 -24
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +238 -9
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +46 -39
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +178 -3
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +80 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +67 -29
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +31 -14
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +117 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +549 -7
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +8 -3
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +17 -7
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +37 -11
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +2 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +34 -0
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +1 -0
- package/build/dist/UI/Components/Forms/ModelForm.js +37 -17
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +22 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js +31 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +472 -27
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +189 -0
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/ExportFromColumns.js +30 -0
- package/build/dist/UI/Components/ModelTable/ExportFromColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/Navbar/NavBar.js +39 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +46 -13
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +11 -6
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Utils/Project.js +161 -15
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/UI/Utils/TableColumnsToCsv.js +67 -6
- package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
- package/build/dist/Utils/UserPreferences.js +33 -0
- package/build/dist/Utils/UserPreferences.js.map +1 -1
- package/package.json +4 -3
|
@@ -31,6 +31,8 @@ import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
|
31
31
|
import ObjectID from "../../../../Types/ObjectID";
|
|
32
32
|
import WorkspaceProjectAuthTokenService from "../../../Services/WorkspaceProjectAuthTokenService";
|
|
33
33
|
import WorkspaceProjectAuthToken, {
|
|
34
|
+
MicrosoftTeamsChat,
|
|
35
|
+
MicrosoftTeamsChatType,
|
|
34
36
|
MicrosoftTeamsMiscData,
|
|
35
37
|
} from "../../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
36
38
|
import Incident from "../../../../Models/DatabaseModels/Incident";
|
|
@@ -60,12 +62,16 @@ import UserService from "../../../Services/UserService";
|
|
|
60
62
|
// Import database utilities
|
|
61
63
|
import QueryHelper from "../../../Types/Database/QueryHelper";
|
|
62
64
|
import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
|
|
65
|
+
import LIMIT_MAX from "../../../../Types/Database/LimitMax";
|
|
63
66
|
|
|
64
67
|
// Bot Framework SDK imports
|
|
65
68
|
import {
|
|
66
69
|
CloudAdapter,
|
|
67
70
|
ConfigurationBotFrameworkAuthentication,
|
|
68
71
|
TeamsActivityHandler,
|
|
72
|
+
TeamsInfo,
|
|
73
|
+
TeamsChannelAccount,
|
|
74
|
+
TeamsPagedMembersResult,
|
|
69
75
|
TurnContext,
|
|
70
76
|
ConversationReference,
|
|
71
77
|
MessageFactory,
|
|
@@ -387,9 +393,30 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
387
393
|
`Token expiry calculated: ${OneUptimeDate.toString(expiryDate)}`,
|
|
388
394
|
);
|
|
389
395
|
|
|
390
|
-
|
|
396
|
+
/*
|
|
397
|
+
* Merge the token fields into a FRESH read of miscData instead of the
|
|
398
|
+
* snapshot taken before the OAuth round-trip. The snapshot can be
|
|
399
|
+
* seconds old, and writing it back verbatim would erase concurrent
|
|
400
|
+
* miscData updates — in particular chats captured into availableChats
|
|
401
|
+
* by bot install events, which cannot be re-derived from Graph.
|
|
402
|
+
*/
|
|
403
|
+
let latestMiscData: MicrosoftTeamsMiscData = data.miscData;
|
|
404
|
+
try {
|
|
405
|
+
const latestProjectAuth: WorkspaceProjectAuthToken | null =
|
|
406
|
+
await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
407
|
+
projectId: data.projectId,
|
|
408
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
409
|
+
});
|
|
410
|
+
if (latestProjectAuth?.miscData) {
|
|
411
|
+
latestMiscData = latestProjectAuth.miscData as MicrosoftTeamsMiscData;
|
|
412
|
+
}
|
|
413
|
+
} catch (err) {
|
|
414
|
+
logger.debug("Could not re-read miscData before token refresh write");
|
|
415
|
+
logger.debug(err);
|
|
416
|
+
}
|
|
417
|
+
|
|
391
418
|
const updatedMiscData: MicrosoftTeamsMiscData = {
|
|
392
|
-
...
|
|
419
|
+
...latestMiscData,
|
|
393
420
|
appAccessToken: newAccessToken,
|
|
394
421
|
appAccessTokenExpiresAt: OneUptimeDate.toString(expiryDate),
|
|
395
422
|
lastAppTokenIssuedAt: OneUptimeDate.toString(now),
|
|
@@ -1223,6 +1250,55 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1223
1250
|
}
|
|
1224
1251
|
}
|
|
1225
1252
|
|
|
1253
|
+
// Send to Teams chats (group / personal chats the OneUptime app was added to).
|
|
1254
|
+
const chatIdsToPostTo: Array<string> =
|
|
1255
|
+
data.workspaceMessagePayload.chatIds || [];
|
|
1256
|
+
|
|
1257
|
+
if (chatIdsToPostTo.length > 0) {
|
|
1258
|
+
logger.debug(`Processing ${chatIdsToPostTo.length} chat ids`);
|
|
1259
|
+
|
|
1260
|
+
const availableChats: Record<string, MicrosoftTeamsChat> =
|
|
1261
|
+
await this.getChatsForProject({
|
|
1262
|
+
projectId: data.projectId,
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
for (const chatId of chatIdsToPostTo) {
|
|
1266
|
+
const chatAsChannel: WorkspaceChannel = {
|
|
1267
|
+
id: chatId,
|
|
1268
|
+
name: availableChats[chatId]?.name || chatId,
|
|
1269
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
try {
|
|
1273
|
+
let lastThread: WorkspaceThread | undefined;
|
|
1274
|
+
for (const adaptiveCard of adaptiveCards) {
|
|
1275
|
+
lastThread = await this.sendAdaptiveCardToChat({
|
|
1276
|
+
chatId: chatId,
|
|
1277
|
+
adaptiveCard: adaptiveCard,
|
|
1278
|
+
projectId: data.projectId,
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
if (lastThread) {
|
|
1283
|
+
logger.debug(
|
|
1284
|
+
`Message sent successfully to chat ${chatAsChannel.name}, thread: ${JSON.stringify(lastThread)}`,
|
|
1285
|
+
);
|
|
1286
|
+
workspaceMessageResponse.threads.push(lastThread);
|
|
1287
|
+
}
|
|
1288
|
+
} catch (e) {
|
|
1289
|
+
logger.error(`Error sending message to chat ID ${chatId}:`, {
|
|
1290
|
+
projectId: data.projectId.toString(),
|
|
1291
|
+
chatId: chatId,
|
|
1292
|
+
});
|
|
1293
|
+
logger.error(e);
|
|
1294
|
+
workspaceMessageResponse.errors!.push({
|
|
1295
|
+
channel: chatAsChannel,
|
|
1296
|
+
error: e instanceof Error ? e.message : String(e),
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1226
1302
|
logger.debug("=== Message sending completed ===");
|
|
1227
1303
|
logger.debug(
|
|
1228
1304
|
`Final thread count: ${workspaceMessageResponse.threads.length}`,
|
|
@@ -1388,6 +1464,137 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1388
1464
|
}
|
|
1389
1465
|
}
|
|
1390
1466
|
|
|
1467
|
+
/*
|
|
1468
|
+
* Sends an adaptive card to a Teams group chat or personal (1:1) chat via a
|
|
1469
|
+
* proactive Bot Framework message. The OneUptime app must have been added to
|
|
1470
|
+
* the chat — Graph has no app-only permission for posting to chats, so the
|
|
1471
|
+
* bot conversation is the only supported transport.
|
|
1472
|
+
*/
|
|
1473
|
+
@CaptureSpan()
|
|
1474
|
+
public static async sendAdaptiveCardToChat(data: {
|
|
1475
|
+
chatId: string;
|
|
1476
|
+
projectId: ObjectID;
|
|
1477
|
+
adaptiveCard: JSONObject;
|
|
1478
|
+
}): Promise<WorkspaceThread> {
|
|
1479
|
+
logger.debug("sendAdaptiveCardToChat called with data:");
|
|
1480
|
+
logger.debug(`Chat ID: ${data.chatId}`);
|
|
1481
|
+
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
1482
|
+
|
|
1483
|
+
try {
|
|
1484
|
+
const projectAuth: WorkspaceProjectAuthToken | null =
|
|
1485
|
+
await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
1486
|
+
projectId: data.projectId,
|
|
1487
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
if (!projectAuth || !projectAuth.miscData) {
|
|
1491
|
+
throw new BadDataException(
|
|
1492
|
+
"Microsoft Teams integration not found for this project",
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
const miscData: MicrosoftTeamsMiscData =
|
|
1497
|
+
projectAuth.miscData as MicrosoftTeamsMiscData;
|
|
1498
|
+
if (!miscData.botId) {
|
|
1499
|
+
throw new BadDataException(
|
|
1500
|
+
"Bot ID not found in Microsoft Teams integration",
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
const tenantId: string | undefined = projectAuth.workspaceProjectId;
|
|
1505
|
+
|
|
1506
|
+
if (!tenantId) {
|
|
1507
|
+
throw new BadDataException(
|
|
1508
|
+
"Tenant ID not found in Microsoft Teams integration",
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
if (!MicrosoftTeamsAppClientId) {
|
|
1513
|
+
throw new BadDataException(
|
|
1514
|
+
"Microsoft Teams App Client ID not configured",
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
const chat: MicrosoftTeamsChat | undefined =
|
|
1519
|
+
miscData.availableChats?.[data.chatId];
|
|
1520
|
+
|
|
1521
|
+
if (!chat) {
|
|
1522
|
+
throw new BadDataException(
|
|
1523
|
+
"This chat is not connected to OneUptime. Please add the OneUptime app to the chat in Microsoft Teams and try again.",
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
const adapter: CloudAdapter = this.getBotAdapter();
|
|
1528
|
+
|
|
1529
|
+
const conversationReference: ConversationReference = {
|
|
1530
|
+
bot: {
|
|
1531
|
+
id: MicrosoftTeamsAppClientId,
|
|
1532
|
+
name: "OneUptime Bot",
|
|
1533
|
+
},
|
|
1534
|
+
conversation: {
|
|
1535
|
+
id: chat.id,
|
|
1536
|
+
name: chat.name,
|
|
1537
|
+
isGroup: chat.chatType === "groupChat",
|
|
1538
|
+
conversationType: chat.chatType,
|
|
1539
|
+
tenantId: tenantId,
|
|
1540
|
+
},
|
|
1541
|
+
channelId: "msteams",
|
|
1542
|
+
/*
|
|
1543
|
+
* Fallback is the commercial-cloud global endpoint; the serviceUrl
|
|
1544
|
+
* captured from bot activities is preferred (required for GCC/DoD).
|
|
1545
|
+
*/
|
|
1546
|
+
serviceUrl: chat.serviceUrl || "https://smba.trafficmanager.net/teams/",
|
|
1547
|
+
};
|
|
1548
|
+
|
|
1549
|
+
logger.debug(
|
|
1550
|
+
`Chat conversation reference: ${JSON.stringify(conversationReference)}`,
|
|
1551
|
+
);
|
|
1552
|
+
|
|
1553
|
+
let messageId: string = "";
|
|
1554
|
+
|
|
1555
|
+
await adapter.continueConversationAsync(
|
|
1556
|
+
MicrosoftTeamsAppClientId,
|
|
1557
|
+
conversationReference,
|
|
1558
|
+
async (context: TurnContext) => {
|
|
1559
|
+
logger.debug("Sending adaptive card to chat as proactive message");
|
|
1560
|
+
|
|
1561
|
+
const message: Partial<Activity> = MessageFactory.attachment({
|
|
1562
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
1563
|
+
content: data.adaptiveCard,
|
|
1564
|
+
});
|
|
1565
|
+
|
|
1566
|
+
const response: ResourceResponse | undefined =
|
|
1567
|
+
await context.sendActivity(message);
|
|
1568
|
+
|
|
1569
|
+
messageId = response?.id || "";
|
|
1570
|
+
|
|
1571
|
+
logger.debug(`Chat message sent with ID: ${messageId}`);
|
|
1572
|
+
},
|
|
1573
|
+
);
|
|
1574
|
+
|
|
1575
|
+
const thread: WorkspaceThread = {
|
|
1576
|
+
channel: {
|
|
1577
|
+
id: chat.id,
|
|
1578
|
+
name: chat.name,
|
|
1579
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1580
|
+
},
|
|
1581
|
+
threadId: messageId,
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
logger.debug(
|
|
1585
|
+
`Sent message to chat via Bot Framework: ${JSON.stringify(thread)}`,
|
|
1586
|
+
);
|
|
1587
|
+
return thread;
|
|
1588
|
+
} catch (error) {
|
|
1589
|
+
logger.error("Error sending adaptive card to chat via Bot Framework:", {
|
|
1590
|
+
projectId: data.projectId.toString(),
|
|
1591
|
+
chatId: data.chatId,
|
|
1592
|
+
});
|
|
1593
|
+
logger.error(error);
|
|
1594
|
+
throw error;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1391
1598
|
@CaptureSpan()
|
|
1392
1599
|
public static override async getWorkspaceChannelFromChannelId(data: {
|
|
1393
1600
|
authToken: string;
|
|
@@ -1921,6 +2128,29 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1921
2128
|
return;
|
|
1922
2129
|
}
|
|
1923
2130
|
|
|
2131
|
+
/*
|
|
2132
|
+
* Backfill chat capture from inbound messages. Chats where the app was
|
|
2133
|
+
* installed before chat capture shipped never fire install events — per
|
|
2134
|
+
* Microsoft docs, app upgrades only send installationUpdate when the
|
|
2135
|
+
* manifest's bot is added or removed, so a version bump alone re-fires
|
|
2136
|
+
* nothing. Messaging the bot in a chat is the documented recovery path,
|
|
2137
|
+
* and this also keeps the stored serviceUrl fresh (docs: verify the
|
|
2138
|
+
* stored serviceUrl when a new message arrives). Cheap when the chat is
|
|
2139
|
+
* already captured (single read, no roster fetch, no write).
|
|
2140
|
+
*/
|
|
2141
|
+
const messageConversationType: string =
|
|
2142
|
+
(conversation["conversationType"] as string) || "";
|
|
2143
|
+
if (
|
|
2144
|
+
messageConversationType === "personal" ||
|
|
2145
|
+
messageConversationType === "groupChat"
|
|
2146
|
+
) {
|
|
2147
|
+
await this.captureChatFromBotActivity({
|
|
2148
|
+
activity: data.activity,
|
|
2149
|
+
turnContext: data.turnContext,
|
|
2150
|
+
onlyIfMissingOrStale: true,
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
|
|
1924
2154
|
// If this is actually an Adaptive Card submit wrapped as a message, route to invoke handler
|
|
1925
2155
|
if (
|
|
1926
2156
|
(possibleActionValue["action"] as string) ||
|
|
@@ -3239,10 +3469,25 @@ All monitoring checks are passing normally.`;
|
|
|
3239
3469
|
return member["id"] === recipientId;
|
|
3240
3470
|
});
|
|
3241
3471
|
|
|
3472
|
+
const botWasRemoved: boolean = membersRemoved.some((member: JSONObject) => {
|
|
3473
|
+
return member["id"] === recipientId;
|
|
3474
|
+
});
|
|
3475
|
+
|
|
3242
3476
|
if (botWasAdded) {
|
|
3243
3477
|
logger.debug("OneUptime bot was added to a Teams conversation");
|
|
3478
|
+
await this.captureChatFromBotActivity({
|
|
3479
|
+
activity: data.activity,
|
|
3480
|
+
turnContext: data.turnContext,
|
|
3481
|
+
});
|
|
3244
3482
|
await this.sendWelcomeAdaptiveCard(data.turnContext);
|
|
3245
3483
|
}
|
|
3484
|
+
|
|
3485
|
+
if (botWasRemoved) {
|
|
3486
|
+
logger.debug("OneUptime bot was removed from a Teams conversation");
|
|
3487
|
+
await this.removeChatFromBotActivity({
|
|
3488
|
+
activity: data.activity,
|
|
3489
|
+
});
|
|
3490
|
+
}
|
|
3246
3491
|
}
|
|
3247
3492
|
|
|
3248
3493
|
@CaptureSpan()
|
|
@@ -3258,13 +3503,421 @@ All monitoring checks are passing normally.`;
|
|
|
3258
3503
|
logger.debug(`Installation update - Action: ${action}`);
|
|
3259
3504
|
logger.debug(`Conversation: ${JSON.stringify(conversation)}`);
|
|
3260
3505
|
|
|
3261
|
-
|
|
3506
|
+
/*
|
|
3507
|
+
* Teams sends "add-upgrade" / "remove-upgrade" when an app upgrade adds
|
|
3508
|
+
* or removes the bot in the manifest (per Microsoft docs, a plain
|
|
3509
|
+
* version bump fires no installationUpdate at all). Treat them the same
|
|
3510
|
+
* as add/remove. Chats installed before chat capture shipped are
|
|
3511
|
+
* backfilled from inbound messages in handleBotMessageActivity instead.
|
|
3512
|
+
*/
|
|
3513
|
+
if (action === "add" || action === "add-upgrade") {
|
|
3262
3514
|
logger.debug("OneUptime bot was installed");
|
|
3263
|
-
|
|
3515
|
+
await this.captureChatFromBotActivity({
|
|
3516
|
+
activity: data.activity,
|
|
3517
|
+
turnContext: data.turnContext,
|
|
3518
|
+
});
|
|
3519
|
+
} else if (action === "remove" || action === "remove-upgrade") {
|
|
3264
3520
|
logger.debug("OneUptime bot was uninstalled");
|
|
3521
|
+
await this.removeChatFromBotActivity({
|
|
3522
|
+
activity: data.activity,
|
|
3523
|
+
});
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
/*
|
|
3528
|
+
* Chats (group chats and personal 1:1 chats) cannot be listed with the
|
|
3529
|
+
* app-only Graph token, so the only way OneUptime learns about them is by
|
|
3530
|
+
* capturing the conversation details when the OneUptime app is added to a
|
|
3531
|
+
* chat. These captured chats power the "post to chat" notification rules.
|
|
3532
|
+
*/
|
|
3533
|
+
|
|
3534
|
+
private static getChatTypeFromConversation(
|
|
3535
|
+
conversation: JSONObject,
|
|
3536
|
+
): MicrosoftTeamsChatType | null {
|
|
3537
|
+
const conversationType: string =
|
|
3538
|
+
(conversation["conversationType"] as string) || "";
|
|
3539
|
+
|
|
3540
|
+
if (conversationType === "personal" || conversationType === "groupChat") {
|
|
3541
|
+
return conversationType;
|
|
3542
|
+
}
|
|
3543
|
+
|
|
3544
|
+
return null; // channels and meetings are not chats.
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
public static getChatDisplayName(data: {
|
|
3548
|
+
chatType: MicrosoftTeamsChatType;
|
|
3549
|
+
topic?: string | undefined;
|
|
3550
|
+
memberNames: Array<string>;
|
|
3551
|
+
}): string {
|
|
3552
|
+
/*
|
|
3553
|
+
* Keep well under the 100-char ShortText columns that store the chat
|
|
3554
|
+
* name in notification logs — Teams chat topics can be much longer.
|
|
3555
|
+
*/
|
|
3556
|
+
const maxNameLength: number = 80;
|
|
3557
|
+
|
|
3558
|
+
const truncate: (name: string) => string = (name: string): string => {
|
|
3559
|
+
return name.length > maxNameLength
|
|
3560
|
+
? `${name.substring(0, maxNameLength - 1)}…`
|
|
3561
|
+
: name;
|
|
3562
|
+
};
|
|
3563
|
+
|
|
3564
|
+
if (data.topic && data.topic.trim()) {
|
|
3565
|
+
return truncate(data.topic.trim());
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
const memberNames: Array<string> = data.memberNames.filter(
|
|
3569
|
+
(name: string) => {
|
|
3570
|
+
return Boolean(name && name.trim());
|
|
3571
|
+
},
|
|
3572
|
+
);
|
|
3573
|
+
|
|
3574
|
+
if (data.chatType === "personal") {
|
|
3575
|
+
return memberNames[0] ? truncate(`${memberNames[0]}`) : "Personal chat";
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
if (memberNames.length === 0) {
|
|
3579
|
+
return "Group chat";
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3582
|
+
const maxNamesToShow: number = 3;
|
|
3583
|
+
const shownNames: Array<string> = memberNames.slice(0, maxNamesToShow);
|
|
3584
|
+
const remainingCount: number = memberNames.length - shownNames.length;
|
|
3585
|
+
|
|
3586
|
+
if (remainingCount > 0) {
|
|
3587
|
+
return truncate(`${shownNames.join(", ")} + ${remainingCount} more`);
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
return truncate(shownNames.join(", "));
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
/*
|
|
3594
|
+
* Returns true when every connected project of this tenant already has
|
|
3595
|
+
* this chat stored with the same service URL — i.e. there is nothing to
|
|
3596
|
+
* capture or refresh.
|
|
3597
|
+
*/
|
|
3598
|
+
private static async isChatCapturedForTenant(data: {
|
|
3599
|
+
tenantId: string;
|
|
3600
|
+
chatId: string;
|
|
3601
|
+
serviceUrl?: string | undefined;
|
|
3602
|
+
}): Promise<boolean> {
|
|
3603
|
+
const projectAuths: Array<WorkspaceProjectAuthToken> =
|
|
3604
|
+
await WorkspaceProjectAuthTokenService.findBy({
|
|
3605
|
+
query: {
|
|
3606
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
3607
|
+
workspaceProjectId: data.tenantId,
|
|
3608
|
+
},
|
|
3609
|
+
select: {
|
|
3610
|
+
_id: true,
|
|
3611
|
+
miscData: true,
|
|
3612
|
+
},
|
|
3613
|
+
limit: LIMIT_MAX,
|
|
3614
|
+
skip: 0,
|
|
3615
|
+
props: {
|
|
3616
|
+
isRoot: true,
|
|
3617
|
+
},
|
|
3618
|
+
});
|
|
3619
|
+
|
|
3620
|
+
if (projectAuths.length === 0) {
|
|
3621
|
+
return true; // no connected projects — nothing to capture into.
|
|
3622
|
+
}
|
|
3623
|
+
|
|
3624
|
+
for (const projectAuth of projectAuths) {
|
|
3625
|
+
const chat: MicrosoftTeamsChat | undefined = (
|
|
3626
|
+
projectAuth.miscData as MicrosoftTeamsMiscData
|
|
3627
|
+
)?.availableChats?.[data.chatId];
|
|
3628
|
+
|
|
3629
|
+
if (!chat) {
|
|
3630
|
+
return false;
|
|
3631
|
+
}
|
|
3632
|
+
|
|
3633
|
+
if (data.serviceUrl && chat.serviceUrl !== data.serviceUrl) {
|
|
3634
|
+
return false; // stored serviceUrl is stale.
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
return true;
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
@CaptureSpan()
|
|
3642
|
+
private static async captureChatFromBotActivity(data: {
|
|
3643
|
+
activity: JSONObject;
|
|
3644
|
+
turnContext: TurnContext;
|
|
3645
|
+
onlyIfMissingOrStale?: boolean | undefined;
|
|
3646
|
+
}): Promise<void> {
|
|
3647
|
+
try {
|
|
3648
|
+
const conversation: JSONObject =
|
|
3649
|
+
(data.activity["conversation"] as JSONObject) || {};
|
|
3650
|
+
|
|
3651
|
+
const chatType: MicrosoftTeamsChatType | null =
|
|
3652
|
+
this.getChatTypeFromConversation(conversation);
|
|
3653
|
+
|
|
3654
|
+
if (!chatType) {
|
|
3655
|
+
return; // bot was added to a team channel, not a chat.
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
const chatId: string = (conversation["id"] as string) || "";
|
|
3659
|
+
|
|
3660
|
+
if (!chatId) {
|
|
3661
|
+
logger.debug("No conversation id found on chat activity. Skipping.");
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3665
|
+
const channelData: JSONObject =
|
|
3666
|
+
(data.activity["channelData"] as JSONObject) || {};
|
|
3667
|
+
const tenantId: string =
|
|
3668
|
+
((channelData["tenant"] as JSONObject)?.["id"] as string) ||
|
|
3669
|
+
(conversation["tenantId"] as string) ||
|
|
3670
|
+
"";
|
|
3671
|
+
|
|
3672
|
+
if (!tenantId) {
|
|
3673
|
+
logger.debug("No tenant id found on chat activity. Skipping.");
|
|
3674
|
+
return;
|
|
3675
|
+
}
|
|
3676
|
+
|
|
3677
|
+
const activityServiceUrl: string | undefined =
|
|
3678
|
+
(data.activity["serviceUrl"] as string) ||
|
|
3679
|
+
data.turnContext.activity.serviceUrl ||
|
|
3680
|
+
undefined;
|
|
3681
|
+
|
|
3682
|
+
/*
|
|
3683
|
+
* On the message-backfill path, skip the roster fetch and DB writes
|
|
3684
|
+
* when the chat is already captured with a fresh serviceUrl.
|
|
3685
|
+
*/
|
|
3686
|
+
if (data.onlyIfMissingOrStale) {
|
|
3687
|
+
const alreadyCaptured: boolean = await this.isChatCapturedForTenant({
|
|
3688
|
+
tenantId: tenantId,
|
|
3689
|
+
chatId: chatId,
|
|
3690
|
+
serviceUrl: activityServiceUrl,
|
|
3691
|
+
});
|
|
3692
|
+
|
|
3693
|
+
if (alreadyCaptured) {
|
|
3694
|
+
return;
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3698
|
+
// Resolve a human friendly name for the chat.
|
|
3699
|
+
const topic: string | undefined = conversation["name"] as
|
|
3700
|
+
| string
|
|
3701
|
+
| undefined;
|
|
3702
|
+
|
|
3703
|
+
let memberNames: Array<string> = [];
|
|
3704
|
+
|
|
3705
|
+
try {
|
|
3706
|
+
const botId: string | undefined =
|
|
3707
|
+
data.turnContext.activity.recipient?.id;
|
|
3708
|
+
|
|
3709
|
+
/*
|
|
3710
|
+
* TeamsInfo.getMembers is deprecated — page through the roster
|
|
3711
|
+
* instead. Chats return the full roster in one page in practice.
|
|
3712
|
+
*/
|
|
3713
|
+
const members: Array<TeamsChannelAccount> = [];
|
|
3714
|
+
let continuationToken: string | undefined = undefined;
|
|
3715
|
+
do {
|
|
3716
|
+
const page: TeamsPagedMembersResult = await TeamsInfo.getPagedMembers(
|
|
3717
|
+
data.turnContext,
|
|
3718
|
+
500,
|
|
3719
|
+
continuationToken,
|
|
3720
|
+
);
|
|
3721
|
+
members.push(...(page.members || []));
|
|
3722
|
+
continuationToken = page.continuationToken;
|
|
3723
|
+
} while (continuationToken);
|
|
3724
|
+
|
|
3725
|
+
memberNames = members
|
|
3726
|
+
.filter((member: TeamsChannelAccount) => {
|
|
3727
|
+
return member.id !== botId;
|
|
3728
|
+
})
|
|
3729
|
+
.map((member: TeamsChannelAccount) => {
|
|
3730
|
+
return member.name || "";
|
|
3731
|
+
});
|
|
3732
|
+
} catch (err) {
|
|
3733
|
+
logger.debug("Could not fetch chat members for chat name resolution");
|
|
3734
|
+
logger.debug(err);
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
const chat: MicrosoftTeamsChat = {
|
|
3738
|
+
id: chatId,
|
|
3739
|
+
name: this.getChatDisplayName({
|
|
3740
|
+
chatType: chatType,
|
|
3741
|
+
topic: topic,
|
|
3742
|
+
memberNames: memberNames,
|
|
3743
|
+
}),
|
|
3744
|
+
chatType: chatType,
|
|
3745
|
+
serviceUrl: activityServiceUrl,
|
|
3746
|
+
addedAt: OneUptimeDate.getCurrentDate().toISOString(),
|
|
3747
|
+
};
|
|
3748
|
+
|
|
3749
|
+
await this.saveChatToProjectAuthTokens({
|
|
3750
|
+
tenantId: tenantId,
|
|
3751
|
+
chat: chat,
|
|
3752
|
+
});
|
|
3753
|
+
|
|
3754
|
+
logger.debug(
|
|
3755
|
+
`Captured Microsoft Teams chat ${chatId} (${chat.name}) for tenant ${tenantId}`,
|
|
3756
|
+
);
|
|
3757
|
+
} catch (err) {
|
|
3758
|
+
logger.error("Error capturing Microsoft Teams chat from bot activity:");
|
|
3759
|
+
logger.error(err);
|
|
3265
3760
|
}
|
|
3266
3761
|
}
|
|
3267
3762
|
|
|
3763
|
+
@CaptureSpan()
|
|
3764
|
+
private static async removeChatFromBotActivity(data: {
|
|
3765
|
+
activity: JSONObject;
|
|
3766
|
+
}): Promise<void> {
|
|
3767
|
+
try {
|
|
3768
|
+
const conversation: JSONObject =
|
|
3769
|
+
(data.activity["conversation"] as JSONObject) || {};
|
|
3770
|
+
|
|
3771
|
+
const chatType: MicrosoftTeamsChatType | null =
|
|
3772
|
+
this.getChatTypeFromConversation(conversation);
|
|
3773
|
+
|
|
3774
|
+
if (!chatType) {
|
|
3775
|
+
return;
|
|
3776
|
+
}
|
|
3777
|
+
|
|
3778
|
+
const chatId: string = (conversation["id"] as string) || "";
|
|
3779
|
+
const channelData: JSONObject =
|
|
3780
|
+
(data.activity["channelData"] as JSONObject) || {};
|
|
3781
|
+
const tenantId: string =
|
|
3782
|
+
((channelData["tenant"] as JSONObject)?.["id"] as string) ||
|
|
3783
|
+
(conversation["tenantId"] as string) ||
|
|
3784
|
+
"";
|
|
3785
|
+
|
|
3786
|
+
if (!chatId || !tenantId) {
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3789
|
+
|
|
3790
|
+
await this.removeChatFromProjectAuthTokens({
|
|
3791
|
+
tenantId: tenantId,
|
|
3792
|
+
chatId: chatId,
|
|
3793
|
+
});
|
|
3794
|
+
|
|
3795
|
+
logger.debug(
|
|
3796
|
+
`Removed Microsoft Teams chat ${chatId} for tenant ${tenantId}`,
|
|
3797
|
+
);
|
|
3798
|
+
} catch (err) {
|
|
3799
|
+
logger.error("Error removing Microsoft Teams chat from bot activity:");
|
|
3800
|
+
logger.error(err);
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
|
|
3804
|
+
@CaptureSpan()
|
|
3805
|
+
public static async saveChatToProjectAuthTokens(data: {
|
|
3806
|
+
tenantId: string;
|
|
3807
|
+
chat: MicrosoftTeamsChat;
|
|
3808
|
+
}): Promise<void> {
|
|
3809
|
+
/*
|
|
3810
|
+
* A tenant can be connected to more than one OneUptime project, and the
|
|
3811
|
+
* install event does not say which project it belongs to — so save the
|
|
3812
|
+
* chat on every project connected to this tenant.
|
|
3813
|
+
*/
|
|
3814
|
+
const projectAuths: Array<WorkspaceProjectAuthToken> =
|
|
3815
|
+
await WorkspaceProjectAuthTokenService.findBy({
|
|
3816
|
+
query: {
|
|
3817
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
3818
|
+
workspaceProjectId: data.tenantId,
|
|
3819
|
+
},
|
|
3820
|
+
select: {
|
|
3821
|
+
_id: true,
|
|
3822
|
+
miscData: true,
|
|
3823
|
+
},
|
|
3824
|
+
limit: LIMIT_MAX,
|
|
3825
|
+
skip: 0,
|
|
3826
|
+
props: {
|
|
3827
|
+
isRoot: true,
|
|
3828
|
+
},
|
|
3829
|
+
});
|
|
3830
|
+
|
|
3831
|
+
for (const projectAuth of projectAuths) {
|
|
3832
|
+
const miscData: MicrosoftTeamsMiscData = {
|
|
3833
|
+
...((projectAuth.miscData as MicrosoftTeamsMiscData) || {}),
|
|
3834
|
+
} as MicrosoftTeamsMiscData;
|
|
3835
|
+
|
|
3836
|
+
miscData.availableChats = {
|
|
3837
|
+
...(miscData.availableChats || {}),
|
|
3838
|
+
[data.chat.id]: data.chat,
|
|
3839
|
+
};
|
|
3840
|
+
|
|
3841
|
+
await WorkspaceProjectAuthTokenService.updateOneById({
|
|
3842
|
+
id: projectAuth.id!,
|
|
3843
|
+
data: {
|
|
3844
|
+
miscData: miscData,
|
|
3845
|
+
},
|
|
3846
|
+
props: {
|
|
3847
|
+
isRoot: true,
|
|
3848
|
+
},
|
|
3849
|
+
});
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
|
|
3853
|
+
@CaptureSpan()
|
|
3854
|
+
public static async removeChatFromProjectAuthTokens(data: {
|
|
3855
|
+
tenantId: string;
|
|
3856
|
+
chatId: string;
|
|
3857
|
+
}): Promise<void> {
|
|
3858
|
+
const projectAuths: Array<WorkspaceProjectAuthToken> =
|
|
3859
|
+
await WorkspaceProjectAuthTokenService.findBy({
|
|
3860
|
+
query: {
|
|
3861
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
3862
|
+
workspaceProjectId: data.tenantId,
|
|
3863
|
+
},
|
|
3864
|
+
select: {
|
|
3865
|
+
_id: true,
|
|
3866
|
+
miscData: true,
|
|
3867
|
+
},
|
|
3868
|
+
limit: LIMIT_MAX,
|
|
3869
|
+
skip: 0,
|
|
3870
|
+
props: {
|
|
3871
|
+
isRoot: true,
|
|
3872
|
+
},
|
|
3873
|
+
});
|
|
3874
|
+
|
|
3875
|
+
for (const projectAuth of projectAuths) {
|
|
3876
|
+
const miscData: MicrosoftTeamsMiscData = {
|
|
3877
|
+
...((projectAuth.miscData as MicrosoftTeamsMiscData) || {}),
|
|
3878
|
+
} as MicrosoftTeamsMiscData;
|
|
3879
|
+
|
|
3880
|
+
if (!miscData.availableChats || !miscData.availableChats[data.chatId]) {
|
|
3881
|
+
continue;
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
const availableChats: Record<string, MicrosoftTeamsChat> = {
|
|
3885
|
+
...miscData.availableChats,
|
|
3886
|
+
};
|
|
3887
|
+
delete availableChats[data.chatId];
|
|
3888
|
+
miscData.availableChats = availableChats;
|
|
3889
|
+
|
|
3890
|
+
await WorkspaceProjectAuthTokenService.updateOneById({
|
|
3891
|
+
id: projectAuth.id!,
|
|
3892
|
+
data: {
|
|
3893
|
+
miscData: miscData,
|
|
3894
|
+
},
|
|
3895
|
+
props: {
|
|
3896
|
+
isRoot: true,
|
|
3897
|
+
},
|
|
3898
|
+
});
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
@CaptureSpan()
|
|
3903
|
+
public static async getChatsForProject(data: {
|
|
3904
|
+
projectId: ObjectID;
|
|
3905
|
+
}): Promise<Record<string, MicrosoftTeamsChat>> {
|
|
3906
|
+
const projectAuth: WorkspaceProjectAuthToken | null =
|
|
3907
|
+
await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
3908
|
+
projectId: data.projectId,
|
|
3909
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
3910
|
+
});
|
|
3911
|
+
|
|
3912
|
+
if (!projectAuth || !projectAuth.miscData) {
|
|
3913
|
+
return {};
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
return (
|
|
3917
|
+
(projectAuth.miscData as MicrosoftTeamsMiscData).availableChats || {}
|
|
3918
|
+
);
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3268
3921
|
/**
|
|
3269
3922
|
* Process Bot Framework activity using the botbuilder SDK adapter.processActivity
|
|
3270
3923
|
* This replaces the manual JWT validation and activity handling with proper SDK methods
|
|
@@ -3331,6 +3984,20 @@ All monitoring checks are passing normally.`;
|
|
|
3331
3984
|
},
|
|
3332
3985
|
);
|
|
3333
3986
|
|
|
3987
|
+
this.onMembersRemoved(
|
|
3988
|
+
async (context: TurnContext, next: () => Promise<void>) => {
|
|
3989
|
+
logger.debug(
|
|
3990
|
+
"Handling members removed activity: " +
|
|
3991
|
+
JSON.stringify(context.activity),
|
|
3992
|
+
);
|
|
3993
|
+
await MicrosoftTeamsUtil.handleConversationUpdateActivity({
|
|
3994
|
+
activity: context.activity as unknown as JSONObject,
|
|
3995
|
+
turnContext: context,
|
|
3996
|
+
});
|
|
3997
|
+
await next();
|
|
3998
|
+
},
|
|
3999
|
+
);
|
|
4000
|
+
|
|
3334
4001
|
this.onInstallationUpdateAdd(
|
|
3335
4002
|
async (context: TurnContext, next: () => Promise<void>) => {
|
|
3336
4003
|
logger.debug(
|
|
@@ -3344,6 +4011,20 @@ All monitoring checks are passing normally.`;
|
|
|
3344
4011
|
await next();
|
|
3345
4012
|
},
|
|
3346
4013
|
);
|
|
4014
|
+
|
|
4015
|
+
this.onInstallationUpdateRemove(
|
|
4016
|
+
async (context: TurnContext, next: () => Promise<void>) => {
|
|
4017
|
+
logger.debug(
|
|
4018
|
+
"Handling installation update remove activity: " +
|
|
4019
|
+
JSON.stringify(context.activity),
|
|
4020
|
+
);
|
|
4021
|
+
await MicrosoftTeamsUtil.handleInstallationUpdateActivity({
|
|
4022
|
+
activity: context.activity as unknown as JSONObject,
|
|
4023
|
+
turnContext: context,
|
|
4024
|
+
});
|
|
4025
|
+
await next();
|
|
4026
|
+
},
|
|
4027
|
+
);
|
|
3347
4028
|
}
|
|
3348
4029
|
|
|
3349
4030
|
protected override async onInvokeActivity(
|
|
@@ -3679,9 +4360,28 @@ All monitoring checks are passing normally.`;
|
|
|
3679
4360
|
|
|
3680
4361
|
logger.debug(`Processed ${Object.keys(availableTeams).length} teams`);
|
|
3681
4362
|
|
|
3682
|
-
|
|
3683
|
-
|
|
4363
|
+
/*
|
|
4364
|
+
* Update project auth token with new teams. Re-read miscData first —
|
|
4365
|
+
* the snapshot from the start of this method is stale by the length
|
|
4366
|
+
* of the paginated Graph fetch, and writing it back verbatim would
|
|
4367
|
+
* erase concurrent updates (e.g. chats captured into availableChats
|
|
4368
|
+
* by bot install events, which cannot be re-derived).
|
|
4369
|
+
*/
|
|
4370
|
+
let miscData: MicrosoftTeamsMiscData =
|
|
3684
4371
|
(projectAuth.miscData as MicrosoftTeamsMiscData) || {};
|
|
4372
|
+
try {
|
|
4373
|
+
const latestProjectAuth: WorkspaceProjectAuthToken | null =
|
|
4374
|
+
await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
4375
|
+
projectId: data.projectId,
|
|
4376
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
4377
|
+
});
|
|
4378
|
+
if (latestProjectAuth?.miscData) {
|
|
4379
|
+
miscData = latestProjectAuth.miscData as MicrosoftTeamsMiscData;
|
|
4380
|
+
}
|
|
4381
|
+
} catch (err) {
|
|
4382
|
+
logger.debug("Could not re-read miscData before teams refresh write");
|
|
4383
|
+
logger.debug(err);
|
|
4384
|
+
}
|
|
3685
4385
|
miscData.availableTeams = availableTeams;
|
|
3686
4386
|
miscData.tenantId = tenantId;
|
|
3687
4387
|
|