@oneuptime/common 12.0.18 → 12.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/EnterpriseLicense.ts +23 -0
- package/Models/DatabaseModels/Index.ts +4 -2
- package/Models/DatabaseModels/Monitor.ts +39 -9
- package/Models/DatabaseModels/UserMicrosoftTeams.ts +344 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +144 -8
- package/Models/DatabaseModels/UserNotificationSetting.ts +34 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +95 -0
- package/Models/DatabaseModels/UserSlack.ts +341 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +9 -0
- package/Server/API/EnterpriseLicenseAPI.ts +94 -19
- package/Server/API/UserMicrosoftTeamsAPI.ts +127 -0
- package/Server/API/UserSlackAPI.ts +125 -0
- package/Server/EnvironmentConfig.ts +29 -107
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Infrastructure/Queue.ts +7 -0
- package/Server/Services/EnterpriseLicenseService.ts +60 -0
- package/Server/Services/Index.ts +6 -2
- package/Server/Services/LlmLogService.ts +25 -9
- package/Server/Services/OnCallReadinessService.ts +86 -2
- package/Server/Services/ProjectService.ts +171 -0
- package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
- package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
- package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
- package/Server/Services/UserNotificationRuleService.ts +589 -11
- package/Server/Services/UserNotificationSettingService.ts +138 -0
- package/Server/Services/UserService.ts +28 -0
- package/Server/Services/UserSlackService.ts +218 -0
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
- package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
- package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
- package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
- package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
- package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
- package/Server/Utils/Response.ts +2 -2
- package/Server/Utils/StartServer.ts +2 -2
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
- package/Server/Views/Partials/AnalyticsConsent.ejs +533 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
- package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +465 -0
- package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +302 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +408 -0
- package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +474 -0
- package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +368 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
- package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +763 -0
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
- package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
- package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
- package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +138 -0
- package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +516 -0
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
- package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
- package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
- package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
- package/Tests/Server/Services/UserSlackService.test.ts +407 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
- package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
- package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
- package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
- package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
- package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
- package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +53 -0
- package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
- package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
- package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
- package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +241 -0
- package/Tests/UI/Components/Charts/ChartTooltipSorted.test.tsx +129 -0
- package/Tests/UI/Components/Charts/TooltipEntries.test.ts +155 -0
- package/Tests/UI/Components/Detail/DetailFieldErrors.test.tsx +347 -0
- package/Tests/UI/Components/Forms/BasicFormStepValidation.test.tsx +392 -0
- package/Tests/UI/Components/Forms/ValidationFormSteps.test.ts +549 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
- package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
- package/Tests/Utils/EnterpriseLicenseSync.test.ts +562 -0
- package/Types/Analytics/RevenueEvent.ts +1 -0
- package/Types/Marketing/MarketingEvent.ts +140 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +8 -1
- package/UI/Components/Charts/Bar/BarChart.tsx +8 -1
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +19 -4
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +20 -2
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +19 -4
- package/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.ts +105 -0
- package/UI/Components/Charts/Line/LineChart.tsx +8 -1
- package/UI/Components/Detail/Detail.tsx +18 -1
- package/UI/Utils/NotificationMethodUtil.ts +41 -1
- package/UI/Utils/PermissionGate.ts +56 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +244 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -2
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
- package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +66 -12
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
- package/build/dist/Server/API/UserSlackAPI.js +81 -0
- package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +25 -70
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +7 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +6 -2
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmLogService.js +25 -9
- package/build/dist/Server/Services/LlmLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +144 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +26 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSlackService.js +195 -0
- package/build/dist/Server/Services/UserSlackService.js.map +1 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Response.js +2 -2
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +2 -2
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +1 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -1
- package/build/dist/Types/Marketing/MarketingEvent.js +82 -0
- package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +9 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +9 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +22 -13
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +27 -15
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +27 -18
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js +71 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +9 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Detail/Detail.js +17 -1
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +32 -0
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +174 -0
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +1 -0
- package/package.json +1 -1
- package/Models/DatabaseModels/MarketingConversion.ts +0 -410
- package/Server/Services/MarketingConversionService.ts +0 -10
- package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
- package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
- package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
- package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
- package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
- package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
- package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
- package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
- package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
- package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
- package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
- package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
- package/Types/Marketing/MarketingConversion.ts +0 -53
- package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
- package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
- package/build/dist/Server/Services/MarketingConversionService.js +0 -9
- package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
- package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
- package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
|
@@ -16,6 +16,8 @@ import UpdateBy from "../Types/Database/UpdateBy";
|
|
|
16
16
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
17
17
|
import Errors from "../Utils/Errors";
|
|
18
18
|
import ProductAnalytics from "../Utils/ProductAnalytics";
|
|
19
|
+
import MarketingEventUtil from "../Utils/Marketing/MarketingEventUtil";
|
|
20
|
+
import { MarketingEventType } from "../../Types/Marketing/MarketingEvent";
|
|
19
21
|
import SessionReplayGateCacheStore from "../Utils/SessionReplay/SessionReplayGateCacheStore";
|
|
20
22
|
import AccessTokenService from "./AccessTokenService";
|
|
21
23
|
import BillingService from "./BillingService";
|
|
@@ -558,10 +560,21 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
558
560
|
trialEndsAt: true,
|
|
559
561
|
paymentProviderCustomerId: true,
|
|
560
562
|
createdOwnerEmail: true,
|
|
563
|
+
/*
|
|
564
|
+
* The full attribution set, not just the three UTMs the analytics
|
|
565
|
+
* properties carry: the outbound plan-change conversion is the only
|
|
566
|
+
* place the campaign that won a customer is ever reported, and a
|
|
567
|
+
* column left unselected reaches the receiver as empty rather than as
|
|
568
|
+
* an error.
|
|
569
|
+
*/
|
|
561
570
|
utmSource: true,
|
|
562
571
|
utmMedium: true,
|
|
563
572
|
utmCampaign: true,
|
|
573
|
+
utmTerm: true,
|
|
574
|
+
utmContent: true,
|
|
575
|
+
utmUrl: true,
|
|
564
576
|
clickIds: true,
|
|
577
|
+
firstTouchAttribution: true,
|
|
565
578
|
},
|
|
566
579
|
props: {
|
|
567
580
|
isRoot: true,
|
|
@@ -884,6 +897,98 @@ These are no longer recorded against the project and have to be cancelled by han
|
|
|
884
897
|
);
|
|
885
898
|
}
|
|
886
899
|
|
|
900
|
+
/*
|
|
901
|
+
* The first-subscription conversion.
|
|
902
|
+
*
|
|
903
|
+
* A project created directly on a paid plan never passes through changePlan,
|
|
904
|
+
* so the plan-change pass below never sees it — and could not report it if
|
|
905
|
+
* it did, because direction is decided by plan ORDER and a first plan has no
|
|
906
|
+
* previous tier to compare against. That left the most bid-worthy self-serve
|
|
907
|
+
* conversion there is emitting nothing at all: a visitor could click an ad,
|
|
908
|
+
* sign up, pick Growth and pay, and no channel carried a single event.
|
|
909
|
+
*
|
|
910
|
+
* It is its own event rather than an upgrade with an empty old_plan because
|
|
911
|
+
* this is new business and an upgrade is expansion. Mixing them is a
|
|
912
|
+
* distinction nothing downstream could restore.
|
|
913
|
+
*
|
|
914
|
+
* Free projects reach here too — a project on the free plan is still
|
|
915
|
+
* subscribed at the payment provider — so the paid check is what keeps this
|
|
916
|
+
* a revenue event.
|
|
917
|
+
*/
|
|
918
|
+
private captureSubscriptionStartedAnalytics(data: {
|
|
919
|
+
project: Model;
|
|
920
|
+
plan: SubscriptionPlan;
|
|
921
|
+
planId: string;
|
|
922
|
+
seats: number;
|
|
923
|
+
}): void {
|
|
924
|
+
if (!data.project.createdOwnerEmail) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Per-month amount, or null when unknown (custom pricing / unknown plan).
|
|
929
|
+
const monthlyAmountInUSD: number | null = data.plan.isCustomPricing()
|
|
930
|
+
? null
|
|
931
|
+
: data.plan.getYearlyPlanId() === data.planId
|
|
932
|
+
? data.plan.getYearlySubscriptionAmountInUSD()
|
|
933
|
+
: data.plan.getMonthlySubscriptionAmountInUSD();
|
|
934
|
+
|
|
935
|
+
const planIsPaid: boolean =
|
|
936
|
+
data.plan.isCustomPricing() ||
|
|
937
|
+
(monthlyAmountInUSD !== null && monthlyAmountInUSD > 0);
|
|
938
|
+
|
|
939
|
+
if (!planIsPaid) {
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const properties: JSONObject = {
|
|
944
|
+
project_id: data.project.id?.toString() || "",
|
|
945
|
+
new_plan: data.plan.getName(),
|
|
946
|
+
seats: data.seats,
|
|
947
|
+
/*
|
|
948
|
+
* Always true. The project went straight from not existing to paying,
|
|
949
|
+
* which is the acquisition this event exists to report.
|
|
950
|
+
*/
|
|
951
|
+
is_paid_conversion: true,
|
|
952
|
+
has_custom_pricing: data.plan.isCustomPricing(),
|
|
953
|
+
utm_source: data.project.utmSource || "",
|
|
954
|
+
utm_medium: data.project.utmMedium || "",
|
|
955
|
+
utm_campaign: data.project.utmCampaign || "",
|
|
956
|
+
utm_term: data.project.utmTerm || "",
|
|
957
|
+
utm_content: data.project.utmContent || "",
|
|
958
|
+
click_ids: data.project.clickIds || {},
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
if (monthlyAmountInUSD !== null) {
|
|
962
|
+
properties["new_monthly_amount_in_usd"] = monthlyAmountInUSD;
|
|
963
|
+
// Value for ROAS reporting: monthly recurring revenue at the start.
|
|
964
|
+
properties["value"] = monthlyAmountInUSD * data.seats;
|
|
965
|
+
properties["currency"] = "USD";
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
ProductAnalytics.capture({
|
|
969
|
+
event: "server/subscription_started",
|
|
970
|
+
distinctId: data.project.createdOwnerEmail.toString(),
|
|
971
|
+
properties: properties,
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
MarketingEventUtil.emitInBackground(
|
|
975
|
+
MarketingEventUtil.buildEvent({
|
|
976
|
+
eventType: MarketingEventType.SubscriptionStarted,
|
|
977
|
+
/*
|
|
978
|
+
* Naturally unique: a project has exactly one first subscription, so a
|
|
979
|
+
* queue retry carries the identical id and the receiver dedupes it.
|
|
980
|
+
* Unlike a plan change, this needs no timestamp to separate it from
|
|
981
|
+
* the next one — there is no next one.
|
|
982
|
+
*/
|
|
983
|
+
eventId: `${MarketingEventType.SubscriptionStarted}:${data.project.id?.toString()}`,
|
|
984
|
+
occurredAt: data.project.createdAt || new Date(),
|
|
985
|
+
email: data.project.createdOwnerEmail.toString(),
|
|
986
|
+
attributionSource: data.project,
|
|
987
|
+
data: properties,
|
|
988
|
+
}),
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
|
|
887
992
|
/*
|
|
888
993
|
* The paid-conversion event for ad platforms: fires server-side (immune to
|
|
889
994
|
* ad blockers) whenever a project's subscription plan changes, with enough
|
|
@@ -982,6 +1087,60 @@ These are no longer recorded against the project and have to be cancelled by han
|
|
|
982
1087
|
distinctId: data.project.createdOwnerEmail.toString(),
|
|
983
1088
|
properties: properties,
|
|
984
1089
|
});
|
|
1090
|
+
|
|
1091
|
+
this.emitPlanChangeMarketingEvent({
|
|
1092
|
+
project: data.project,
|
|
1093
|
+
properties: properties,
|
|
1094
|
+
isUpgrade: properties["is_upgrade"] === true,
|
|
1095
|
+
isDowngrade: properties["is_downgrade"] === true,
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/*
|
|
1100
|
+
* The outbound subscription_upgraded / subscription_downgraded conversions.
|
|
1101
|
+
*
|
|
1102
|
+
* Rides on the analytics pass above rather than on the plan-change path
|
|
1103
|
+
* directly, because that is where "is this an upgrade" is already decided —
|
|
1104
|
+
* by plan ORDER, not price, so a monthly-to-yearly switch at the same tier
|
|
1105
|
+
* is neither. Duplicating that comparison here is how the two would
|
|
1106
|
+
* eventually disagree about what a customer did.
|
|
1107
|
+
*
|
|
1108
|
+
* Only real tier movements are emitted. A first plan on a project with no
|
|
1109
|
+
* previous one has no direction (both flags read false, since oldPlanOrder
|
|
1110
|
+
* is null), and an interval change is not a conversion — sending either as
|
|
1111
|
+
* an upgrade would inflate exactly the number this exists to report.
|
|
1112
|
+
*/
|
|
1113
|
+
private emitPlanChangeMarketingEvent(data: {
|
|
1114
|
+
project: Model;
|
|
1115
|
+
properties: JSONObject;
|
|
1116
|
+
isUpgrade: boolean;
|
|
1117
|
+
isDowngrade: boolean;
|
|
1118
|
+
}): void {
|
|
1119
|
+
if (!data.isUpgrade && !data.isDowngrade) {
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
const eventType: MarketingEventType = data.isUpgrade
|
|
1124
|
+
? MarketingEventType.SubscriptionUpgraded
|
|
1125
|
+
: MarketingEventType.SubscriptionDowngraded;
|
|
1126
|
+
|
|
1127
|
+
const occurredAt: Date = new Date();
|
|
1128
|
+
|
|
1129
|
+
MarketingEventUtil.emitInBackground(
|
|
1130
|
+
MarketingEventUtil.buildEvent({
|
|
1131
|
+
eventType: eventType,
|
|
1132
|
+
/*
|
|
1133
|
+
* A project can legitimately upgrade, downgrade and upgrade again, so
|
|
1134
|
+
* unlike a signup or a booking there is no naturally unique key —
|
|
1135
|
+
* the instant is what separates one change from the next.
|
|
1136
|
+
*/
|
|
1137
|
+
eventId: `${eventType}:${data.project.id?.toString()}:${occurredAt.toISOString()}`,
|
|
1138
|
+
occurredAt: occurredAt,
|
|
1139
|
+
email: data.project.createdOwnerEmail?.toString(),
|
|
1140
|
+
attributionSource: data.project,
|
|
1141
|
+
data: data.properties,
|
|
1142
|
+
}),
|
|
1143
|
+
);
|
|
985
1144
|
}
|
|
986
1145
|
|
|
987
1146
|
private async sendSubscriptionChangeWebhookSlackNotification(
|
|
@@ -1219,6 +1378,18 @@ These are no longer recorded against the project and have to be cancelled by han
|
|
|
1219
1378
|
},
|
|
1220
1379
|
});
|
|
1221
1380
|
|
|
1381
|
+
/*
|
|
1382
|
+
* Raised after the row is written, mirroring changePlan: the write is
|
|
1383
|
+
* what makes the subscription real to OneUptime, and the conversion
|
|
1384
|
+
* describes a subscription that exists.
|
|
1385
|
+
*/
|
|
1386
|
+
this.captureSubscriptionStartedAnalytics({
|
|
1387
|
+
project: createdItem,
|
|
1388
|
+
plan: plan,
|
|
1389
|
+
planId: createdItem.paymentProviderPlanId!,
|
|
1390
|
+
seats: 1,
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1222
1393
|
// mark the promo code as used it it exists.
|
|
1223
1394
|
|
|
1224
1395
|
if (createdItem.paymentProviderPromoCode) {
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
2
|
+
import DeleteBy from "../Types/Database/DeleteBy";
|
|
3
|
+
import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
|
4
|
+
import DatabaseService from "./DatabaseService";
|
|
5
|
+
import UserNotificationRuleService, {
|
|
6
|
+
NotificationDeletionImpact,
|
|
7
|
+
NotificationMethodChannel,
|
|
8
|
+
} from "./UserNotificationRuleService";
|
|
9
|
+
import WorkspaceProjectAuthTokenService from "./WorkspaceProjectAuthTokenService";
|
|
10
|
+
import WorkspaceUserAuthTokenService from "./WorkspaceUserAuthTokenService";
|
|
11
|
+
import logger from "../Utils/Logger";
|
|
12
|
+
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
13
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
14
|
+
import { JSONObject } from "../../Types/JSON";
|
|
15
|
+
import ObjectID from "../../Types/ObjectID";
|
|
16
|
+
import WorkspaceType from "../../Types/Workspace/WorkspaceType";
|
|
17
|
+
import WorkspaceProjectAuthToken from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
18
|
+
import WorkspaceUserAuthToken from "../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
19
|
+
import Model from "../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
20
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
21
|
+
|
|
22
|
+
export class Service extends DatabaseService<Model> {
|
|
23
|
+
public constructor() {
|
|
24
|
+
super(Model);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@CaptureSpan()
|
|
28
|
+
protected override async onBeforeDelete(
|
|
29
|
+
deleteBy: DeleteBy<Model>,
|
|
30
|
+
): Promise<OnDelete<Model>> {
|
|
31
|
+
const itemsToDelete: Array<Model> = await this.findBy({
|
|
32
|
+
query: deleteBy.query,
|
|
33
|
+
select: {
|
|
34
|
+
_id: true,
|
|
35
|
+
projectId: true,
|
|
36
|
+
},
|
|
37
|
+
skip: 0,
|
|
38
|
+
limit: LIMIT_MAX,
|
|
39
|
+
props: {
|
|
40
|
+
isRoot: true,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
for (const item of itemsToDelete) {
|
|
45
|
+
await UserNotificationRuleService.deleteBy({
|
|
46
|
+
query: {
|
|
47
|
+
userMicrosoftTeamsId: item.id!,
|
|
48
|
+
projectId: item.projectId!,
|
|
49
|
+
},
|
|
50
|
+
limit: LIMIT_MAX,
|
|
51
|
+
skip: 0,
|
|
52
|
+
props: {
|
|
53
|
+
isRoot: true,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
deleteBy,
|
|
60
|
+
carryForward: null,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* What this user would lose if this Microsoft Teams account were deleted.
|
|
66
|
+
* Ask BEFORE calling delete; nothing here refuses anything.
|
|
67
|
+
*
|
|
68
|
+
* The hook directly above deletes every UserNotificationRule that points at
|
|
69
|
+
* this account, and the foreign key is onDelete: "CASCADE" so the rows would
|
|
70
|
+
* go even if it did not.
|
|
71
|
+
*/
|
|
72
|
+
@CaptureSpan()
|
|
73
|
+
public async getDeletionImpact(data: {
|
|
74
|
+
itemId: ObjectID;
|
|
75
|
+
projectId: ObjectID;
|
|
76
|
+
}): Promise<NotificationDeletionImpact> {
|
|
77
|
+
return UserNotificationRuleService.getNotificationMethodDeletionImpact({
|
|
78
|
+
projectId: data.projectId,
|
|
79
|
+
methodType: NotificationMethodChannel.MicrosoftTeams,
|
|
80
|
+
methodId: data.itemId,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* A Microsoft Teams notification method is a POINTER at the user's existing
|
|
86
|
+
* workspace link, not a hand-typed address: the Microsoft Entra user id is
|
|
87
|
+
* resolved from the user's own WorkspaceUserAuthToken and never accepted
|
|
88
|
+
* from the request. That is why creation IS verification — the OAuth dance
|
|
89
|
+
* that wrote the auth-token row already proved the person controls that
|
|
90
|
+
* Microsoft Teams account.
|
|
91
|
+
*/
|
|
92
|
+
@CaptureSpan()
|
|
93
|
+
protected override async onBeforeCreate(
|
|
94
|
+
createBy: CreateBy<Model>,
|
|
95
|
+
): Promise<OnCreate<Model>> {
|
|
96
|
+
if (!createBy.props.isRoot && createBy.data.isVerified) {
|
|
97
|
+
throw new BadDataException("isVerified cannot be set to true");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!createBy.props.isRoot && createBy.data.microsoftTeamsUserId) {
|
|
101
|
+
throw new BadDataException("microsoftTeamsUserId cannot be set directly");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!createBy.data.projectId || !createBy.data.userId) {
|
|
105
|
+
throw new BadDataException("projectId and userId are required");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const projectId: ObjectID = new ObjectID(
|
|
109
|
+
createBy.data.projectId.toString(),
|
|
110
|
+
);
|
|
111
|
+
const userId: ObjectID = new ObjectID(createBy.data.userId.toString());
|
|
112
|
+
|
|
113
|
+
/*
|
|
114
|
+
* One workspace link per (user, project) means a second row would be an
|
|
115
|
+
* exact duplicate of the first.
|
|
116
|
+
*/
|
|
117
|
+
const existingCount: number = (
|
|
118
|
+
await this.countBy({
|
|
119
|
+
query: {
|
|
120
|
+
projectId: projectId,
|
|
121
|
+
userId: userId,
|
|
122
|
+
},
|
|
123
|
+
props: {
|
|
124
|
+
isRoot: true,
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
).toNumber();
|
|
128
|
+
|
|
129
|
+
if (existingCount > 0) {
|
|
130
|
+
throw new BadDataException(
|
|
131
|
+
"Microsoft Teams is already added as a notification method for this project.",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const projectAuth: WorkspaceProjectAuthToken | null =
|
|
136
|
+
await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
137
|
+
projectId: projectId,
|
|
138
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!projectAuth || !projectAuth.authToken) {
|
|
142
|
+
throw new BadDataException(
|
|
143
|
+
"This project is not connected to Microsoft Teams. Please ask a project admin to connect Microsoft Teams in Project Settings > Microsoft Teams Integration.",
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const userAuth: WorkspaceUserAuthToken | null =
|
|
148
|
+
await WorkspaceUserAuthTokenService.getUserAuth({
|
|
149
|
+
projectId: projectId,
|
|
150
|
+
userId: userId,
|
|
151
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (!userAuth || !userAuth.workspaceUserId) {
|
|
155
|
+
throw new BadDataException(
|
|
156
|
+
"Your Microsoft Teams account is not connected to OneUptime for this project. Please go to User Settings > Microsoft Teams Integration and connect your Microsoft Teams account first.",
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
createBy.data.microsoftTeamsUserId = userAuth.workspaceUserId;
|
|
161
|
+
createBy.data.isVerified = true;
|
|
162
|
+
|
|
163
|
+
/* Display label only — captured when the user linked their account. */
|
|
164
|
+
const displayName: string | undefined = (
|
|
165
|
+
userAuth.miscData as JSONObject | undefined
|
|
166
|
+
)?.["displayName"] as string | undefined;
|
|
167
|
+
|
|
168
|
+
if (displayName) {
|
|
169
|
+
createBy.data.microsoftTeamsUserName = displayName;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
createBy,
|
|
174
|
+
carryForward: null,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
@CaptureSpan()
|
|
179
|
+
protected override async onCreateSuccess(
|
|
180
|
+
_onCreate: OnCreate<Model>,
|
|
181
|
+
createdItem: Model,
|
|
182
|
+
): Promise<Model> {
|
|
183
|
+
/*
|
|
184
|
+
* The row is born verified (creation is gated on the live workspace
|
|
185
|
+
* link), so default on-call rules are seeded at create time — same as
|
|
186
|
+
* webhooks.
|
|
187
|
+
*/
|
|
188
|
+
if (createdItem.projectId && createdItem.userId && createdItem.id) {
|
|
189
|
+
try {
|
|
190
|
+
await UserNotificationRuleService.addDefaultNotificationRulesForVerifiedMethod(
|
|
191
|
+
{
|
|
192
|
+
projectId: createdItem.projectId,
|
|
193
|
+
userId: createdItem.userId,
|
|
194
|
+
notificationMethod: {
|
|
195
|
+
userMicrosoftTeamsId: createdItem.id,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
logger.error(err);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return createdItem;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export default new Service();
|
|
@@ -17,6 +17,8 @@ import UserPushService from "./UserPushService";
|
|
|
17
17
|
import UserService from "./UserService";
|
|
18
18
|
import UserSmsService from "./UserSmsService";
|
|
19
19
|
import UserTelegramService from "./UserTelegramService";
|
|
20
|
+
import UserSlackService from "./UserSlackService";
|
|
21
|
+
import UserMicrosoftTeamsService from "./UserMicrosoftTeamsService";
|
|
20
22
|
import UserWebhookService from "./UserWebhookService";
|
|
21
23
|
import UserWhatsAppService from "./UserWhatsAppService";
|
|
22
24
|
import type AuditLogServiceType from "./AuditLogService";
|
|
@@ -42,6 +44,8 @@ import UserEmail from "../../Models/DatabaseModels/UserEmail";
|
|
|
42
44
|
import UserPush from "../../Models/DatabaseModels/UserPush";
|
|
43
45
|
import UserSMS from "../../Models/DatabaseModels/UserSMS";
|
|
44
46
|
import UserTelegram from "../../Models/DatabaseModels/UserTelegram";
|
|
47
|
+
import UserSlack from "../../Models/DatabaseModels/UserSlack";
|
|
48
|
+
import UserMicrosoftTeams from "../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
45
49
|
import UserWebhook from "../../Models/DatabaseModels/UserWebhook";
|
|
46
50
|
import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
|
|
47
51
|
|
|
@@ -66,7 +70,8 @@ import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
|
|
|
66
70
|
* filters, sort columns appended after the guard had run. The models are still
|
|
67
71
|
* CurrentUser-only. Nothing in this file changes that, and an administrator's
|
|
68
72
|
* every direct read or write of UserEmail, UserSMS, UserCall, UserWhatsApp,
|
|
69
|
-
* UserTelegram, UserPush and UserWebhook is
|
|
73
|
+
* UserTelegram, UserSlack, UserMicrosoftTeams, UserPush and UserWebhook is
|
|
74
|
+
* still refused.
|
|
70
75
|
*
|
|
71
76
|
* What this file does instead is a NARROW, SERVER-SIDE, AUDITED capability
|
|
72
77
|
* that runs as root on the admin's behalf and never hands them the row:
|
|
@@ -88,7 +93,7 @@ import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
|
|
|
88
93
|
* notification methods page as an unverified method they did not add — on
|
|
89
94
|
* top of the mail this service sends them.
|
|
90
95
|
*
|
|
91
|
-
* - REMOVE is allowed on all
|
|
96
|
+
* - REMOVE is allowed on all nine channels, because offboarding a stale
|
|
92
97
|
* device is a real administrative job and the owner may well be gone. It is
|
|
93
98
|
* the destructive direction, so it is the one with a preview attached:
|
|
94
99
|
* UserNotificationRuleService.getNotificationMethodDeletionImpact says how
|
|
@@ -100,7 +105,10 @@ import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
|
|
|
100
105
|
* omissions are not oversights. Push is a device token minted by a browser
|
|
101
106
|
* or a phone at registration time; there is nothing an admin could type.
|
|
102
107
|
* Telegram needs the account holder to message the bot before a chat id
|
|
103
|
-
* exists.
|
|
108
|
+
* exists. Slack and Microsoft Teams are pointers at the owner's own OAuth
|
|
109
|
+
* workspace link, which only the owner can establish — and an admin-created
|
|
110
|
+
* row would be live immediately, since creation is verification for those
|
|
111
|
+
* two. Webhook is the one that is deliberately refused rather than
|
|
104
112
|
* merely impractical: UserWebhook has no isVerified column at all, so an
|
|
105
113
|
* admin-created webhook would be live the moment it was written, which is
|
|
106
114
|
* exactly the silent-redirect this whole design is built to prevent.
|
|
@@ -139,7 +147,7 @@ export interface AdminNotificationMethodView {
|
|
|
139
147
|
|
|
140
148
|
/**
|
|
141
149
|
* The four channels an administrator may CREATE. See the header for why the
|
|
142
|
-
* other
|
|
150
|
+
* other five are absent; this enum is the single place that decides, and both
|
|
143
151
|
* the API's validation and the service's own dispatch read it.
|
|
144
152
|
*/
|
|
145
153
|
export enum AdminAddableChannel {
|
|
@@ -751,6 +759,160 @@ export class UserNotificationMethodAdminService extends BaseService {
|
|
|
751
759
|
});
|
|
752
760
|
},
|
|
753
761
|
},
|
|
762
|
+
{
|
|
763
|
+
methodType: ReadinessMethodType.Slack,
|
|
764
|
+
isAdminAddable: false,
|
|
765
|
+
modelType: UserSlack,
|
|
766
|
+
list: async (data: {
|
|
767
|
+
projectId: ObjectID;
|
|
768
|
+
userId: ObjectID;
|
|
769
|
+
}): Promise<Array<AdminNotificationMethodView>> => {
|
|
770
|
+
const rows: Array<UserSlack> = await UserSlackService.findBy({
|
|
771
|
+
query: { projectId: data.projectId, userId: data.userId },
|
|
772
|
+
/*
|
|
773
|
+
* The username only — never slackUserId, which is the addressable
|
|
774
|
+
* target the bot sends to. Same rule OnCallReadinessService
|
|
775
|
+
* follows.
|
|
776
|
+
*/
|
|
777
|
+
select: {
|
|
778
|
+
_id: true,
|
|
779
|
+
slackUserName: true,
|
|
780
|
+
isVerified: true,
|
|
781
|
+
createdAt: true,
|
|
782
|
+
},
|
|
783
|
+
sort: { createdAt: SortOrder.Ascending },
|
|
784
|
+
skip: 0,
|
|
785
|
+
limit: LIMIT_PER_PROJECT,
|
|
786
|
+
props: { isRoot: true },
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
return rows.map((row: UserSlack): AdminNotificationMethodView => {
|
|
790
|
+
return this.toView({
|
|
791
|
+
row: row,
|
|
792
|
+
methodType: ReadinessMethodType.Slack,
|
|
793
|
+
isAdminAddable: false,
|
|
794
|
+
identifier: row.slackUserName,
|
|
795
|
+
kind: MaskedIdentifierKind.Handle,
|
|
796
|
+
isVerified: row.isVerified,
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
},
|
|
800
|
+
findOwnedRow: async (data: {
|
|
801
|
+
methodId: ObjectID;
|
|
802
|
+
projectId: ObjectID;
|
|
803
|
+
userId: ObjectID;
|
|
804
|
+
}): Promise<AdminNotificationMethodView | null> => {
|
|
805
|
+
const row: UserSlack | null = await UserSlackService.findOneBy({
|
|
806
|
+
query: {
|
|
807
|
+
_id: data.methodId,
|
|
808
|
+
projectId: data.projectId,
|
|
809
|
+
userId: data.userId,
|
|
810
|
+
},
|
|
811
|
+
select: {
|
|
812
|
+
_id: true,
|
|
813
|
+
slackUserName: true,
|
|
814
|
+
isVerified: true,
|
|
815
|
+
},
|
|
816
|
+
props: { isRoot: true },
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
return row
|
|
820
|
+
? this.toView({
|
|
821
|
+
row: row,
|
|
822
|
+
methodType: ReadinessMethodType.Slack,
|
|
823
|
+
isAdminAddable: false,
|
|
824
|
+
identifier: row.slackUserName,
|
|
825
|
+
kind: MaskedIdentifierKind.Handle,
|
|
826
|
+
isVerified: row.isVerified,
|
|
827
|
+
})
|
|
828
|
+
: null;
|
|
829
|
+
},
|
|
830
|
+
deleteRow: async (data: { methodId: ObjectID }): Promise<void> => {
|
|
831
|
+
await UserSlackService.deleteOneById({
|
|
832
|
+
id: data.methodId,
|
|
833
|
+
props: { isRoot: true },
|
|
834
|
+
});
|
|
835
|
+
},
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
methodType: ReadinessMethodType.MicrosoftTeams,
|
|
839
|
+
isAdminAddable: false,
|
|
840
|
+
modelType: UserMicrosoftTeams,
|
|
841
|
+
list: async (data: {
|
|
842
|
+
projectId: ObjectID;
|
|
843
|
+
userId: ObjectID;
|
|
844
|
+
}): Promise<Array<AdminNotificationMethodView>> => {
|
|
845
|
+
const rows: Array<UserMicrosoftTeams> =
|
|
846
|
+
await UserMicrosoftTeamsService.findBy({
|
|
847
|
+
query: { projectId: data.projectId, userId: data.userId },
|
|
848
|
+
/*
|
|
849
|
+
* The display name only — never microsoftTeamsUserId, which is
|
|
850
|
+
* the addressable target the bot sends to. Same rule
|
|
851
|
+
* OnCallReadinessService follows.
|
|
852
|
+
*/
|
|
853
|
+
select: {
|
|
854
|
+
_id: true,
|
|
855
|
+
microsoftTeamsUserName: true,
|
|
856
|
+
isVerified: true,
|
|
857
|
+
createdAt: true,
|
|
858
|
+
},
|
|
859
|
+
sort: { createdAt: SortOrder.Ascending },
|
|
860
|
+
skip: 0,
|
|
861
|
+
limit: LIMIT_PER_PROJECT,
|
|
862
|
+
props: { isRoot: true },
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
return rows.map(
|
|
866
|
+
(row: UserMicrosoftTeams): AdminNotificationMethodView => {
|
|
867
|
+
return this.toView({
|
|
868
|
+
row: row,
|
|
869
|
+
methodType: ReadinessMethodType.MicrosoftTeams,
|
|
870
|
+
isAdminAddable: false,
|
|
871
|
+
identifier: row.microsoftTeamsUserName,
|
|
872
|
+
kind: MaskedIdentifierKind.Handle,
|
|
873
|
+
isVerified: row.isVerified,
|
|
874
|
+
});
|
|
875
|
+
},
|
|
876
|
+
);
|
|
877
|
+
},
|
|
878
|
+
findOwnedRow: async (data: {
|
|
879
|
+
methodId: ObjectID;
|
|
880
|
+
projectId: ObjectID;
|
|
881
|
+
userId: ObjectID;
|
|
882
|
+
}): Promise<AdminNotificationMethodView | null> => {
|
|
883
|
+
const row: UserMicrosoftTeams | null =
|
|
884
|
+
await UserMicrosoftTeamsService.findOneBy({
|
|
885
|
+
query: {
|
|
886
|
+
_id: data.methodId,
|
|
887
|
+
projectId: data.projectId,
|
|
888
|
+
userId: data.userId,
|
|
889
|
+
},
|
|
890
|
+
select: {
|
|
891
|
+
_id: true,
|
|
892
|
+
microsoftTeamsUserName: true,
|
|
893
|
+
isVerified: true,
|
|
894
|
+
},
|
|
895
|
+
props: { isRoot: true },
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
return row
|
|
899
|
+
? this.toView({
|
|
900
|
+
row: row,
|
|
901
|
+
methodType: ReadinessMethodType.MicrosoftTeams,
|
|
902
|
+
isAdminAddable: false,
|
|
903
|
+
identifier: row.microsoftTeamsUserName,
|
|
904
|
+
kind: MaskedIdentifierKind.Handle,
|
|
905
|
+
isVerified: row.isVerified,
|
|
906
|
+
})
|
|
907
|
+
: null;
|
|
908
|
+
},
|
|
909
|
+
deleteRow: async (data: { methodId: ObjectID }): Promise<void> => {
|
|
910
|
+
await UserMicrosoftTeamsService.deleteOneById({
|
|
911
|
+
id: data.methodId,
|
|
912
|
+
props: { isRoot: true },
|
|
913
|
+
});
|
|
914
|
+
},
|
|
915
|
+
},
|
|
754
916
|
{
|
|
755
917
|
methodType: ReadinessMethodType.Webhook,
|
|
756
918
|
isAdminAddable: false,
|
|
@@ -966,7 +1128,7 @@ export class UserNotificationMethodAdminService extends BaseService {
|
|
|
966
1128
|
* the feature to somebody probing it.
|
|
967
1129
|
*/
|
|
968
1130
|
throw new BadDataException(
|
|
969
|
-
`A project administrator can only add Email, SMS, Call and WhatsApp notification methods for another user. Push, Telegram and webhook methods have to be added by the person who owns the device.`,
|
|
1131
|
+
`A project administrator can only add Email, SMS, Call and WhatsApp notification methods for another user. Push, Telegram, Slack, Microsoft Teams and webhook methods have to be added by the person who owns the device or account.`,
|
|
970
1132
|
);
|
|
971
1133
|
}
|
|
972
1134
|
|