@oneuptime/common 7.0.5014 → 7.0.5020
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/StatusPage.ts +39 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +59 -0
- package/Server/API/StatusPageAPI.ts +48 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1755775040650-MigrationName.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1755778495455-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1755778934927-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/StatusPageSubscriberService.ts +67 -9
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +113 -373
- package/build/dist/Models/DatabaseModels/StatusPage.js +40 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +61 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +27 -3
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1755775040650-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1755775040650-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1755778495455-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1755778495455-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1755778934927-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1755778934927-MigrationName.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/StatusPageSubscriberService.js +50 -9
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +93 -302
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,414 +3,154 @@ import HTTPResponse from "../../../../Types/API/HTTPResponse";
|
|
|
3
3
|
import URL from "../../../../Types/API/URL";
|
|
4
4
|
import { JSONObject } from "../../../../Types/JSON";
|
|
5
5
|
import API from "../../../../Utils/API";
|
|
6
|
-
import WorkspaceMessagePayload from "../../../../Types/Workspace/WorkspaceMessagePayload";
|
|
7
6
|
import logger from "../../Logger";
|
|
8
|
-
import
|
|
9
|
-
import WorkspaceBase, {
|
|
10
|
-
WorkspaceChannel,
|
|
11
|
-
WorkspaceSendMessageResponse,
|
|
12
|
-
WorkspaceThread,
|
|
13
|
-
} from "../WorkspaceBase";
|
|
14
|
-
import WorkspaceType from "../../../../Types/Workspace/WorkspaceType";
|
|
15
|
-
import OneUptimeDate from "../../../../Types/Date";
|
|
7
|
+
import WorkspaceBase from "../WorkspaceBase";
|
|
16
8
|
import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
17
|
-
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
18
9
|
|
|
19
10
|
export default class MicrosoftTeams extends WorkspaceBase {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const channels: Dictionary<WorkspaceChannel> = {};
|
|
28
|
-
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
29
|
-
await API.get<JSONObject>(
|
|
30
|
-
URL.fromString("https://graph.microsoft.com/v1.0/me/joinedTeams"),
|
|
31
|
-
{
|
|
32
|
-
Authorization: `Bearer ${data.authToken}`,
|
|
33
|
-
},
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
logger.debug("Response from Microsoft Graph API for getting all channels:");
|
|
37
|
-
logger.debug(JSON.stringify(response, null, 2));
|
|
38
|
-
|
|
39
|
-
if (response instanceof HTTPErrorResponse) {
|
|
40
|
-
logger.error("Error response from Microsoft Graph API:");
|
|
41
|
-
logger.error(response);
|
|
42
|
-
throw response;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
for (const team of (response.jsonData as JSONObject)?.[
|
|
46
|
-
"value"
|
|
47
|
-
] as Array<JSONObject>) {
|
|
48
|
-
if (!team["id"] || !team["displayName"]) {
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
channels[team["displayName"].toString()] = {
|
|
53
|
-
id: team["id"] as string,
|
|
54
|
-
name: team["displayName"] as string,
|
|
55
|
-
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
logger.debug("All workspace channels obtained:");
|
|
60
|
-
logger.debug(channels);
|
|
61
|
-
return channels;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
@CaptureSpan()
|
|
65
|
-
public static override getDividerBlock(): JSONObject {
|
|
66
|
-
return {
|
|
67
|
-
type: "divider",
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@CaptureSpan()
|
|
72
|
-
public static getValuesFromView(data: {
|
|
73
|
-
view: JSONObject;
|
|
74
|
-
}): Dictionary<string | number | Array<string | number> | Date> {
|
|
75
|
-
logger.debug("Getting values from view with data:");
|
|
76
|
-
logger.debug(JSON.stringify(data, null, 2));
|
|
77
|
-
|
|
78
|
-
const teamsView: JSONObject = data.view;
|
|
79
|
-
const values: Dictionary<string | number | Array<string | number> | Date> =
|
|
80
|
-
{};
|
|
81
|
-
|
|
82
|
-
if (!teamsView["state"] || !(teamsView["state"] as JSONObject)["values"]) {
|
|
83
|
-
return {};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (const valueId in (teamsView["state"] as JSONObject)[
|
|
87
|
-
"values"
|
|
88
|
-
] as JSONObject) {
|
|
89
|
-
for (const blockId in (
|
|
90
|
-
(teamsView["state"] as JSONObject)["values"] as JSONObject
|
|
91
|
-
)[valueId] as JSONObject) {
|
|
92
|
-
const valueObject: JSONObject = (
|
|
93
|
-
(teamsView["state"] as JSONObject)["values"] as JSONObject
|
|
94
|
-
)[valueId] as JSONObject;
|
|
95
|
-
const value: JSONObject = valueObject[blockId] as JSONObject;
|
|
96
|
-
values[blockId] = value["value"] as string | number;
|
|
97
|
-
|
|
98
|
-
if ((value["selected_option"] as JSONObject)?.["value"]) {
|
|
99
|
-
values[blockId] = (value["selected_option"] as JSONObject)?.[
|
|
100
|
-
"value"
|
|
101
|
-
] as string;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (Array.isArray(value["selected_options"])) {
|
|
105
|
-
values[blockId] = (
|
|
106
|
-
value["selected_options"] as Array<JSONObject>
|
|
107
|
-
).map((option: JSONObject) => {
|
|
108
|
-
return option["value"] as string | number;
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// if date picker
|
|
113
|
-
if (value["selected_date_time"]) {
|
|
114
|
-
values[blockId] = OneUptimeDate.fromUnixTimestamp(
|
|
115
|
-
value["selected_date_time"] as number,
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
logger.debug("Values obtained from view:");
|
|
122
|
-
logger.debug(values);
|
|
123
|
-
|
|
124
|
-
return values;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
@CaptureSpan()
|
|
128
|
-
public static override async inviteUserToChannelByChannelName(data: {
|
|
129
|
-
authToken: string;
|
|
130
|
-
channelName: string;
|
|
131
|
-
workspaceUserId: string;
|
|
132
|
-
}): Promise<void> {
|
|
133
|
-
logger.debug("Inviting user to channel with data:");
|
|
134
|
-
logger.debug(data);
|
|
135
|
-
|
|
136
|
-
const channelId: string = (
|
|
137
|
-
await this.getWorkspaceChannelFromChannelName({
|
|
138
|
-
authToken: data.authToken,
|
|
139
|
-
channelName: data.channelName,
|
|
11
|
+
private static buildMessageCardFromMarkdown(markdown: string): JSONObject {
|
|
12
|
+
// Teams MessageCard has limited markdown support. Headings like '##' are not supported
|
|
13
|
+
// and single newlines can collapse. Convert common patterns to a structured card.
|
|
14
|
+
const lines: Array<string> = markdown
|
|
15
|
+
.split("\n")
|
|
16
|
+
.map((l: string) => {
|
|
17
|
+
return l.trim();
|
|
140
18
|
})
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return this.inviteUserToChannelByChannelId({
|
|
144
|
-
authToken: data.authToken,
|
|
145
|
-
channelId: channelId,
|
|
146
|
-
workspaceUserId: data.workspaceUserId,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
@CaptureSpan()
|
|
151
|
-
public static override async createChannelsIfDoesNotExist(data: {
|
|
152
|
-
authToken: string;
|
|
153
|
-
channelNames: Array<string>;
|
|
154
|
-
}): Promise<Array<WorkspaceChannel>> {
|
|
155
|
-
logger.debug("Creating channels if they do not exist with data:");
|
|
156
|
-
logger.debug(data);
|
|
157
|
-
|
|
158
|
-
const workspaceChannels: Array<WorkspaceChannel> = [];
|
|
159
|
-
const existingWorkspaceChannels: Dictionary<WorkspaceChannel> =
|
|
160
|
-
await this.getAllWorkspaceChannels({
|
|
161
|
-
authToken: data.authToken,
|
|
19
|
+
.filter((l: string) => {
|
|
20
|
+
return l.length > 0;
|
|
162
21
|
});
|
|
163
22
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
23
|
+
let title: string = "";
|
|
24
|
+
const facts: Array<JSONObject> = [];
|
|
25
|
+
const actions: Array<JSONObject> = [];
|
|
26
|
+
const bodyTextParts: Array<string> = [];
|
|
27
|
+
|
|
28
|
+
// Extract title from the first non-empty line and strip markdown heading markers
|
|
29
|
+
if (lines.length > 0) {
|
|
30
|
+
const firstLine: string = lines[0] ?? "";
|
|
31
|
+
title = firstLine
|
|
32
|
+
.replace(/^#+\s*/, "") // remove leading markdown headers like ##
|
|
33
|
+
.replace(/^\*\*|\*\*$/g, "") // remove stray bold markers if any
|
|
34
|
+
.trim();
|
|
35
|
+
lines.shift();
|
|
36
|
+
}
|
|
178
37
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
38
|
+
const linkRegex: RegExp = /\[([^\]]+)\]\(([^)]+)\)/g; // [text](url)
|
|
39
|
+
|
|
40
|
+
for (const line of lines) {
|
|
41
|
+
// Extract links to actions and strip them from text
|
|
42
|
+
let lineWithoutLinks: string = line;
|
|
43
|
+
let match: RegExpExecArray | null = null;
|
|
44
|
+
while ((match = linkRegex.exec(line))) {
|
|
45
|
+
const name: string = match[1] ?? "";
|
|
46
|
+
const url: string = match[2] ?? "";
|
|
47
|
+
actions.push({
|
|
48
|
+
["@type"]: "OpenUri",
|
|
49
|
+
name: name,
|
|
50
|
+
targets: [
|
|
51
|
+
{
|
|
52
|
+
os: "default",
|
|
53
|
+
uri: url,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
});
|
|
57
|
+
lineWithoutLinks = lineWithoutLinks.replace(match[0], "").trim();
|
|
183
58
|
}
|
|
184
59
|
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
});
|
|
60
|
+
// Parse facts of the form **Label:** value
|
|
61
|
+
const factMatch: RegExpExecArray | null = /\*\*(.*?)\:\*\*\s*(.*)/.exec(
|
|
62
|
+
lineWithoutLinks,
|
|
63
|
+
);
|
|
190
64
|
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
65
|
+
if (factMatch) {
|
|
66
|
+
const name: string = (factMatch[1] ?? "").trim();
|
|
67
|
+
const value: string = (factMatch[2] ?? "").trim();
|
|
68
|
+
if (
|
|
69
|
+
name.toLowerCase() === "description" ||
|
|
70
|
+
name.toLowerCase() === "note"
|
|
71
|
+
) {
|
|
72
|
+
bodyTextParts.push(`**${name}:** ${value}`);
|
|
73
|
+
} else {
|
|
74
|
+
facts.push({ name: name, value: value });
|
|
75
|
+
}
|
|
76
|
+
} else if (lineWithoutLinks) {
|
|
77
|
+
bodyTextParts.push(lineWithoutLinks);
|
|
194
78
|
}
|
|
195
79
|
}
|
|
196
80
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
public static override async getWorkspaceChannelFromChannelName(data: {
|
|
204
|
-
authToken: string;
|
|
205
|
-
channelName: string;
|
|
206
|
-
}): Promise<WorkspaceChannel> {
|
|
207
|
-
logger.debug("Getting workspace channel ID from channel name with data:");
|
|
208
|
-
logger.debug(data);
|
|
209
|
-
|
|
210
|
-
const channels: Dictionary<WorkspaceChannel> =
|
|
211
|
-
await this.getAllWorkspaceChannels({
|
|
212
|
-
authToken: data.authToken,
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
logger.debug("All workspace channels:");
|
|
216
|
-
logger.debug(channels);
|
|
81
|
+
const payload: JSONObject = {
|
|
82
|
+
["@type"]: "MessageCard",
|
|
83
|
+
["@context"]: "https://schema.org/extensions",
|
|
84
|
+
title: title,
|
|
85
|
+
summary: title,
|
|
86
|
+
};
|
|
217
87
|
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
throw new BadDataException("Channel not found.");
|
|
88
|
+
if (bodyTextParts.length > 0) {
|
|
89
|
+
payload["text"] = bodyTextParts.join("\n\n");
|
|
221
90
|
}
|
|
222
91
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return channels[data.channelName]!;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
@CaptureSpan()
|
|
230
|
-
public static override async getWorkspaceChannelFromChannelId(data: {
|
|
231
|
-
authToken: string;
|
|
232
|
-
channelId: string;
|
|
233
|
-
}): Promise<WorkspaceChannel> {
|
|
234
|
-
logger.debug("Getting workspace channel from channel ID with data:");
|
|
235
|
-
logger.debug(data);
|
|
236
|
-
|
|
237
|
-
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
238
|
-
await API.get<JSONObject>(
|
|
239
|
-
URL.fromString(
|
|
240
|
-
`https://graph.microsoft.com/v1.0/teams/${data.channelId}`,
|
|
241
|
-
),
|
|
92
|
+
if (facts.length > 0) {
|
|
93
|
+
payload["sections"] = [
|
|
242
94
|
{
|
|
243
|
-
|
|
95
|
+
facts: facts,
|
|
244
96
|
},
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
logger.debug("Response from Microsoft Graph API for getting channel info:");
|
|
248
|
-
logger.debug(response);
|
|
249
|
-
|
|
250
|
-
if (response instanceof HTTPErrorResponse) {
|
|
251
|
-
logger.error("Error response from Microsoft Graph API:");
|
|
252
|
-
logger.error(response);
|
|
253
|
-
throw response;
|
|
97
|
+
];
|
|
254
98
|
}
|
|
255
99
|
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
logger.error(response.jsonData);
|
|
259
|
-
throw new Error("Invalid response");
|
|
100
|
+
if (actions.length > 0) {
|
|
101
|
+
payload["potentialAction"] = actions;
|
|
260
102
|
}
|
|
261
103
|
|
|
262
|
-
|
|
263
|
-
name: (response.jsonData as JSONObject)["displayName"] as string,
|
|
264
|
-
id: data.channelId,
|
|
265
|
-
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
logger.debug("Workspace channel obtained:");
|
|
269
|
-
logger.debug(channel);
|
|
270
|
-
return channel;
|
|
104
|
+
return payload;
|
|
271
105
|
}
|
|
272
106
|
|
|
273
107
|
@CaptureSpan()
|
|
274
|
-
public static override async
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}): Promise<
|
|
278
|
-
|
|
279
|
-
if (data.channelName && data.channelName.startsWith("#")) {
|
|
280
|
-
data.channelName = data.channelName.substring(1);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// convert channel name to lowercase
|
|
284
|
-
data.channelName = data.channelName.toLowerCase();
|
|
285
|
-
|
|
286
|
-
// get channel id from channel name
|
|
287
|
-
const channels: Dictionary<WorkspaceChannel> =
|
|
288
|
-
await this.getAllWorkspaceChannels({
|
|
289
|
-
authToken: data.authToken,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// if this channel exists
|
|
293
|
-
if (channels[data.channelName]) {
|
|
294
|
-
return true;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return false;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
@CaptureSpan()
|
|
301
|
-
public static override async sendMessage(data: {
|
|
302
|
-
workspaceMessagePayload: WorkspaceMessagePayload;
|
|
303
|
-
authToken: string; // which auth token should we use to send.
|
|
304
|
-
userId: string;
|
|
305
|
-
}): Promise<WorkspaceSendMessageResponse> {
|
|
306
|
-
logger.debug("Sending message to Microsoft Teams with data:");
|
|
108
|
+
public static override async sendMessageToChannelViaIncomingWebhook(data: {
|
|
109
|
+
url: URL;
|
|
110
|
+
text: string;
|
|
111
|
+
}): Promise<HTTPResponse<JSONObject> | HTTPErrorResponse> {
|
|
112
|
+
logger.debug("Sending message to Teams channel via incoming webhook:");
|
|
307
113
|
logger.debug(data);
|
|
308
114
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
logger.debug("Blocks generated from workspace message payload:");
|
|
314
|
-
logger.debug(blocks);
|
|
315
|
-
|
|
316
|
-
const existingWorkspaceChannels: Dictionary<WorkspaceChannel> =
|
|
317
|
-
await this.getAllWorkspaceChannels({
|
|
318
|
-
authToken: data.authToken,
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
logger.debug("Existing workspace channels:");
|
|
322
|
-
logger.debug(existingWorkspaceChannels);
|
|
323
|
-
|
|
324
|
-
const workspaceChannelsToPostTo: Array<WorkspaceChannel> = [];
|
|
325
|
-
|
|
326
|
-
for (let channelName of data.workspaceMessagePayload.channelNames) {
|
|
327
|
-
if (channelName && channelName.startsWith("#")) {
|
|
328
|
-
// trim # from channel name
|
|
329
|
-
channelName = channelName.substring(1);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
let channel: WorkspaceChannel | null = null;
|
|
115
|
+
// Build a structured MessageCard from markdown for better rendering in Teams
|
|
116
|
+
const payload: JSONObject = this.buildMessageCardFromMarkdown(data.text);
|
|
333
117
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
118
|
+
const apiResult: HTTPResponse<JSONObject> | HTTPErrorResponse | null =
|
|
119
|
+
await API.post(data.url, payload);
|
|
337
120
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
121
|
+
if (!apiResult) {
|
|
122
|
+
logger.error(
|
|
123
|
+
"Could not send message to Teams channel via incoming webhook.",
|
|
124
|
+
);
|
|
125
|
+
throw new Error(
|
|
126
|
+
"Could not send message to Teams channel via incoming webhook.",
|
|
127
|
+
);
|
|
343
128
|
}
|
|
344
129
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
authToken: data.authToken,
|
|
352
|
-
channelId: channelId,
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
workspaceChannelsToPostTo.push(channel);
|
|
356
|
-
} catch (err) {
|
|
357
|
-
logger.error(`Error getting channel info for channel ID ${channelId}:`);
|
|
358
|
-
logger.error(err);
|
|
359
|
-
|
|
360
|
-
// Fallback: create channel object with empty name if API call fails
|
|
361
|
-
const channel: WorkspaceChannel = {
|
|
362
|
-
id: channelId,
|
|
363
|
-
name: channelId,
|
|
364
|
-
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
workspaceChannelsToPostTo.push(channel);
|
|
368
|
-
}
|
|
130
|
+
if (apiResult instanceof HTTPErrorResponse) {
|
|
131
|
+
logger.error(
|
|
132
|
+
"Error sending message to Teams channel via incoming webhook:",
|
|
133
|
+
);
|
|
134
|
+
logger.error(apiResult);
|
|
135
|
+
throw apiResult;
|
|
369
136
|
}
|
|
370
137
|
|
|
371
|
-
logger.debug(
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
threads: [],
|
|
376
|
-
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
for (const channel of workspaceChannelsToPostTo) {
|
|
380
|
-
try {
|
|
381
|
-
// check if the user is in the channel.
|
|
382
|
-
const isUserInChannel: boolean = await this.isUserInChannel({
|
|
383
|
-
authToken: data.authToken,
|
|
384
|
-
channelId: channel.id,
|
|
385
|
-
userId: data.userId,
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
if (!isUserInChannel) {
|
|
389
|
-
// add user to the channel
|
|
390
|
-
await this.joinChannel({
|
|
391
|
-
authToken: data.authToken,
|
|
392
|
-
channelId: channel.id,
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const thread: WorkspaceThread = await this.sendPayloadBlocksToChannel({
|
|
397
|
-
authToken: data.authToken,
|
|
398
|
-
workspaceChannel: channel,
|
|
399
|
-
blocks: blocks,
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
workspaceMessageResponse.threads.push(thread);
|
|
403
|
-
|
|
404
|
-
logger.debug(`Message sent to channel ID ${channel.id} successfully.`);
|
|
405
|
-
} catch (e) {
|
|
406
|
-
logger.error(`Error sending message to channel ID ${channel.id}:`);
|
|
407
|
-
logger.error(e);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
138
|
+
logger.debug(
|
|
139
|
+
"Message sent to Teams channel via incoming webhook successfully:",
|
|
140
|
+
);
|
|
141
|
+
logger.debug(apiResult);
|
|
410
142
|
|
|
411
|
-
|
|
412
|
-
|
|
143
|
+
return apiResult;
|
|
144
|
+
}
|
|
413
145
|
|
|
414
|
-
|
|
146
|
+
public static isValidMicrosoftTeamsIncomingWebhookUrl(
|
|
147
|
+
incomingWebhookUrl: URL,
|
|
148
|
+
): boolean {
|
|
149
|
+
// Check if the URL contains outlook.office.com or office.com webhook pattern
|
|
150
|
+
const urlString: string = incomingWebhookUrl.toString();
|
|
151
|
+
return (
|
|
152
|
+
urlString.includes("outlook.office.com") ||
|
|
153
|
+
urlString.includes("office.com")
|
|
154
|
+
);
|
|
415
155
|
}
|
|
416
156
|
}
|
|
@@ -75,6 +75,7 @@ let StatusPage = class StatusPage extends BaseModel {
|
|
|
75
75
|
this.allowSubscribersToChooseEventTypes = undefined;
|
|
76
76
|
this.enableSmsSubscribers = undefined;
|
|
77
77
|
this.enableSlackSubscribers = undefined;
|
|
78
|
+
this.enableMicrosoftTeamsSubscribers = undefined;
|
|
78
79
|
this.copyrightText = undefined;
|
|
79
80
|
this.customFields = undefined;
|
|
80
81
|
this.requireSsoForLogin = undefined;
|
|
@@ -1198,6 +1199,45 @@ __decorate([
|
|
|
1198
1199
|
}),
|
|
1199
1200
|
__metadata("design:type", Boolean)
|
|
1200
1201
|
], StatusPage.prototype, "enableSlackSubscribers", void 0);
|
|
1202
|
+
__decorate([
|
|
1203
|
+
ColumnAccessControl({
|
|
1204
|
+
create: [
|
|
1205
|
+
Permission.ProjectOwner,
|
|
1206
|
+
Permission.ProjectAdmin,
|
|
1207
|
+
Permission.ProjectMember,
|
|
1208
|
+
Permission.CreateProjectStatusPage,
|
|
1209
|
+
],
|
|
1210
|
+
read: [
|
|
1211
|
+
Permission.ProjectOwner,
|
|
1212
|
+
Permission.ProjectAdmin,
|
|
1213
|
+
Permission.ProjectMember,
|
|
1214
|
+
Permission.ReadProjectStatusPage,
|
|
1215
|
+
],
|
|
1216
|
+
update: [
|
|
1217
|
+
Permission.ProjectOwner,
|
|
1218
|
+
Permission.ProjectAdmin,
|
|
1219
|
+
Permission.ProjectMember,
|
|
1220
|
+
Permission.EditProjectStatusPage,
|
|
1221
|
+
],
|
|
1222
|
+
}),
|
|
1223
|
+
TableColumn({
|
|
1224
|
+
isDefaultValueColumn: true,
|
|
1225
|
+
type: TableColumnType.Boolean,
|
|
1226
|
+
title: "Enable Microsoft Teams Subscribers",
|
|
1227
|
+
description: "Can Microsoft Teams subscribers subscribe to this Status Page?",
|
|
1228
|
+
defaultValue: false,
|
|
1229
|
+
}),
|
|
1230
|
+
Column({
|
|
1231
|
+
type: ColumnType.Boolean,
|
|
1232
|
+
default: false,
|
|
1233
|
+
}),
|
|
1234
|
+
ColumnBillingAccessControl({
|
|
1235
|
+
read: PlanType.Free,
|
|
1236
|
+
update: PlanType.Scale,
|
|
1237
|
+
create: PlanType.Free,
|
|
1238
|
+
}),
|
|
1239
|
+
__metadata("design:type", Boolean)
|
|
1240
|
+
], StatusPage.prototype, "enableMicrosoftTeamsSubscribers", void 0);
|
|
1201
1241
|
__decorate([
|
|
1202
1242
|
ColumnAccessControl({
|
|
1203
1243
|
create: [
|