@oneuptime/common 12.0.12 → 12.0.13
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/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
- package/Models/DatabaseModels/NetworkDevice.ts +64 -0
- package/Models/DatabaseModels/StatusPage.ts +59 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
- package/Server/API/AIChatAPI.ts +55 -56
- package/Server/API/CommonAPI.ts +89 -0
- package/Server/API/MicrosoftTeamsAPI.ts +20 -9
- package/Server/API/SlackAPI.ts +13 -10
- package/Server/API/StatusPageAPI.ts +2197 -2128
- package/Server/API/TelemetryAPI.ts +72 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +11 -2
- package/Server/Services/ApiKeyPermissionService.ts +116 -2
- package/Server/Services/DatabaseService.ts +17 -6
- package/Server/Services/LogAggregationService.ts +34 -0
- package/Server/Services/LogService.ts +21 -0
- package/Server/Services/NetworkSiteService.ts +12 -0
- package/Server/Services/SpanService.ts +21 -0
- package/Server/Services/TraceAggregationService.ts +15 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
- package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
- package/Server/Utils/APIKey/AccessPermission.ts +16 -40
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
- package/Server/Utils/LogRedaction.ts +576 -0
- package/Server/Utils/Logger.ts +123 -46
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
- package/Server/Utils/Monitor/MonitorResource.ts +29 -9
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
- package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
- package/Server/Utils/StartServer.ts +30 -9
- package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
- package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
- package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
- package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
- package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
- package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
- package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
- package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
- package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
- package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
- package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
- package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
- package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
- package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
- package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
- package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
- package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
- package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
- package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
- package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
- package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
- package/Tests/UI/Components/MasterPage.test.tsx +36 -1
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
- package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
- package/Types/API/Hostname.ts +79 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
- package/Types/Database/DatabaseProperty.ts +38 -0
- package/Types/Database/TableColumn.ts +34 -7
- package/Types/Events/Recurring.ts +16 -1
- package/Types/Monitor/MonitorStep.ts +13 -1
- package/Types/Monitor/MonitorType.ts +345 -40
- package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
- package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
- package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
- package/UI/Components/CardSelect/CardSelect.tsx +714 -105
- package/UI/Components/EmptyState/EmptyState.tsx +8 -1
- package/UI/Components/Footer/Footer.tsx +32 -6
- package/UI/Components/Forms/Fields/FormField.tsx +3 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Loader/PageLoader.tsx +29 -8
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
- package/UI/Components/MasterPage/MasterPage.tsx +24 -11
- package/Utils/Monitor/MonitorMetricType.ts +16 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
- package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
- package/Utils/Telemetry/CrossSignalScope.ts +75 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +48 -35
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +59 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +13 -10
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +745 -673
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +34 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +15 -6
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +2 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogService.js +16 -0
- package/build/dist/Server/Services/LogService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +12 -0
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/SpanService.js +16 -0
- package/build/dist/Server/Services/SpanService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +2 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +449 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Logger.js +98 -45
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +10 -7
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +63 -0
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +38 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js +24 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +13 -1
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +9 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +330 -31
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/Footer/Footer.js +7 -4
- package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
- package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
import WorkspaceNotificationRuleService from "../../../Server/Services/WorkspaceNotificationRuleService";
|
|
2
|
+
import WorkspaceProjectAuthTokenService from "../../../Server/Services/WorkspaceProjectAuthTokenService";
|
|
3
|
+
import WorkspaceUserAuthTokenService from "../../../Server/Services/WorkspaceUserAuthTokenService";
|
|
4
|
+
import WorkspaceNotificationLogService from "../../../Server/Services/WorkspaceNotificationLogService";
|
|
5
|
+
import WorkspaceUtil from "../../../Server/Utils/Workspace/Workspace";
|
|
6
|
+
import {
|
|
7
|
+
WorkspaceChannel,
|
|
8
|
+
WorkspaceSendMessageResponse,
|
|
9
|
+
} from "../../../Server/Utils/Workspace/WorkspaceBase";
|
|
10
|
+
import NotificationRuleWorkspaceChannel from "../../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
|
|
11
|
+
import WorkspaceNotificationRule from "../../../Models/DatabaseModels/WorkspaceNotificationRule";
|
|
12
|
+
import WorkspaceProjectAuthToken, {
|
|
13
|
+
MicrosoftTeamsMiscData,
|
|
14
|
+
} from "../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
15
|
+
import WorkspaceUserAuthToken from "../../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
16
|
+
import WorkspaceNotificationLog from "../../../Models/DatabaseModels/WorkspaceNotificationLog";
|
|
17
|
+
import BaseNotificationRule from "../../../Types/Workspace/NotificationRules/BaseNotificationRule";
|
|
18
|
+
import NotificationRuleEventType from "../../../Types/Workspace/NotificationRules/EventType";
|
|
19
|
+
import FilterCondition from "../../../Types/Filter/FilterCondition";
|
|
20
|
+
import WorkspaceType from "../../../Types/Workspace/WorkspaceType";
|
|
21
|
+
import WorkspaceMessagePayload from "../../../Types/Workspace/WorkspaceMessagePayload";
|
|
22
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
23
|
+
import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* Tests for the CHANNEL half of WorkspaceNotificationRuleService.testRule — the
|
|
27
|
+
* "Test Rule" button an admin presses to prove a workspace integration works.
|
|
28
|
+
*
|
|
29
|
+
* The bug these pin down: the loop that posted the test card into a channel the
|
|
30
|
+
* test had just created was nested inside `if (shouldPostToExistingChannel)`. A
|
|
31
|
+
* rule configured to CREATE a channel and not post to an existing one therefore
|
|
32
|
+
* created the channel, invited nobody (a no-op on Teams) and returned success
|
|
33
|
+
* having sent nothing at all.
|
|
34
|
+
*
|
|
35
|
+
* That is worse than a missing feature. The customer who reported the Microsoft
|
|
36
|
+
* Teams roster failure used exactly this configuration as a control: "creating a
|
|
37
|
+
* channel works, so the app must be installed correctly." It never posted, so it
|
|
38
|
+
* proved nothing, and the real fault was looked for in the wrong place.
|
|
39
|
+
*
|
|
40
|
+
* The created-channel post was also addressed with `existingTeam` — a field
|
|
41
|
+
* belonging to the OTHER half of the rule, undefined for a create-only rule and
|
|
42
|
+
* potentially a different team when both halves are configured.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
const TEAM_A: string = "aaaaaaaa-1111-2222-3333-444444444444";
|
|
46
|
+
const TEAM_B: string = "bbbbbbbb-5555-6666-7777-888888888888";
|
|
47
|
+
|
|
48
|
+
interface RuleOverrides extends Partial<BaseNotificationRule> {
|
|
49
|
+
shouldCreateNewChannel?: boolean;
|
|
50
|
+
teamToCreateChannelIn?: string;
|
|
51
|
+
newChannelTemplateName?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function makeRule(data: {
|
|
55
|
+
workspaceType?: WorkspaceType | undefined;
|
|
56
|
+
overrides: RuleOverrides;
|
|
57
|
+
name?: string | undefined;
|
|
58
|
+
}): WorkspaceNotificationRule {
|
|
59
|
+
const rule: WorkspaceNotificationRule = new WorkspaceNotificationRule();
|
|
60
|
+
rule.id = ObjectID.generate();
|
|
61
|
+
rule.projectId = ObjectID.generate();
|
|
62
|
+
rule.workspaceType = data.workspaceType || WorkspaceType.MicrosoftTeams;
|
|
63
|
+
rule.eventType = NotificationRuleEventType.Alert;
|
|
64
|
+
rule.name = data.name || "Test public channel Team";
|
|
65
|
+
rule.notificationRule = {
|
|
66
|
+
_type: "NotificationRule",
|
|
67
|
+
filterCondition: FilterCondition.Any,
|
|
68
|
+
filters: [],
|
|
69
|
+
shouldPostToExistingChannel: false,
|
|
70
|
+
existingChannelNames: "",
|
|
71
|
+
shouldCreateNewChannel: false,
|
|
72
|
+
...data.overrides,
|
|
73
|
+
} as BaseNotificationRule;
|
|
74
|
+
return rule;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function makeCreatedChannel(data: {
|
|
78
|
+
id: string;
|
|
79
|
+
name: string;
|
|
80
|
+
teamId?: string | undefined;
|
|
81
|
+
workspaceType?: WorkspaceType | undefined;
|
|
82
|
+
}): NotificationRuleWorkspaceChannel {
|
|
83
|
+
const channel: NotificationRuleWorkspaceChannel = {
|
|
84
|
+
id: data.id,
|
|
85
|
+
name: data.name,
|
|
86
|
+
workspaceType: data.workspaceType || WorkspaceType.MicrosoftTeams,
|
|
87
|
+
notificationRuleId: ObjectID.generate().toString(),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (data.teamId) {
|
|
91
|
+
channel.teamId = data.teamId;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return channel;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function makeSendResponse(data: {
|
|
98
|
+
workspaceType?: WorkspaceType | undefined;
|
|
99
|
+
threads?: Array<{ id: string; name: string; threadId: string }> | undefined;
|
|
100
|
+
errors?: Array<{ id: string; name: string; error: string }> | undefined;
|
|
101
|
+
}): WorkspaceSendMessageResponse {
|
|
102
|
+
const workspaceType: WorkspaceType =
|
|
103
|
+
data.workspaceType || WorkspaceType.MicrosoftTeams;
|
|
104
|
+
|
|
105
|
+
const response: WorkspaceSendMessageResponse = {
|
|
106
|
+
workspaceType: workspaceType,
|
|
107
|
+
threads: (data.threads || []).map(
|
|
108
|
+
(thread: { id: string; name: string; threadId: string }) => {
|
|
109
|
+
return {
|
|
110
|
+
channel: {
|
|
111
|
+
id: thread.id,
|
|
112
|
+
name: thread.name,
|
|
113
|
+
workspaceType: workspaceType,
|
|
114
|
+
} as WorkspaceChannel,
|
|
115
|
+
threadId: thread.threadId,
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
if (data.errors) {
|
|
122
|
+
response.errors = data.errors.map(
|
|
123
|
+
(error: { id: string; name: string; error: string }) => {
|
|
124
|
+
return {
|
|
125
|
+
channel: {
|
|
126
|
+
id: error.id,
|
|
127
|
+
name: error.name,
|
|
128
|
+
workspaceType: workspaceType,
|
|
129
|
+
} as WorkspaceChannel,
|
|
130
|
+
error: error.error,
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return response;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface Mocks {
|
|
140
|
+
postSpy: jest.SpyInstance;
|
|
141
|
+
createChannelsSpy: jest.SpyInstance;
|
|
142
|
+
inviteUsersSpy: jest.SpyInstance;
|
|
143
|
+
doesChannelExistSpy: jest.SpyInstance;
|
|
144
|
+
createLogSpy: jest.SpyInstance;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const testByUserId: ObjectID = ObjectID.generate();
|
|
148
|
+
|
|
149
|
+
function mockDeps(data: {
|
|
150
|
+
rule: WorkspaceNotificationRule;
|
|
151
|
+
createdChannels?: Array<NotificationRuleWorkspaceChannel> | undefined;
|
|
152
|
+
responses?: Array<WorkspaceSendMessageResponse> | undefined;
|
|
153
|
+
channelExists?: boolean | undefined;
|
|
154
|
+
}): Mocks {
|
|
155
|
+
jest
|
|
156
|
+
.spyOn(WorkspaceNotificationRuleService, "findOneById")
|
|
157
|
+
.mockResolvedValue(data.rule);
|
|
158
|
+
|
|
159
|
+
const userAuth: WorkspaceUserAuthToken = new WorkspaceUserAuthToken();
|
|
160
|
+
userAuth.workspaceUserId = "teams-user-1";
|
|
161
|
+
jest
|
|
162
|
+
.spyOn(WorkspaceUserAuthTokenService, "findOneBy")
|
|
163
|
+
.mockResolvedValue(userAuth);
|
|
164
|
+
|
|
165
|
+
const projectAuth: WorkspaceProjectAuthToken =
|
|
166
|
+
new WorkspaceProjectAuthToken();
|
|
167
|
+
projectAuth.authToken = "project-auth-token";
|
|
168
|
+
projectAuth.workspaceType = data.rule.workspaceType!;
|
|
169
|
+
jest
|
|
170
|
+
.spyOn(WorkspaceProjectAuthTokenService, "findOneBy")
|
|
171
|
+
.mockResolvedValue(projectAuth);
|
|
172
|
+
|
|
173
|
+
const authWithMisc: WorkspaceProjectAuthToken =
|
|
174
|
+
new WorkspaceProjectAuthToken();
|
|
175
|
+
authWithMisc.miscData = {
|
|
176
|
+
tenantId: "tenant-1",
|
|
177
|
+
teamId: TEAM_A,
|
|
178
|
+
teamName: "Test Team",
|
|
179
|
+
botId: "bot-1",
|
|
180
|
+
} as MicrosoftTeamsMiscData;
|
|
181
|
+
jest
|
|
182
|
+
.spyOn(WorkspaceProjectAuthTokenService, "getProjectAuth")
|
|
183
|
+
.mockResolvedValue(authWithMisc);
|
|
184
|
+
|
|
185
|
+
const createChannelsSpy: jest.SpyInstance = jest
|
|
186
|
+
.spyOn(WorkspaceNotificationRuleService, "createChannelsBasedOnRules")
|
|
187
|
+
.mockResolvedValue(data.createdChannels || []);
|
|
188
|
+
|
|
189
|
+
const inviteUsersSpy: jest.SpyInstance = jest
|
|
190
|
+
.spyOn(
|
|
191
|
+
WorkspaceNotificationRuleService,
|
|
192
|
+
"inviteUsersBasedOnRulesAndWorkspaceChannels",
|
|
193
|
+
)
|
|
194
|
+
.mockResolvedValue(undefined as never);
|
|
195
|
+
|
|
196
|
+
const doesChannelExistSpy: jest.SpyInstance = jest
|
|
197
|
+
.fn()
|
|
198
|
+
.mockResolvedValue(
|
|
199
|
+
data.channelExists === undefined ? true : data.channelExists,
|
|
200
|
+
) as unknown as jest.SpyInstance;
|
|
201
|
+
|
|
202
|
+
jest.spyOn(WorkspaceUtil, "getWorkspaceTypeUtil").mockReturnValue({
|
|
203
|
+
doesChannelExist: doesChannelExistSpy,
|
|
204
|
+
} as never);
|
|
205
|
+
|
|
206
|
+
const postSpy: jest.SpyInstance = jest
|
|
207
|
+
.spyOn(WorkspaceUtil, "postMessageToAllWorkspaceChannelsAsBot")
|
|
208
|
+
.mockResolvedValue(data.responses || []);
|
|
209
|
+
|
|
210
|
+
const createLogSpy: jest.SpyInstance = jest
|
|
211
|
+
.spyOn(WorkspaceNotificationLogService, "create")
|
|
212
|
+
.mockResolvedValue(new WorkspaceNotificationLog());
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
postSpy,
|
|
216
|
+
createChannelsSpy,
|
|
217
|
+
inviteUsersSpy,
|
|
218
|
+
doesChannelExistSpy,
|
|
219
|
+
createLogSpy,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function runTestRule(rule: WorkspaceNotificationRule): Promise<void> {
|
|
224
|
+
return WorkspaceNotificationRuleService.testRule({
|
|
225
|
+
ruleId: rule.id!,
|
|
226
|
+
projectId: ObjectID.generate(), // testRule overwrites this with rule.projectId.
|
|
227
|
+
testByUserId: testByUserId,
|
|
228
|
+
props: { isRoot: true },
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/*
|
|
233
|
+
* The single payload handed to postMessageToAllWorkspaceChannelsAsBot on a
|
|
234
|
+
* given call, for asserting destinations and teamId.
|
|
235
|
+
*/
|
|
236
|
+
function payloadOfCall(
|
|
237
|
+
postSpy: jest.SpyInstance,
|
|
238
|
+
callIndex: number,
|
|
239
|
+
): WorkspaceMessagePayload {
|
|
240
|
+
const args: {
|
|
241
|
+
messagePayloadsByWorkspace: Array<WorkspaceMessagePayload>;
|
|
242
|
+
} = postSpy.mock.calls[callIndex]![0] as {
|
|
243
|
+
messagePayloadsByWorkspace: Array<WorkspaceMessagePayload>;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
return args.messagePayloadsByWorkspace[0]!;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
beforeEach(() => {
|
|
250
|
+
jest.restoreAllMocks();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
afterEach(() => {
|
|
254
|
+
jest.restoreAllMocks();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe("testRule posts to channels it created, even when the rule posts nowhere else", () => {
|
|
258
|
+
test("a create-only rule sends a test message", async () => {
|
|
259
|
+
/*
|
|
260
|
+
* THE regression test. Before the fix this asserted zero calls, silently,
|
|
261
|
+
* and the Dashboard still showed a success modal.
|
|
262
|
+
*/
|
|
263
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
264
|
+
overrides: {
|
|
265
|
+
shouldCreateNewChannel: true,
|
|
266
|
+
shouldPostToExistingChannel: false,
|
|
267
|
+
teamToCreateChannelIn: TEAM_A,
|
|
268
|
+
newChannelTemplateName: "test public channel",
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const mocks: Mocks = mockDeps({
|
|
273
|
+
rule: rule,
|
|
274
|
+
createdChannels: [
|
|
275
|
+
makeCreatedChannel({
|
|
276
|
+
id: "19:new-channel@thread.tacv2",
|
|
277
|
+
name: "test-public-channel-ab12c",
|
|
278
|
+
teamId: TEAM_A,
|
|
279
|
+
}),
|
|
280
|
+
],
|
|
281
|
+
responses: [
|
|
282
|
+
makeSendResponse({
|
|
283
|
+
threads: [
|
|
284
|
+
{
|
|
285
|
+
id: "19:new-channel@thread.tacv2",
|
|
286
|
+
name: "test-public-channel-ab12c",
|
|
287
|
+
threadId: "thread-1",
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
}),
|
|
291
|
+
],
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
await runTestRule(rule);
|
|
295
|
+
|
|
296
|
+
expect(mocks.postSpy).toHaveBeenCalledTimes(1);
|
|
297
|
+
expect(payloadOfCall(mocks.postSpy, 0).channelIds).toEqual([
|
|
298
|
+
"19:new-channel@thread.tacv2",
|
|
299
|
+
]);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("a create-only rule addresses the send with the team the channel was created in", async () => {
|
|
303
|
+
/*
|
|
304
|
+
* existingTeam belongs to the post-to-existing half of the rule and is
|
|
305
|
+
* undefined here, so the send used to go out with no team at all.
|
|
306
|
+
*/
|
|
307
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
308
|
+
overrides: {
|
|
309
|
+
shouldCreateNewChannel: true,
|
|
310
|
+
shouldPostToExistingChannel: false,
|
|
311
|
+
teamToCreateChannelIn: TEAM_A,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const mocks: Mocks = mockDeps({
|
|
316
|
+
rule: rule,
|
|
317
|
+
createdChannels: [
|
|
318
|
+
makeCreatedChannel({
|
|
319
|
+
id: "19:new@thread.tacv2",
|
|
320
|
+
name: "oneuptime-alert-x",
|
|
321
|
+
teamId: TEAM_A,
|
|
322
|
+
}),
|
|
323
|
+
],
|
|
324
|
+
responses: [makeSendResponse({})],
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
await runTestRule(rule);
|
|
328
|
+
|
|
329
|
+
expect(payloadOfCall(mocks.postSpy, 0).teamId).toBe(TEAM_A);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test("the created channel's team wins over a DIFFERENT existingTeam on the same rule", async () => {
|
|
333
|
+
/*
|
|
334
|
+
* Both halves configured, naming two different teams. Addressing the created
|
|
335
|
+
* channel with existingTeam pointed the send at a team that does not own it.
|
|
336
|
+
*/
|
|
337
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
338
|
+
overrides: {
|
|
339
|
+
shouldCreateNewChannel: true,
|
|
340
|
+
shouldPostToExistingChannel: true,
|
|
341
|
+
teamToCreateChannelIn: TEAM_A,
|
|
342
|
+
existingTeam: TEAM_B,
|
|
343
|
+
existingChannelNames: "general",
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const mocks: Mocks = mockDeps({
|
|
348
|
+
rule: rule,
|
|
349
|
+
createdChannels: [
|
|
350
|
+
makeCreatedChannel({
|
|
351
|
+
id: "19:new@thread.tacv2",
|
|
352
|
+
name: "oneuptime-alert-x",
|
|
353
|
+
teamId: TEAM_A,
|
|
354
|
+
}),
|
|
355
|
+
],
|
|
356
|
+
responses: [makeSendResponse({})],
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
await runTestRule(rule);
|
|
360
|
+
|
|
361
|
+
expect(mocks.postSpy).toHaveBeenCalledTimes(2);
|
|
362
|
+
expect(payloadOfCall(mocks.postSpy, 0).teamId).toBe(TEAM_A);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("a create-only rule still creates the channel and invites the tester", async () => {
|
|
366
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
367
|
+
overrides: {
|
|
368
|
+
shouldCreateNewChannel: true,
|
|
369
|
+
teamToCreateChannelIn: TEAM_A,
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const mocks: Mocks = mockDeps({
|
|
374
|
+
rule: rule,
|
|
375
|
+
createdChannels: [
|
|
376
|
+
makeCreatedChannel({
|
|
377
|
+
id: "19:new@thread.tacv2",
|
|
378
|
+
name: "oneuptime-alert-x",
|
|
379
|
+
teamId: TEAM_A,
|
|
380
|
+
}),
|
|
381
|
+
],
|
|
382
|
+
responses: [makeSendResponse({})],
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
await runTestRule(rule);
|
|
386
|
+
|
|
387
|
+
expect(mocks.createChannelsSpy).toHaveBeenCalledTimes(1);
|
|
388
|
+
expect(mocks.inviteUsersSpy).toHaveBeenCalledTimes(1);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test("a failed send to a created channel fails the test instead of reporting success", async () => {
|
|
392
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
393
|
+
overrides: {
|
|
394
|
+
shouldCreateNewChannel: true,
|
|
395
|
+
teamToCreateChannelIn: TEAM_A,
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
mockDeps({
|
|
400
|
+
rule: rule,
|
|
401
|
+
createdChannels: [
|
|
402
|
+
makeCreatedChannel({
|
|
403
|
+
id: "19:new@thread.tacv2",
|
|
404
|
+
name: "oneuptime-alert-x",
|
|
405
|
+
teamId: TEAM_A,
|
|
406
|
+
}),
|
|
407
|
+
],
|
|
408
|
+
responses: [
|
|
409
|
+
makeSendResponse({
|
|
410
|
+
errors: [
|
|
411
|
+
{
|
|
412
|
+
id: "19:new@thread.tacv2",
|
|
413
|
+
name: "oneuptime-alert-x",
|
|
414
|
+
error: "The bot is not part of the conversation roster.",
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
}),
|
|
418
|
+
],
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
await expect(runTestRule(rule)).rejects.toThrow(
|
|
422
|
+
/Failed to send test message to some channels/,
|
|
423
|
+
);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
test("a failed send to a created channel is recorded in Notification Logs", async () => {
|
|
427
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
428
|
+
overrides: {
|
|
429
|
+
shouldCreateNewChannel: true,
|
|
430
|
+
teamToCreateChannelIn: TEAM_A,
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const mocks: Mocks = mockDeps({
|
|
435
|
+
rule: rule,
|
|
436
|
+
createdChannels: [
|
|
437
|
+
makeCreatedChannel({
|
|
438
|
+
id: "19:new@thread.tacv2",
|
|
439
|
+
name: "oneuptime-alert-x",
|
|
440
|
+
teamId: TEAM_A,
|
|
441
|
+
}),
|
|
442
|
+
],
|
|
443
|
+
responses: [
|
|
444
|
+
makeSendResponse({
|
|
445
|
+
errors: [
|
|
446
|
+
{
|
|
447
|
+
id: "19:new@thread.tacv2",
|
|
448
|
+
name: "oneuptime-alert-x",
|
|
449
|
+
error: "boom",
|
|
450
|
+
},
|
|
451
|
+
],
|
|
452
|
+
}),
|
|
453
|
+
],
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
await expect(runTestRule(rule)).rejects.toThrow();
|
|
457
|
+
expect(mocks.createLogSpy).toHaveBeenCalled();
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test("a successful send to a created channel logs a Success entry", async () => {
|
|
461
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
462
|
+
overrides: {
|
|
463
|
+
shouldCreateNewChannel: true,
|
|
464
|
+
teamToCreateChannelIn: TEAM_A,
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const mocks: Mocks = mockDeps({
|
|
469
|
+
rule: rule,
|
|
470
|
+
createdChannels: [
|
|
471
|
+
makeCreatedChannel({
|
|
472
|
+
id: "19:new@thread.tacv2",
|
|
473
|
+
name: "oneuptime-alert-x",
|
|
474
|
+
teamId: TEAM_A,
|
|
475
|
+
}),
|
|
476
|
+
],
|
|
477
|
+
responses: [
|
|
478
|
+
makeSendResponse({
|
|
479
|
+
threads: [
|
|
480
|
+
{
|
|
481
|
+
id: "19:new@thread.tacv2",
|
|
482
|
+
name: "oneuptime-alert-x",
|
|
483
|
+
threadId: "thread-7",
|
|
484
|
+
},
|
|
485
|
+
],
|
|
486
|
+
}),
|
|
487
|
+
],
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
await runTestRule(rule);
|
|
491
|
+
|
|
492
|
+
const log: WorkspaceNotificationLog = mocks.createLogSpy.mock.calls[0]![0]
|
|
493
|
+
.data as WorkspaceNotificationLog;
|
|
494
|
+
expect(log.threadId).toBe("thread-7");
|
|
495
|
+
expect(log.channelName).toBe("oneuptime-alert-x");
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test("several created channels each get their own send", async () => {
|
|
499
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
500
|
+
overrides: {
|
|
501
|
+
shouldCreateNewChannel: true,
|
|
502
|
+
teamToCreateChannelIn: TEAM_A,
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const mocks: Mocks = mockDeps({
|
|
507
|
+
rule: rule,
|
|
508
|
+
createdChannels: [
|
|
509
|
+
makeCreatedChannel({
|
|
510
|
+
id: "19:a@thread.tacv2",
|
|
511
|
+
name: "a",
|
|
512
|
+
teamId: TEAM_A,
|
|
513
|
+
}),
|
|
514
|
+
makeCreatedChannel({
|
|
515
|
+
id: "19:b@thread.tacv2",
|
|
516
|
+
name: "b",
|
|
517
|
+
teamId: TEAM_A,
|
|
518
|
+
}),
|
|
519
|
+
],
|
|
520
|
+
responses: [makeSendResponse({})],
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
await runTestRule(rule);
|
|
524
|
+
|
|
525
|
+
expect(mocks.postSpy).toHaveBeenCalledTimes(2);
|
|
526
|
+
expect(payloadOfCall(mocks.postSpy, 0).channelIds).toEqual([
|
|
527
|
+
"19:a@thread.tacv2",
|
|
528
|
+
]);
|
|
529
|
+
expect(payloadOfCall(mocks.postSpy, 1).channelIds).toEqual([
|
|
530
|
+
"19:b@thread.tacv2",
|
|
531
|
+
]);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
test("a rule that creates nothing and posts nowhere sends nothing", async () => {
|
|
535
|
+
// Not every empty rule should now start posting — only created channels did.
|
|
536
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
537
|
+
overrides: {
|
|
538
|
+
shouldCreateNewChannel: false,
|
|
539
|
+
shouldPostToExistingChannel: false,
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
const mocks: Mocks = mockDeps({ rule: rule, responses: [] });
|
|
544
|
+
|
|
545
|
+
await runTestRule(rule);
|
|
546
|
+
|
|
547
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test("Slack create-only rules post too, and carry no teamId", async () => {
|
|
551
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
552
|
+
workspaceType: WorkspaceType.Slack,
|
|
553
|
+
overrides: { shouldCreateNewChannel: true },
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
const mocks: Mocks = mockDeps({
|
|
557
|
+
rule: rule,
|
|
558
|
+
createdChannels: [
|
|
559
|
+
makeCreatedChannel({
|
|
560
|
+
id: "C123",
|
|
561
|
+
name: "oneuptime-alert-x",
|
|
562
|
+
workspaceType: WorkspaceType.Slack,
|
|
563
|
+
}),
|
|
564
|
+
],
|
|
565
|
+
responses: [makeSendResponse({ workspaceType: WorkspaceType.Slack })],
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
await runTestRule(rule);
|
|
569
|
+
|
|
570
|
+
expect(mocks.postSpy).toHaveBeenCalledTimes(1);
|
|
571
|
+
// teamId is a Teams concept and must not be set on a Slack payload.
|
|
572
|
+
expect(payloadOfCall(mocks.postSpy, 0).teamId).toBeUndefined();
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
describe("testRule posting to existing channels is unchanged", () => {
|
|
577
|
+
test("an existing-channel rule posts once per channel with the rule's team", async () => {
|
|
578
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
579
|
+
overrides: {
|
|
580
|
+
shouldPostToExistingChannel: true,
|
|
581
|
+
existingTeam: TEAM_B,
|
|
582
|
+
existingChannelNames: "general,test public channel",
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
const mocks: Mocks = mockDeps({
|
|
587
|
+
rule: rule,
|
|
588
|
+
responses: [makeSendResponse({})],
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
await runTestRule(rule);
|
|
592
|
+
|
|
593
|
+
expect(mocks.postSpy).toHaveBeenCalledTimes(2);
|
|
594
|
+
expect(payloadOfCall(mocks.postSpy, 0).channelNames).toEqual(["general"]);
|
|
595
|
+
expect(payloadOfCall(mocks.postSpy, 0).teamId).toBe(TEAM_B);
|
|
596
|
+
expect(payloadOfCall(mocks.postSpy, 1).channelNames).toEqual([
|
|
597
|
+
"test public channel",
|
|
598
|
+
]);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
test("an existing channel that does not resolve fails the test before any send", async () => {
|
|
602
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
603
|
+
overrides: {
|
|
604
|
+
shouldPostToExistingChannel: true,
|
|
605
|
+
existingTeam: TEAM_B,
|
|
606
|
+
existingChannelNames: "typo-channel",
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
const mocks: Mocks = mockDeps({
|
|
611
|
+
rule: rule,
|
|
612
|
+
channelExists: false,
|
|
613
|
+
responses: [makeSendResponse({})],
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
await expect(runTestRule(rule)).rejects.toThrow(/does not exist/);
|
|
617
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
test("existing channels are validated BEFORE the created-channel post runs", async () => {
|
|
621
|
+
/*
|
|
622
|
+
* Preserved ordering: a rule with both halves and a bad existing-channel
|
|
623
|
+
* name must fail without having posted anything, so the test does not leave
|
|
624
|
+
* a card in a freshly created channel and then report failure.
|
|
625
|
+
*/
|
|
626
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
627
|
+
overrides: {
|
|
628
|
+
shouldCreateNewChannel: true,
|
|
629
|
+
teamToCreateChannelIn: TEAM_A,
|
|
630
|
+
shouldPostToExistingChannel: true,
|
|
631
|
+
existingTeam: TEAM_B,
|
|
632
|
+
existingChannelNames: "typo-channel",
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
const mocks: Mocks = mockDeps({
|
|
637
|
+
rule: rule,
|
|
638
|
+
createdChannels: [
|
|
639
|
+
makeCreatedChannel({
|
|
640
|
+
id: "19:new@thread.tacv2",
|
|
641
|
+
name: "oneuptime-alert-x",
|
|
642
|
+
teamId: TEAM_A,
|
|
643
|
+
}),
|
|
644
|
+
],
|
|
645
|
+
channelExists: false,
|
|
646
|
+
responses: [makeSendResponse({})],
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
await expect(runTestRule(rule)).rejects.toThrow(/does not exist/);
|
|
650
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
test("a Microsoft Teams existing-channel rule with no team selected is rejected", async () => {
|
|
654
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
655
|
+
overrides: {
|
|
656
|
+
shouldPostToExistingChannel: true,
|
|
657
|
+
existingChannelNames: "general",
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
const mocks: Mocks = mockDeps({
|
|
662
|
+
rule: rule,
|
|
663
|
+
responses: [makeSendResponse({})],
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
await expect(runTestRule(rule)).rejects.toThrow(
|
|
667
|
+
/requires a team to be selected/,
|
|
668
|
+
);
|
|
669
|
+
expect(mocks.postSpy).not.toHaveBeenCalled();
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
test("created channels are posted to before existing ones", async () => {
|
|
673
|
+
const rule: WorkspaceNotificationRule = makeRule({
|
|
674
|
+
overrides: {
|
|
675
|
+
shouldCreateNewChannel: true,
|
|
676
|
+
teamToCreateChannelIn: TEAM_A,
|
|
677
|
+
shouldPostToExistingChannel: true,
|
|
678
|
+
existingTeam: TEAM_B,
|
|
679
|
+
existingChannelNames: "general",
|
|
680
|
+
},
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
const mocks: Mocks = mockDeps({
|
|
684
|
+
rule: rule,
|
|
685
|
+
createdChannels: [
|
|
686
|
+
makeCreatedChannel({
|
|
687
|
+
id: "19:new@thread.tacv2",
|
|
688
|
+
name: "oneuptime-alert-x",
|
|
689
|
+
teamId: TEAM_A,
|
|
690
|
+
}),
|
|
691
|
+
],
|
|
692
|
+
responses: [makeSendResponse({})],
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
await runTestRule(rule);
|
|
696
|
+
|
|
697
|
+
expect(payloadOfCall(mocks.postSpy, 0).channelIds).toEqual([
|
|
698
|
+
"19:new@thread.tacv2",
|
|
699
|
+
]);
|
|
700
|
+
expect(payloadOfCall(mocks.postSpy, 1).channelNames).toEqual(["general"]);
|
|
701
|
+
});
|
|
702
|
+
});
|