@oneuptime/common 11.7.1 → 11.7.2
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/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/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/Index.ts +6 -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/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/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/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/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/ModelDetail/CardModelDetail.tsx +23 -1
- package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +300 -30
- package/UI/Components/ModelTable/Column.ts +12 -0
- package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
- package/UI/Components/Navbar/NavBar.tsx +66 -1
- package/UI/Components/Table/Table.tsx +52 -13
- package/UI/Components/Table/Types/Column.ts +18 -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/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/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/Index.js +6 -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/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/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 +234 -21
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- 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/Navbar/NavBar.js +39 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +31 -11
- package/build/dist/UI/Components/Table/Table.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/package.json +4 -3
|
@@ -98,7 +98,7 @@ describe("NotificationRuleConditionUtil", () => {
|
|
|
98
98
|
shouldPostToExistingChannel: false,
|
|
99
99
|
});
|
|
100
100
|
expect(validate(rule)).toBe(
|
|
101
|
-
"Please select
|
|
101
|
+
"Please select a destination: create a Slack channel or post to an existing Slack channel",
|
|
102
102
|
);
|
|
103
103
|
});
|
|
104
104
|
|
|
@@ -139,6 +139,224 @@ describe("NotificationRuleConditionUtil", () => {
|
|
|
139
139
|
});
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
describe("getValidationError - chats", () => {
|
|
143
|
+
const teamsChatEitherOrError: string =
|
|
144
|
+
"Please select a destination: create a Microsoft Teams channel, post to an existing Microsoft Teams channel, or post to an existing Microsoft Teams chat";
|
|
145
|
+
|
|
146
|
+
const teamsChatIdsError: string =
|
|
147
|
+
"Please select at least one Microsoft Teams chat to post to. If no chats are listed, add the OneUptime app to a chat in Microsoft Teams first.";
|
|
148
|
+
|
|
149
|
+
const validateTeams: (
|
|
150
|
+
rule: IncidentNotificationRule,
|
|
151
|
+
eventType?: NotificationRuleEventType,
|
|
152
|
+
) => string | null = (
|
|
153
|
+
rule: IncidentNotificationRule,
|
|
154
|
+
eventType: NotificationRuleEventType = NotificationRuleEventType.Incident,
|
|
155
|
+
): string | null => {
|
|
156
|
+
return NotificationRuleConditionUtil.getValidationError({
|
|
157
|
+
notificationRule: rule,
|
|
158
|
+
eventType: eventType,
|
|
159
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
test("a Teams rule posting only to an existing chat satisfies the either/or requirement", () => {
|
|
164
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
165
|
+
shouldCreateNewChannel: false,
|
|
166
|
+
shouldPostToExistingChannel: false,
|
|
167
|
+
shouldPostToExistingChat: true,
|
|
168
|
+
existingChatIds: ["c1"],
|
|
169
|
+
});
|
|
170
|
+
expect(validateTeams(rule)).toBeNull();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("a Teams rule with all three destination toggles off mentions the chat option in the error", () => {
|
|
174
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
175
|
+
shouldCreateNewChannel: false,
|
|
176
|
+
shouldPostToExistingChannel: false,
|
|
177
|
+
shouldPostToExistingChat: false,
|
|
178
|
+
});
|
|
179
|
+
expect(validateTeams(rule)).toBe(teamsChatEitherOrError);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test("a Slack rule with both channel toggles off does not mention the chat option", () => {
|
|
183
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
184
|
+
shouldCreateNewChannel: false,
|
|
185
|
+
shouldPostToExistingChannel: false,
|
|
186
|
+
});
|
|
187
|
+
const error: string | null = validate(rule);
|
|
188
|
+
expect(error).toBe(
|
|
189
|
+
"Please select a destination: create a Slack channel or post to an existing Slack channel",
|
|
190
|
+
);
|
|
191
|
+
expect(error).not.toContain("chat");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("chat toggle on with an empty chat-id list returns the chat-selection error", () => {
|
|
195
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
196
|
+
shouldCreateNewChannel: false,
|
|
197
|
+
shouldPostToExistingChannel: false,
|
|
198
|
+
shouldPostToExistingChat: true,
|
|
199
|
+
existingChatIds: [],
|
|
200
|
+
});
|
|
201
|
+
expect(validateTeams(rule)).toBe(teamsChatIdsError);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("chat toggle on with undefined chat ids returns the chat-selection error", () => {
|
|
205
|
+
// existingChatIds is intentionally left off the rule so it is undefined.
|
|
206
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
207
|
+
shouldCreateNewChannel: false,
|
|
208
|
+
shouldPostToExistingChannel: false,
|
|
209
|
+
shouldPostToExistingChat: true,
|
|
210
|
+
});
|
|
211
|
+
expect(rule.existingChatIds).toBeUndefined();
|
|
212
|
+
expect(validateTeams(rule)).toBe(teamsChatIdsError);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("chat toggle on with chat ids passes even when channel toggles are also configured", () => {
|
|
216
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
217
|
+
shouldPostToExistingChat: true,
|
|
218
|
+
existingChatIds: ["19:abc@thread.v2", "19:def@unq.gbl.spaces"],
|
|
219
|
+
});
|
|
220
|
+
expect(validateTeams(rule)).toBeNull();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("chat-id validation still runs for the Monitor event type (either/or block is skipped)", () => {
|
|
224
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
225
|
+
shouldCreateNewChannel: false,
|
|
226
|
+
shouldPostToExistingChannel: false,
|
|
227
|
+
shouldPostToExistingChat: true,
|
|
228
|
+
existingChatIds: [],
|
|
229
|
+
});
|
|
230
|
+
expect(validateTeams(rule, NotificationRuleEventType.Monitor)).toBe(
|
|
231
|
+
teamsChatIdsError,
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("chat-id validation still runs for the OnCallDutyPolicy event type", () => {
|
|
236
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
237
|
+
shouldCreateNewChannel: false,
|
|
238
|
+
shouldPostToExistingChannel: false,
|
|
239
|
+
shouldPostToExistingChat: true,
|
|
240
|
+
existingChatIds: [],
|
|
241
|
+
});
|
|
242
|
+
expect(
|
|
243
|
+
validateTeams(rule, NotificationRuleEventType.OnCallDutyPolicy),
|
|
244
|
+
).toBe(teamsChatIdsError);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("a Monitor-event chat rule with chat ids passes", () => {
|
|
248
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
249
|
+
shouldCreateNewChannel: false,
|
|
250
|
+
shouldPostToExistingChannel: false,
|
|
251
|
+
shouldPostToExistingChat: true,
|
|
252
|
+
existingChatIds: ["c1"],
|
|
253
|
+
});
|
|
254
|
+
expect(validateTeams(rule, NotificationRuleEventType.Monitor)).toBeNull();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("a combined existing-channel + chat rule is valid", () => {
|
|
258
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
259
|
+
shouldCreateNewChannel: false,
|
|
260
|
+
shouldPostToExistingChannel: true,
|
|
261
|
+
existingChannelNames: "ops-alerts",
|
|
262
|
+
shouldPostToExistingChat: true,
|
|
263
|
+
existingChatIds: ["c1"],
|
|
264
|
+
});
|
|
265
|
+
expect(validateTeams(rule)).toBeNull();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("existing-channel toggle still requires channel names even when chat fields are valid", () => {
|
|
269
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
270
|
+
shouldCreateNewChannel: false,
|
|
271
|
+
shouldPostToExistingChannel: true,
|
|
272
|
+
existingChannelNames: " ",
|
|
273
|
+
shouldPostToExistingChat: true,
|
|
274
|
+
existingChatIds: ["c1"],
|
|
275
|
+
});
|
|
276
|
+
expect(validateTeams(rule)).toBe(
|
|
277
|
+
"Existing Microsoft Teams channel name is required",
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
test("create-channel toggle still requires a template name even when chat fields are valid", () => {
|
|
282
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
283
|
+
shouldCreateNewChannel: true,
|
|
284
|
+
newChannelTemplateName: " ",
|
|
285
|
+
shouldPostToExistingChannel: false,
|
|
286
|
+
shouldPostToExistingChat: true,
|
|
287
|
+
existingChatIds: ["c1"],
|
|
288
|
+
});
|
|
289
|
+
expect(validateTeams(rule)).toBe(
|
|
290
|
+
"New Microsoft Teams channel name is required",
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("channel-name errors take precedence over the chat-id error", () => {
|
|
295
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
296
|
+
shouldCreateNewChannel: false,
|
|
297
|
+
shouldPostToExistingChannel: true,
|
|
298
|
+
existingChannelNames: "",
|
|
299
|
+
shouldPostToExistingChat: true,
|
|
300
|
+
existingChatIds: [],
|
|
301
|
+
});
|
|
302
|
+
expect(validateTeams(rule)).toBe(
|
|
303
|
+
"Existing Microsoft Teams channel name is required",
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("chat-id validation is workspace-agnostic: a Slack rule with the chat toggle on and no ids errors too", () => {
|
|
308
|
+
/*
|
|
309
|
+
* The chat toggle is only surfaced in the Teams UI, but the validator
|
|
310
|
+
* does not gate the chat-ids check on workspace type. Pin the current
|
|
311
|
+
* behavior so a change here is deliberate.
|
|
312
|
+
*/
|
|
313
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
314
|
+
shouldPostToExistingChat: true,
|
|
315
|
+
existingChatIds: [],
|
|
316
|
+
});
|
|
317
|
+
expect(validate(rule)).toBe(
|
|
318
|
+
"Please select at least one Slack chat to post to. If no chats are listed, add the OneUptime app to a chat in Slack first.",
|
|
319
|
+
);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test.each([
|
|
323
|
+
NotificationRuleEventType.Incident,
|
|
324
|
+
NotificationRuleEventType.Alert,
|
|
325
|
+
NotificationRuleEventType.AlertEpisode,
|
|
326
|
+
NotificationRuleEventType.IncidentEpisode,
|
|
327
|
+
NotificationRuleEventType.ScheduledMaintenance,
|
|
328
|
+
])(
|
|
329
|
+
"a chat-only Teams rule is accepted for the %s event type",
|
|
330
|
+
(eventType: NotificationRuleEventType) => {
|
|
331
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
332
|
+
shouldCreateNewChannel: false,
|
|
333
|
+
shouldPostToExistingChannel: false,
|
|
334
|
+
shouldPostToExistingChat: true,
|
|
335
|
+
existingChatIds: ["c1"],
|
|
336
|
+
});
|
|
337
|
+
expect(validateTeams(rule, eventType)).toBeNull();
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
test.each([
|
|
342
|
+
NotificationRuleEventType.Incident,
|
|
343
|
+
NotificationRuleEventType.Alert,
|
|
344
|
+
NotificationRuleEventType.AlertEpisode,
|
|
345
|
+
NotificationRuleEventType.IncidentEpisode,
|
|
346
|
+
NotificationRuleEventType.ScheduledMaintenance,
|
|
347
|
+
])(
|
|
348
|
+
"a Teams rule with no destination at all is rejected for the %s event type",
|
|
349
|
+
(eventType: NotificationRuleEventType) => {
|
|
350
|
+
const rule: IncidentNotificationRule = makeRule({
|
|
351
|
+
shouldCreateNewChannel: false,
|
|
352
|
+
shouldPostToExistingChannel: false,
|
|
353
|
+
shouldPostToExistingChat: false,
|
|
354
|
+
});
|
|
355
|
+
expect(validateTeams(rule, eventType)).toBe(teamsChatEitherOrError);
|
|
356
|
+
},
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
|
|
142
360
|
describe("hasValueField", () => {
|
|
143
361
|
test.each([
|
|
144
362
|
ConditionType.IsEmpty,
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import { fireEvent, render, RenderResult } from "@testing-library/react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
5
|
+
import BulkUpdateForm, {
|
|
6
|
+
BulkActionButtonSchema,
|
|
7
|
+
ComponentProps,
|
|
8
|
+
} from "../../../UI/Components/BulkUpdate/BulkUpdateForm";
|
|
9
|
+
import { ButtonStyleType } from "../../../UI/Components/Button/Button";
|
|
10
|
+
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* react-i18next is not initialized in the test environment. Mock the hook so
|
|
14
|
+
* translate helpers echo their input and the component renders synchronously.
|
|
15
|
+
*/
|
|
16
|
+
jest.mock("react-i18next", () => {
|
|
17
|
+
return {
|
|
18
|
+
useTranslation: () => {
|
|
19
|
+
return {
|
|
20
|
+
t: (key: string, opts?: { defaultValue?: string }): string => {
|
|
21
|
+
return opts?.defaultValue ?? key;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
interface Row {
|
|
29
|
+
_id?: string | undefined;
|
|
30
|
+
name?: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type MakeRowsFunction = (count: number) => Array<Row>;
|
|
34
|
+
|
|
35
|
+
const makeRows: MakeRowsFunction = (count: number): Array<Row> => {
|
|
36
|
+
const rows: Array<Row> = [];
|
|
37
|
+
|
|
38
|
+
for (let i: number = 0; i < count; i++) {
|
|
39
|
+
rows.push({ _id: `id-${i}`, name: `Row ${i}` });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return rows;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const BUTTONS: Array<BulkActionButtonSchema<Row>> = [
|
|
46
|
+
{
|
|
47
|
+
title: "Archive",
|
|
48
|
+
buttonStyleType: ButtonStyleType.NORMAL,
|
|
49
|
+
onClick: (): Promise<void> => {
|
|
50
|
+
return Promise.resolve();
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
type RenderFormFunction = (
|
|
56
|
+
overrides: Partial<ComponentProps<Row>>,
|
|
57
|
+
) => RenderResult;
|
|
58
|
+
|
|
59
|
+
const renderForm: RenderFormFunction = (
|
|
60
|
+
overrides: Partial<ComponentProps<Row>>,
|
|
61
|
+
): RenderResult => {
|
|
62
|
+
const props: ComponentProps<Row> = {
|
|
63
|
+
selectedItems: makeRows(3),
|
|
64
|
+
isAllItemsSelected: false,
|
|
65
|
+
onSelectAllClick: () => {},
|
|
66
|
+
onClearSelectionClick: () => {},
|
|
67
|
+
singularLabel: "Label",
|
|
68
|
+
pluralLabel: "Labels",
|
|
69
|
+
buttons: BUTTONS,
|
|
70
|
+
...overrides,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return render(<BulkUpdateForm<Row> {...props} />);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
describe("BulkUpdateForm", () => {
|
|
77
|
+
test("renders nothing when nothing is selected", () => {
|
|
78
|
+
const { container } = renderForm({ selectedItems: [] });
|
|
79
|
+
|
|
80
|
+
expect(container.innerHTML).toBe("");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("offers Select All while only part of the table is selected", () => {
|
|
84
|
+
const { getByText } = renderForm({ isAllItemsSelected: false });
|
|
85
|
+
|
|
86
|
+
expect(getByText("3 Labels Selected")).toBeTruthy();
|
|
87
|
+
expect(getByText("Select All Labels")).toBeTruthy();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("hides Select All once everything really is selected", () => {
|
|
91
|
+
const { queryByText } = renderForm({ isAllItemsSelected: true });
|
|
92
|
+
|
|
93
|
+
expect(queryByText("Select All Labels")).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("blocks a second Select All while the first is still loading", () => {
|
|
97
|
+
const onSelectAllClick: MockFunction = getJestMockFunction();
|
|
98
|
+
|
|
99
|
+
const { getByText } = renderForm({
|
|
100
|
+
isSelectingAllItems: true,
|
|
101
|
+
onSelectAllClick: onSelectAllClick as unknown as () => void,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const button: HTMLElement = getByText("Selecting All Labels...");
|
|
105
|
+
|
|
106
|
+
fireEvent.click(button);
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
* Selecting everything is a paged, multi-request fetch. Letting it be
|
|
110
|
+
* re-entrant would interleave two runs over the same selection.
|
|
111
|
+
*/
|
|
112
|
+
expect(onSelectAllClick).not.toHaveBeenCalled();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("surfaces a failed select-all next to the button that triggered it", () => {
|
|
116
|
+
const { getByText } = renderForm({
|
|
117
|
+
errorMessage: "Network request failed",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(getByText("Network request failed")).toBeTruthy();
|
|
121
|
+
// The selection the user already had is still reported.
|
|
122
|
+
expect(getByText("3 Labels Selected")).toBeTruthy();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("shows no error when the select-all succeeded", () => {
|
|
126
|
+
const { queryByText } = renderForm({ errorMessage: undefined });
|
|
127
|
+
|
|
128
|
+
expect(queryByText(/failed/)).toBeNull();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("names real numbers when the selection was truncated", () => {
|
|
132
|
+
const { getByText } = renderForm({
|
|
133
|
+
selectedItems: makeRows(5),
|
|
134
|
+
isAllItemsSelected: true,
|
|
135
|
+
isSelectionTruncated: true,
|
|
136
|
+
totalMatchingItemsCount: 24318,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(
|
|
140
|
+
getByText(
|
|
141
|
+
new RegExp(`Selected 5 of ${(24318).toLocaleString()} matching Labels`),
|
|
142
|
+
),
|
|
143
|
+
).toBeTruthy();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("stays quiet when a full selection happens to sit on the ceiling", () => {
|
|
147
|
+
/*
|
|
148
|
+
* The warning used to be inferred from
|
|
149
|
+
* `selectedItems.length === LIMIT_PER_PROJECT`, so a project holding
|
|
150
|
+
* exactly the ceiling was told its selection had been cut short when
|
|
151
|
+
* nothing had been dropped at all.
|
|
152
|
+
*/
|
|
153
|
+
const { queryByText } = renderForm({
|
|
154
|
+
selectedItems: makeRows(LIMIT_PER_PROJECT),
|
|
155
|
+
isAllItemsSelected: true,
|
|
156
|
+
isSelectionTruncated: false,
|
|
157
|
+
totalMatchingItemsCount: LIMIT_PER_PROJECT,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
expect(queryByText(/matching Labels/)).toBeNull();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("clearing the selection calls back", () => {
|
|
164
|
+
const onClearSelectionClick: MockFunction = getJestMockFunction();
|
|
165
|
+
|
|
166
|
+
const { getByText } = renderForm({
|
|
167
|
+
onClearSelectionClick: onClearSelectionClick as unknown as () => void,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
fireEvent.click(getByText("Clear Selection"));
|
|
171
|
+
|
|
172
|
+
expect(onClearSelectionClick).toHaveBeenCalledTimes(1);
|
|
173
|
+
});
|
|
174
|
+
});
|