@oneuptime/common 9.2.12 → 9.2.14
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/AlertInternalNote.ts +29 -0
- package/Models/DatabaseModels/IncidentInternalNote.ts +29 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +29 -0
- package/Models/DatabaseModels/Index.ts +3 -11
- package/Models/DatabaseModels/{CopilotPullRequest.ts → LlmProvider.ts} +243 -248
- package/Models/DatabaseModels/ScheduledMaintenanceInternalNote.ts +29 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +29 -0
- package/Server/API/LlmProviderAPI.ts +57 -0
- package/Server/API/MicrosoftTeamsAPI.ts +2 -146
- package/Server/API/SlackAPI.ts +105 -0
- package/Server/API/StatusPageAPI.ts +6 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.ts +71 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.ts +45 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.ts +61 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/SlackAuthorization.ts +4 -1
- package/Server/Services/AlertInternalNoteService.ts +26 -0
- package/Server/Services/IncidentInternalNoteService.ts +26 -0
- package/Server/Services/IncidentPublicNoteService.ts +26 -0
- package/Server/Services/Index.ts +2 -11
- package/Server/Services/LlmProviderService.ts +100 -0
- package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +26 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +26 -0
- package/Server/Services/StatusPageService.ts +3 -0
- package/Server/Utils/Express.ts +1 -0
- package/Server/Utils/StartServer.ts +5 -0
- package/Server/Utils/StatusPageResource.ts +89 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +96 -55
- package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +16 -0
- package/Server/Utils/Workspace/Slack/Actions/Alert.ts +184 -1
- package/Server/Utils/Workspace/Slack/Actions/Incident.ts +224 -1
- package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +232 -1
- package/Server/Utils/Workspace/Slack/Slack.ts +113 -0
- package/Server/Utils/Workspace/Slack/app-manifest.json +13 -2
- package/Tests/Server/Utils/StatusPageResource.test.ts +161 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/LLM/Index.ts +4 -0
- package/Types/LLM/LlmType.ts +7 -0
- package/Types/Permission.ts +38 -113
- package/UI/Components/Icon/Icon.tsx +8 -0
- package/UI/Components/Link/Link.tsx +5 -1
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js +30 -0
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js +30 -0
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +30 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -10
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{CopilotPullRequest.js → LlmProvider.js} +263 -255
- package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js +30 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +30 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +36 -0
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -0
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +2 -91
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +74 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +6 -0
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.js +39 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.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/Middleware/SlackAuthorization.js +4 -1
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AlertInternalNoteService.js +24 -0
- package/build/dist/Server/Services/AlertInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentInternalNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -10
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +85 -0
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -0
- package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +24 -0
- package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +24 -0
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +3 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +5 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageResource.js +68 -0
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +73 -42
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +13 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +143 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +173 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +173 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +92 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +12 -2
- package/build/dist/Tests/Server/Utils/StatusPageResource.test.js +122 -0
- package/build/dist/Tests/Server/Utils/StatusPageResource.test.js.map +1 -0
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/LLM/Index.js +4 -0
- package/build/dist/Types/LLM/Index.js.map +1 -0
- package/build/dist/Types/LLM/LlmType.js +8 -0
- package/build/dist/Types/LLM/LlmType.js.map +1 -0
- package/build/dist/Types/Permission.js +32 -97
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +3 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Link/Link.js +4 -1
- package/build/dist/UI/Components/Link/Link.js.map +1 -1
- package/package.json +5 -1
- package/Models/DatabaseModels/CopilotAction.ts +0 -772
- package/Models/DatabaseModels/CopilotActionTypePriority.ts +0 -340
- package/Models/DatabaseModels/CopilotCodeRepository.ts +0 -637
- package/Models/DatabaseModels/ServiceCopilotCodeRepository.ts +0 -544
- package/Server/API/CopilotActionAPI.ts +0 -418
- package/Server/API/CopilotCodeRepositoryAPI.ts +0 -127
- package/Server/API/CopilotPullRequestAPI.ts +0 -243
- package/Server/Docs/CodeRepository.md +0 -43
- package/Server/Middleware/CodeRepositoryAuthorization.ts +0 -50
- package/Server/Services/CopilotActionService.ts +0 -10
- package/Server/Services/CopilotActionTypePriorityService.ts +0 -67
- package/Server/Services/CopilotCodeRepositoryService.ts +0 -62
- package/Server/Services/CopilotPullRequestService.ts +0 -10
- package/Server/Services/ServiceCopilotCodeRepositoryService.ts +0 -10
- package/Types/Copilot/CopilotActionProps/DirectoryActionProp.ts +0 -3
- package/Types/Copilot/CopilotActionProps/ExceptionActionProp.ts +0 -4
- package/Types/Copilot/CopilotActionProps/FileActionProp.ts +0 -7
- package/Types/Copilot/CopilotActionProps/FunctionActionProp.ts +0 -5
- package/Types/Copilot/CopilotActionProps/Index.ts +0 -96
- package/Types/Copilot/CopilotActionProps/SpanActionProp.ts +0 -4
- package/Types/Copilot/CopilotActionStatus.ts +0 -114
- package/Types/Copilot/CopilotActionType.ts +0 -212
- package/build/dist/Models/DatabaseModels/CopilotAction.js +0 -793
- package/build/dist/Models/DatabaseModels/CopilotAction.js.map +0 -1
- package/build/dist/Models/DatabaseModels/CopilotActionTypePriority.js +0 -358
- package/build/dist/Models/DatabaseModels/CopilotActionTypePriority.js.map +0 -1
- package/build/dist/Models/DatabaseModels/CopilotCodeRepository.js +0 -656
- package/build/dist/Models/DatabaseModels/CopilotCodeRepository.js.map +0 -1
- package/build/dist/Models/DatabaseModels/CopilotPullRequest.js.map +0 -1
- package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js +0 -561
- package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js.map +0 -1
- package/build/dist/Server/API/CopilotActionAPI.js +0 -295
- package/build/dist/Server/API/CopilotActionAPI.js.map +0 -1
- package/build/dist/Server/API/CopilotCodeRepositoryAPI.js +0 -91
- package/build/dist/Server/API/CopilotCodeRepositoryAPI.js.map +0 -1
- package/build/dist/Server/API/CopilotPullRequestAPI.js +0 -166
- package/build/dist/Server/API/CopilotPullRequestAPI.js.map +0 -1
- package/build/dist/Server/Middleware/CodeRepositoryAuthorization.js +0 -48
- package/build/dist/Server/Middleware/CodeRepositoryAuthorization.js.map +0 -1
- package/build/dist/Server/Services/CopilotActionService.js +0 -9
- package/build/dist/Server/Services/CopilotActionService.js.map +0 -1
- package/build/dist/Server/Services/CopilotActionTypePriorityService.js +0 -61
- package/build/dist/Server/Services/CopilotActionTypePriorityService.js.map +0 -1
- package/build/dist/Server/Services/CopilotCodeRepositoryService.js +0 -61
- package/build/dist/Server/Services/CopilotCodeRepositoryService.js.map +0 -1
- package/build/dist/Server/Services/CopilotPullRequestService.js +0 -9
- package/build/dist/Server/Services/CopilotPullRequestService.js.map +0 -1
- package/build/dist/Server/Services/ServiceCopilotCodeRepositoryService.js +0 -9
- package/build/dist/Server/Services/ServiceCopilotCodeRepositoryService.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/DirectoryActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/DirectoryActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/ExceptionActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/ExceptionActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/FileActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/FileActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/FunctionActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/FunctionActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/Index.js +0 -64
- package/build/dist/Types/Copilot/CopilotActionProps/Index.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/SpanActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/SpanActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionStatus.js +0 -96
- package/build/dist/Types/Copilot/CopilotActionStatus.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionType.js +0 -175
- package/build/dist/Types/Copilot/CopilotActionType.js.map +0 -1
|
@@ -3,7 +3,10 @@ import ObjectID from "../../../../../Types/ObjectID";
|
|
|
3
3
|
import ScheduledMaintenanceService from "../../../../Services/ScheduledMaintenanceService";
|
|
4
4
|
import { ExpressRequest, ExpressResponse } from "../../../Express";
|
|
5
5
|
import SlackUtil from "../Slack";
|
|
6
|
-
import SlackActionType
|
|
6
|
+
import SlackActionType, {
|
|
7
|
+
PrivateNoteEmojis,
|
|
8
|
+
PublicNoteEmojis,
|
|
9
|
+
} from "./ActionTypes";
|
|
7
10
|
import { SlackAction, SlackRequest } from "./Auth";
|
|
8
11
|
import Response from "../../../Response";
|
|
9
12
|
import {
|
|
@@ -34,7 +37,12 @@ import OneUptimeDate from "../../../../../Types/Date";
|
|
|
34
37
|
import AccessTokenService from "../../../../Services/AccessTokenService";
|
|
35
38
|
import CaptureSpan from "../../../Telemetry/CaptureSpan";
|
|
36
39
|
import WorkspaceType from "../../../../../Types/Workspace/WorkspaceType";
|
|
40
|
+
import WorkspaceUserAuthTokenService from "../../../../Services/WorkspaceUserAuthTokenService";
|
|
37
41
|
import WorkspaceNotificationLogService from "../../../../Services/WorkspaceNotificationLogService";
|
|
42
|
+
import WorkspaceProjectAuthTokenService from "../../../../Services/WorkspaceProjectAuthTokenService";
|
|
43
|
+
import WorkspaceNotificationLog from "../../../../../Models/DatabaseModels/WorkspaceNotificationLog";
|
|
44
|
+
import WorkspaceProjectAuthToken from "../../../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
45
|
+
import WorkspaceUserAuthToken from "../../../../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
38
46
|
|
|
39
47
|
export default class SlackScheduledMaintenanceActions {
|
|
40
48
|
@CaptureSpan()
|
|
@@ -1110,4 +1118,227 @@ export default class SlackScheduledMaintenanceActions {
|
|
|
1110
1118
|
new BadDataException("Invalid Action Type"),
|
|
1111
1119
|
);
|
|
1112
1120
|
}
|
|
1121
|
+
|
|
1122
|
+
@CaptureSpan()
|
|
1123
|
+
public static async handleEmojiReaction(data: {
|
|
1124
|
+
teamId: string;
|
|
1125
|
+
reaction: string;
|
|
1126
|
+
userId: string;
|
|
1127
|
+
channelId: string;
|
|
1128
|
+
messageTs: string;
|
|
1129
|
+
}): Promise<void> {
|
|
1130
|
+
logger.debug(
|
|
1131
|
+
"Handling emoji reaction for Scheduled Maintenance with data:",
|
|
1132
|
+
);
|
|
1133
|
+
logger.debug(data);
|
|
1134
|
+
|
|
1135
|
+
const { teamId, reaction, userId, channelId, messageTs } = data;
|
|
1136
|
+
|
|
1137
|
+
// Check if the emoji is a supported private or public note emoji
|
|
1138
|
+
const isPrivateNoteEmoji: boolean = PrivateNoteEmojis.includes(reaction);
|
|
1139
|
+
const isPublicNoteEmoji: boolean = PublicNoteEmojis.includes(reaction);
|
|
1140
|
+
|
|
1141
|
+
if (!isPrivateNoteEmoji && !isPublicNoteEmoji) {
|
|
1142
|
+
logger.debug(
|
|
1143
|
+
`Emoji "${reaction}" is not a supported note emoji. Ignoring.`,
|
|
1144
|
+
);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// Get the project auth token using the team ID
|
|
1149
|
+
const projectAuth: WorkspaceProjectAuthToken | null =
|
|
1150
|
+
await WorkspaceProjectAuthTokenService.findOneBy({
|
|
1151
|
+
query: {
|
|
1152
|
+
workspaceProjectId: teamId,
|
|
1153
|
+
},
|
|
1154
|
+
select: {
|
|
1155
|
+
projectId: true,
|
|
1156
|
+
authToken: true,
|
|
1157
|
+
},
|
|
1158
|
+
props: {
|
|
1159
|
+
isRoot: true,
|
|
1160
|
+
},
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
if (!projectAuth || !projectAuth.projectId || !projectAuth.authToken) {
|
|
1164
|
+
logger.debug(
|
|
1165
|
+
"No project auth found for team ID. Ignoring emoji reaction.",
|
|
1166
|
+
);
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
const projectId: ObjectID = projectAuth.projectId;
|
|
1171
|
+
const authToken: string = projectAuth.authToken;
|
|
1172
|
+
|
|
1173
|
+
// Find the scheduled maintenance linked to this channel
|
|
1174
|
+
const workspaceLog: WorkspaceNotificationLog | null =
|
|
1175
|
+
await WorkspaceNotificationLogService.findOneBy({
|
|
1176
|
+
query: {
|
|
1177
|
+
channelId: channelId,
|
|
1178
|
+
workspaceType: WorkspaceType.Slack,
|
|
1179
|
+
projectId: projectId,
|
|
1180
|
+
},
|
|
1181
|
+
select: {
|
|
1182
|
+
scheduledMaintenanceId: true,
|
|
1183
|
+
},
|
|
1184
|
+
props: {
|
|
1185
|
+
isRoot: true,
|
|
1186
|
+
},
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
if (!workspaceLog || !workspaceLog.scheduledMaintenanceId) {
|
|
1190
|
+
logger.debug(
|
|
1191
|
+
"No scheduled maintenance found linked to this channel. Ignoring emoji reaction.",
|
|
1192
|
+
);
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
const scheduledMaintenanceId: ObjectID =
|
|
1197
|
+
workspaceLog.scheduledMaintenanceId;
|
|
1198
|
+
|
|
1199
|
+
// Get the scheduled maintenance number for the confirmation message
|
|
1200
|
+
const scheduledMaintenanceNumber: number | null =
|
|
1201
|
+
await ScheduledMaintenanceService.getScheduledMaintenanceNumber({
|
|
1202
|
+
scheduledMaintenanceId: scheduledMaintenanceId,
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1205
|
+
// Get the user ID in OneUptime based on Slack user ID
|
|
1206
|
+
const userAuth: WorkspaceUserAuthToken | null =
|
|
1207
|
+
await WorkspaceUserAuthTokenService.findOneBy({
|
|
1208
|
+
query: {
|
|
1209
|
+
workspaceUserId: userId,
|
|
1210
|
+
workspaceType: WorkspaceType.Slack,
|
|
1211
|
+
projectId: projectId,
|
|
1212
|
+
},
|
|
1213
|
+
select: {
|
|
1214
|
+
userId: true,
|
|
1215
|
+
},
|
|
1216
|
+
props: {
|
|
1217
|
+
isRoot: true,
|
|
1218
|
+
},
|
|
1219
|
+
});
|
|
1220
|
+
|
|
1221
|
+
if (!userAuth || !userAuth.userId) {
|
|
1222
|
+
logger.debug(
|
|
1223
|
+
"No OneUptime user found for Slack user. Ignoring emoji reaction.",
|
|
1224
|
+
);
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
const oneUptimeUserId: ObjectID = userAuth.userId;
|
|
1229
|
+
|
|
1230
|
+
// Fetch the message text using the timestamp
|
|
1231
|
+
let messageText: string | null = null;
|
|
1232
|
+
try {
|
|
1233
|
+
messageText = await SlackUtil.getMessageByTimestamp({
|
|
1234
|
+
authToken: authToken,
|
|
1235
|
+
channelId: channelId,
|
|
1236
|
+
messageTs: messageTs,
|
|
1237
|
+
});
|
|
1238
|
+
} catch (err) {
|
|
1239
|
+
logger.error("Error fetching message text:");
|
|
1240
|
+
logger.error(err);
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
if (!messageText) {
|
|
1245
|
+
logger.debug("No message text found. Ignoring emoji reaction.");
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// Create a unique identifier for this Slack message to prevent duplicate notes
|
|
1250
|
+
const postedFromSlackMessageId: string = `${channelId}:${messageTs}`;
|
|
1251
|
+
|
|
1252
|
+
// Save the note based on the emoji type
|
|
1253
|
+
let noteType: string;
|
|
1254
|
+
try {
|
|
1255
|
+
if (isPrivateNoteEmoji) {
|
|
1256
|
+
noteType = "private";
|
|
1257
|
+
|
|
1258
|
+
// Check if a note from this Slack message already exists
|
|
1259
|
+
const hasExistingNote: boolean =
|
|
1260
|
+
await ScheduledMaintenanceInternalNoteService.hasNoteFromSlackMessage(
|
|
1261
|
+
{
|
|
1262
|
+
scheduledMaintenanceId: scheduledMaintenanceId,
|
|
1263
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
1264
|
+
},
|
|
1265
|
+
);
|
|
1266
|
+
|
|
1267
|
+
if (hasExistingNote) {
|
|
1268
|
+
logger.debug(
|
|
1269
|
+
"Private note from this Slack message already exists. Skipping duplicate.",
|
|
1270
|
+
);
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
await ScheduledMaintenanceInternalNoteService.addNote({
|
|
1275
|
+
scheduledMaintenanceId: scheduledMaintenanceId,
|
|
1276
|
+
note: messageText,
|
|
1277
|
+
projectId: projectId,
|
|
1278
|
+
userId: oneUptimeUserId,
|
|
1279
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
1280
|
+
});
|
|
1281
|
+
logger.debug("Private note added successfully.");
|
|
1282
|
+
} else if (isPublicNoteEmoji) {
|
|
1283
|
+
noteType = "public";
|
|
1284
|
+
|
|
1285
|
+
// Check if a note from this Slack message already exists
|
|
1286
|
+
const hasExistingNote: boolean =
|
|
1287
|
+
await ScheduledMaintenancePublicNoteService.hasNoteFromSlackMessage({
|
|
1288
|
+
scheduledMaintenanceId: scheduledMaintenanceId,
|
|
1289
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
if (hasExistingNote) {
|
|
1293
|
+
logger.debug(
|
|
1294
|
+
"Public note from this Slack message already exists. Skipping duplicate.",
|
|
1295
|
+
);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
await ScheduledMaintenancePublicNoteService.addNote({
|
|
1300
|
+
scheduledMaintenanceId: scheduledMaintenanceId,
|
|
1301
|
+
note: messageText,
|
|
1302
|
+
projectId: projectId,
|
|
1303
|
+
userId: oneUptimeUserId,
|
|
1304
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
1305
|
+
});
|
|
1306
|
+
logger.debug("Public note added successfully.");
|
|
1307
|
+
} else {
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
} catch (err) {
|
|
1311
|
+
logger.error("Error saving note:");
|
|
1312
|
+
logger.error(err);
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// Send confirmation message as a reply to the original message thread
|
|
1317
|
+
try {
|
|
1318
|
+
const scheduledMaintenanceLink: string = (
|
|
1319
|
+
await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(
|
|
1320
|
+
projectId,
|
|
1321
|
+
scheduledMaintenanceId,
|
|
1322
|
+
)
|
|
1323
|
+
).toString();
|
|
1324
|
+
|
|
1325
|
+
const confirmationMessage: string =
|
|
1326
|
+
noteType === "private"
|
|
1327
|
+
? `✅ Message saved as *private note* to <${scheduledMaintenanceLink}|Scheduled Maintenance #${scheduledMaintenanceNumber}>.`
|
|
1328
|
+
: `✅ Message saved as *public note* to <${scheduledMaintenanceLink}|Scheduled Maintenance #${scheduledMaintenanceNumber}>. This note will be visible on the status page.`;
|
|
1329
|
+
|
|
1330
|
+
await SlackUtil.sendMessageToThread({
|
|
1331
|
+
authToken: authToken,
|
|
1332
|
+
channelId: channelId,
|
|
1333
|
+
threadTs: messageTs,
|
|
1334
|
+
text: confirmationMessage,
|
|
1335
|
+
});
|
|
1336
|
+
|
|
1337
|
+
logger.debug("Confirmation message sent successfully.");
|
|
1338
|
+
} catch (err) {
|
|
1339
|
+
logger.error("Error sending confirmation message:");
|
|
1340
|
+
logger.error(err);
|
|
1341
|
+
// Don't throw - note was saved successfully, confirmation is best effort
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1113
1344
|
}
|
|
@@ -1156,6 +1156,119 @@ export default class SlackUtil extends WorkspaceBase {
|
|
|
1156
1156
|
};
|
|
1157
1157
|
}
|
|
1158
1158
|
|
|
1159
|
+
@CaptureSpan()
|
|
1160
|
+
public static async sendMessageToThread(data: {
|
|
1161
|
+
authToken: string;
|
|
1162
|
+
channelId: string;
|
|
1163
|
+
threadTs: string;
|
|
1164
|
+
text: string;
|
|
1165
|
+
}): Promise<void> {
|
|
1166
|
+
logger.debug("Sending message to thread with data:");
|
|
1167
|
+
logger.debug(data);
|
|
1168
|
+
|
|
1169
|
+
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
1170
|
+
await API.post({
|
|
1171
|
+
url: URL.fromString("https://slack.com/api/chat.postMessage"),
|
|
1172
|
+
data: {
|
|
1173
|
+
channel: data.channelId,
|
|
1174
|
+
thread_ts: data.threadTs,
|
|
1175
|
+
text: data.text,
|
|
1176
|
+
},
|
|
1177
|
+
headers: {
|
|
1178
|
+
Authorization: `Bearer ${data.authToken}`,
|
|
1179
|
+
["Content-Type"]: "application/json",
|
|
1180
|
+
},
|
|
1181
|
+
options: {
|
|
1182
|
+
retries: 3,
|
|
1183
|
+
exponentialBackoff: true,
|
|
1184
|
+
},
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
logger.debug("Response from Slack API for sending thread message:");
|
|
1188
|
+
logger.debug(response);
|
|
1189
|
+
|
|
1190
|
+
if (response instanceof HTTPErrorResponse) {
|
|
1191
|
+
logger.error("Error response from Slack API:");
|
|
1192
|
+
logger.error(response);
|
|
1193
|
+
throw response;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
|
1197
|
+
logger.error("Invalid response from Slack API:");
|
|
1198
|
+
logger.error(response.jsonData);
|
|
1199
|
+
const messageFromSlack: string = (response.jsonData as JSONObject)?.[
|
|
1200
|
+
"error"
|
|
1201
|
+
] as string;
|
|
1202
|
+
throw new BadRequestException("Error from Slack " + messageFromSlack);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
logger.debug("Thread message sent successfully.");
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
@CaptureSpan()
|
|
1209
|
+
public static async getMessageByTimestamp(data: {
|
|
1210
|
+
authToken: string;
|
|
1211
|
+
channelId: string;
|
|
1212
|
+
messageTs: string;
|
|
1213
|
+
}): Promise<string | null> {
|
|
1214
|
+
logger.debug("Getting message by timestamp with data:");
|
|
1215
|
+
logger.debug(data);
|
|
1216
|
+
|
|
1217
|
+
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
1218
|
+
await API.post({
|
|
1219
|
+
url: URL.fromString("https://slack.com/api/conversations.history"),
|
|
1220
|
+
data: {
|
|
1221
|
+
channel: data.channelId,
|
|
1222
|
+
latest: data.messageTs,
|
|
1223
|
+
oldest: data.messageTs,
|
|
1224
|
+
inclusive: true,
|
|
1225
|
+
limit: 1,
|
|
1226
|
+
},
|
|
1227
|
+
headers: {
|
|
1228
|
+
Authorization: `Bearer ${data.authToken}`,
|
|
1229
|
+
["Content-Type"]: "application/x-www-form-urlencoded",
|
|
1230
|
+
},
|
|
1231
|
+
options: {
|
|
1232
|
+
retries: 3,
|
|
1233
|
+
exponentialBackoff: true,
|
|
1234
|
+
},
|
|
1235
|
+
});
|
|
1236
|
+
|
|
1237
|
+
logger.debug("Response from Slack API for getting message:");
|
|
1238
|
+
logger.debug(response);
|
|
1239
|
+
|
|
1240
|
+
if (response instanceof HTTPErrorResponse) {
|
|
1241
|
+
logger.error("Error response from Slack API:");
|
|
1242
|
+
logger.error(response);
|
|
1243
|
+
throw response;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
|
1247
|
+
logger.error("Invalid response from Slack API:");
|
|
1248
|
+
logger.error(response.jsonData);
|
|
1249
|
+
const messageFromSlack: string = (response.jsonData as JSONObject)?.[
|
|
1250
|
+
"error"
|
|
1251
|
+
] as string;
|
|
1252
|
+
throw new BadRequestException("Error from Slack " + messageFromSlack);
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
const messages: Array<JSONObject> = (response.jsonData as JSONObject)?.[
|
|
1256
|
+
"messages"
|
|
1257
|
+
] as Array<JSONObject>;
|
|
1258
|
+
|
|
1259
|
+
if (!messages || messages.length === 0) {
|
|
1260
|
+
logger.debug("No messages found for timestamp.");
|
|
1261
|
+
return null;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
const messageText: string | undefined = messages[0]?.["text"] as string;
|
|
1265
|
+
|
|
1266
|
+
logger.debug("Message text retrieved:");
|
|
1267
|
+
logger.debug(messageText);
|
|
1268
|
+
|
|
1269
|
+
return messageText || null;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1159
1272
|
@CaptureSpan()
|
|
1160
1273
|
public static override getButtonsBlock(data: {
|
|
1161
1274
|
payloadButtonsBlock: WorkspacePayloadButtons;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"background_color": "#000000",
|
|
6
6
|
"long_description": "OneUptime is a comprehensive solution for monitoring and managing your online services. Whether you need to check the availability of your website, dashboard, API, or any other online resource, OneUptime can alert your team when downtime happens and keep your customers informed with a status page. OneUptime also helps you handle incidents, set up on-call rotations, run tests, secure your services, analyze logs, track performance, and debug errors."
|
|
7
7
|
},
|
|
8
|
+
|
|
8
9
|
"features": {
|
|
9
10
|
"app_home": {
|
|
10
11
|
"home_tab_enabled": false,
|
|
@@ -66,10 +67,14 @@
|
|
|
66
67
|
"users:read",
|
|
67
68
|
"groups:read",
|
|
68
69
|
"groups:write",
|
|
70
|
+
"groups:history",
|
|
69
71
|
"im:read",
|
|
70
72
|
"im:write",
|
|
73
|
+
"im:history",
|
|
71
74
|
"mpim:read",
|
|
72
|
-
"mpim:write"
|
|
75
|
+
"mpim:write",
|
|
76
|
+
"mpim:history",
|
|
77
|
+
"reactions:read"
|
|
73
78
|
]
|
|
74
79
|
}
|
|
75
80
|
},
|
|
@@ -81,6 +86,12 @@
|
|
|
81
86
|
},
|
|
82
87
|
"org_deploy_enabled": true,
|
|
83
88
|
"socket_mode_enabled": false,
|
|
84
|
-
"token_rotation_enabled": false
|
|
89
|
+
"token_rotation_enabled": false,
|
|
90
|
+
"event_subscriptions": {
|
|
91
|
+
"request_url": "{{SERVER_URL}}/api/slack/events",
|
|
92
|
+
"bot_events": [
|
|
93
|
+
"reaction_added"
|
|
94
|
+
]
|
|
95
|
+
}
|
|
85
96
|
}
|
|
86
97
|
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import StatusPageResourceUtil from "../../../Server/Utils/StatusPageResource";
|
|
2
|
+
import StatusPageResource from "../../../Models/DatabaseModels/StatusPageResource";
|
|
3
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
4
|
+
|
|
5
|
+
describe("StatusPageResourceUtil", () => {
|
|
6
|
+
describe("getResourcesGroupedByGroupName", () => {
|
|
7
|
+
it("should return empty string for empty resources array", () => {
|
|
8
|
+
const result: string =
|
|
9
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName([]);
|
|
10
|
+
expect(result).toBe("");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should return custom default value for empty resources array", () => {
|
|
14
|
+
const result: string =
|
|
15
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(
|
|
16
|
+
[],
|
|
17
|
+
"No resources",
|
|
18
|
+
);
|
|
19
|
+
expect(result).toBe("No resources");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should return simple comma-separated list when no resources have groups", () => {
|
|
23
|
+
const resources: Array<StatusPageResource> = [
|
|
24
|
+
createResource("API"),
|
|
25
|
+
createResource("Website"),
|
|
26
|
+
createResource("Database"),
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const result: string =
|
|
30
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
|
|
31
|
+
expect(result).toBe("API, Website, Database");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should group resources by their resource group name", () => {
|
|
35
|
+
const resources: Array<StatusPageResource> = [
|
|
36
|
+
createResourceWithGroup("Infrastructure", "EU"),
|
|
37
|
+
createResourceWithGroup("Website", "EU"),
|
|
38
|
+
createResourceWithGroup("Infrastructure", "UK"),
|
|
39
|
+
createResourceWithGroup("API", "UK"),
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const result: string =
|
|
43
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
|
|
44
|
+
/*
|
|
45
|
+
* Groups should be formatted as "GroupName: Resource1, Resource2"
|
|
46
|
+
* Multiple groups separated by <br/> for HTML rendering
|
|
47
|
+
*/
|
|
48
|
+
expect(result).toContain("EU: Infrastructure, Website");
|
|
49
|
+
expect(result).toContain("UK: Infrastructure, API");
|
|
50
|
+
expect(result).toContain("<br/>");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should handle mixed grouped and ungrouped resources", () => {
|
|
54
|
+
const resources: Array<StatusPageResource> = [
|
|
55
|
+
createResourceWithGroup("Infrastructure", "EU"),
|
|
56
|
+
createResource("Global API"),
|
|
57
|
+
createResourceWithGroup("Website", "UK"),
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const result: string =
|
|
61
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
|
|
62
|
+
expect(result).toContain("EU: Infrastructure");
|
|
63
|
+
expect(result).toContain("UK: Website");
|
|
64
|
+
// Ungrouped resources should appear on their own line without "Other" label
|
|
65
|
+
expect(result).toContain("Global API");
|
|
66
|
+
expect(result).not.toContain("Other:");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should handle single grouped resource", () => {
|
|
70
|
+
const resources: Array<StatusPageResource> = [
|
|
71
|
+
createResourceWithGroup("Infrastructure", "EU"),
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const result: string =
|
|
75
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
|
|
76
|
+
expect(result).toBe("EU: Infrastructure");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should handle single ungrouped resource", () => {
|
|
80
|
+
const resources: Array<StatusPageResource> = [createResource("API")];
|
|
81
|
+
|
|
82
|
+
const result: string =
|
|
83
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
|
|
84
|
+
expect(result).toBe("API");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should skip resources without displayName", () => {
|
|
88
|
+
const resources: Array<StatusPageResource> = [
|
|
89
|
+
createResource("API"),
|
|
90
|
+
createResourceWithoutDisplayName(),
|
|
91
|
+
createResource("Website"),
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const result: string =
|
|
95
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
|
|
96
|
+
expect(result).toBe("API, Website");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should handle null resources array", () => {
|
|
100
|
+
const result: string =
|
|
101
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(
|
|
102
|
+
null as unknown as Array<StatusPageResource>,
|
|
103
|
+
);
|
|
104
|
+
expect(result).toBe("");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should return empty string as default when specified", () => {
|
|
108
|
+
const result: string =
|
|
109
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName([], "");
|
|
110
|
+
expect(result).toBe("");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should handle multiple resources in same group", () => {
|
|
114
|
+
const resources: Array<StatusPageResource> = [
|
|
115
|
+
createResourceWithGroup("Infrastructure", "EU"),
|
|
116
|
+
createResourceWithGroup("Website", "EU"),
|
|
117
|
+
createResourceWithGroup("API", "EU"),
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
const result: string =
|
|
121
|
+
StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
|
|
122
|
+
expect(result).toBe("EU: Infrastructure, Website, API");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Helper function to create a StatusPageResource without a group
|
|
129
|
+
*/
|
|
130
|
+
function createResource(displayName: string): StatusPageResource {
|
|
131
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
132
|
+
resource._id = ObjectID.generate().toString();
|
|
133
|
+
resource.displayName = displayName;
|
|
134
|
+
return resource;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Helper function to create a StatusPageResource with a group
|
|
139
|
+
*/
|
|
140
|
+
function createResourceWithGroup(
|
|
141
|
+
displayName: string,
|
|
142
|
+
groupName: string,
|
|
143
|
+
): StatusPageResource {
|
|
144
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
145
|
+
resource._id = ObjectID.generate().toString();
|
|
146
|
+
resource.displayName = displayName;
|
|
147
|
+
resource.statusPageGroupId = ObjectID.generate();
|
|
148
|
+
resource.statusPageGroup = {
|
|
149
|
+
name: groupName,
|
|
150
|
+
} as any;
|
|
151
|
+
return resource;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Helper function to create a StatusPageResource without a displayName
|
|
156
|
+
*/
|
|
157
|
+
function createResourceWithoutDisplayName(): StatusPageResource {
|
|
158
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
159
|
+
resource._id = ObjectID.generate().toString();
|
|
160
|
+
return resource;
|
|
161
|
+
}
|
package/Types/Icon/IconProp.ts
CHANGED