@oneuptime/common 8.0.5387 → 8.0.5409
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/AnalyticsModels/Index.ts +0 -2
- package/Models/DatabaseModels/TelemetryException.ts +0 -7
- package/Server/API/MicrosoftTeamsAPI.ts +3 -4
- package/Server/API/UserOnCallLogTimelineAPI.ts +21 -17
- package/Server/DatabaseConfig.ts +7 -2
- package/Server/EnvironmentConfig.ts +20 -7
- package/Server/Infrastructure/GlobalCache.ts +12 -5
- package/Server/Services/AlertService.ts +0 -10
- package/Server/Services/AnalyticsDatabaseService.ts +5 -0
- package/Server/Services/IncidentService.ts +0 -10
- package/Server/Services/Index.ts +0 -2
- package/Server/Services/TeamMemberService.ts +11 -2
- package/Server/Services/TelemetryAttributeService.ts +261 -48
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +33 -0
- package/Server/Utils/Monitor/MonitorResource.ts +0 -16
- package/Server/Utils/Telemetry/Telemetry.ts +0 -61
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +149 -14
- package/Types/Permission.ts +0 -3
- package/UI/Components/Filters/FilterViewer.tsx +5 -1
- package/UI/Components/Filters/FiltersForm.tsx +8 -1
- package/UI/Components/List/List.tsx +4 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +102 -55
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +1 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +6 -0
- package/UI/Components/Table/Table.tsx +4 -0
- package/UI/Config.ts +28 -19
- package/UI/Utils/API/ApiDocsAPI.ts +6 -1
- package/UI/Utils/API/DashboardAPI.ts +2 -1
- package/UI/Utils/API/IdentityAPI.ts +6 -1
- package/build/dist/Models/AnalyticsModels/Index.js +0 -2
- package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryException.js +0 -7
- package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +4 -4
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +6 -3
- package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
- package/build/dist/Server/DatabaseConfig.js +3 -2
- package/build/dist/Server/DatabaseConfig.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +8 -7
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +11 -9
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +0 -9
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +0 -9
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +0 -2
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +3 -2
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +165 -46
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +25 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +0 -15
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +0 -41
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +84 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Permission.js +0 -2
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/Filters/FilterViewer.js +2 -2
- package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js +6 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
- package/build/dist/UI/Components/List/List.js +1 -1
- package/build/dist/UI/Components/List/List.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +43 -16
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +1 -1
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Config.js +20 -19
- package/build/dist/UI/Config.js.map +1 -1
- package/build/dist/UI/Utils/API/ApiDocsAPI.js +2 -1
- package/build/dist/UI/Utils/API/ApiDocsAPI.js.map +1 -1
- package/build/dist/UI/Utils/API/DashboardAPI.js +2 -1
- package/build/dist/UI/Utils/API/DashboardAPI.js.map +1 -1
- package/build/dist/UI/Utils/API/IdentityAPI.js +2 -1
- package/build/dist/UI/Utils/API/IdentityAPI.js.map +1 -1
- package/package.json +1 -1
- package/Models/AnalyticsModels/TelemetryAttribute.ts +0 -164
- package/build/dist/Models/AnalyticsModels/TelemetryAttribute.js +0 -154
- package/build/dist/Models/AnalyticsModels/TelemetryAttribute.js.map +0 -1
|
@@ -98,6 +98,10 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
if (!microsoftAppTenantId) {
|
|
102
|
+
throw new BadDataException("Microsoft Teams tenant ID is required");
|
|
103
|
+
}
|
|
104
|
+
|
|
101
105
|
logger.debug(
|
|
102
106
|
"Creating Bot Framework adapter with authentication configuration",
|
|
103
107
|
);
|
|
@@ -125,6 +129,12 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
125
129
|
projectId: ObjectID;
|
|
126
130
|
}): Promise<string> {
|
|
127
131
|
logger.debug("=== getValidAccessToken called ===");
|
|
132
|
+
|
|
133
|
+
if (!data.projectId) {
|
|
134
|
+
throw new BadDataException(
|
|
135
|
+
"projectId is required to get Microsoft Teams access token",
|
|
136
|
+
);
|
|
137
|
+
}
|
|
128
138
|
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
129
139
|
logger.debug(
|
|
130
140
|
`Auth token (first 20 chars): ${data.authToken?.substring(0, 20)}...`,
|
|
@@ -155,6 +165,19 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
155
165
|
|
|
156
166
|
const miscData: MicrosoftTeamsMiscData =
|
|
157
167
|
projectAuth.miscData as MicrosoftTeamsMiscData;
|
|
168
|
+
const tenantId: string | undefined = projectAuth.workspaceProjectId;
|
|
169
|
+
|
|
170
|
+
logger.debug(`Resolved tenant ID: ${tenantId}`);
|
|
171
|
+
|
|
172
|
+
if (!tenantId) {
|
|
173
|
+
logger.error(
|
|
174
|
+
"Microsoft Teams tenant ID missing from project auth configuration",
|
|
175
|
+
);
|
|
176
|
+
throw new BadDataException(
|
|
177
|
+
"Microsoft Teams tenant ID not found for this project",
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
158
181
|
logger.debug(
|
|
159
182
|
`MiscData appAccessToken exists: ${Boolean(miscData.appAccessToken)}`,
|
|
160
183
|
);
|
|
@@ -184,6 +207,7 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
184
207
|
const newToken: string | null = await this.refreshAccessToken({
|
|
185
208
|
projectId: data.projectId,
|
|
186
209
|
miscData,
|
|
210
|
+
tenantId,
|
|
187
211
|
});
|
|
188
212
|
if (newToken) {
|
|
189
213
|
logger.debug("Successfully refreshed token");
|
|
@@ -210,6 +234,7 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
210
234
|
const newToken: string | null = await this.refreshAccessToken({
|
|
211
235
|
projectId: data.projectId,
|
|
212
236
|
miscData,
|
|
237
|
+
tenantId,
|
|
213
238
|
});
|
|
214
239
|
if (newToken) {
|
|
215
240
|
logger.debug("Successfully refreshed token");
|
|
@@ -227,10 +252,24 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
227
252
|
private static async refreshAccessToken(data: {
|
|
228
253
|
projectId: ObjectID;
|
|
229
254
|
miscData: MicrosoftTeamsMiscData;
|
|
255
|
+
tenantId: string;
|
|
230
256
|
}): Promise<string | null> {
|
|
231
257
|
logger.debug("=== refreshAccessToken called ===");
|
|
258
|
+
|
|
259
|
+
if (!data.projectId) {
|
|
260
|
+
throw new BadDataException(
|
|
261
|
+
"projectId is required to refresh Microsoft Teams access token",
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!data.miscData) {
|
|
266
|
+
throw new BadDataException(
|
|
267
|
+
"miscData is required to refresh Microsoft Teams access token",
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
232
271
|
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
233
|
-
logger.debug(`Tenant ID: ${data.
|
|
272
|
+
logger.debug(`Tenant ID: ${data.tenantId}`);
|
|
234
273
|
|
|
235
274
|
try {
|
|
236
275
|
// Check if we have the necessary client credentials
|
|
@@ -246,18 +285,18 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
246
285
|
|
|
247
286
|
logger.debug("Client credentials are configured");
|
|
248
287
|
|
|
249
|
-
if (!data.
|
|
250
|
-
logger.error("Tenant ID not
|
|
288
|
+
if (!data.tenantId) {
|
|
289
|
+
logger.error("Tenant ID not provided, cannot refresh token");
|
|
251
290
|
return null;
|
|
252
291
|
}
|
|
253
292
|
|
|
254
293
|
logger.debug(
|
|
255
294
|
`Attempting to refresh Microsoft Teams access token for project ${data.projectId.toString()}`,
|
|
256
295
|
);
|
|
257
|
-
logger.debug(`Using tenant ID: ${data.
|
|
296
|
+
logger.debug(`Using tenant ID: ${data.tenantId}`);
|
|
258
297
|
|
|
259
298
|
// Use OAuth 2.0 client credentials flow to get a new app access token
|
|
260
|
-
const tokenUrl: string = `https://login.microsoftonline.com/${data.
|
|
299
|
+
const tokenUrl: string = `https://login.microsoftonline.com/${data.tenantId}/oauth2/v2.0/token`;
|
|
261
300
|
logger.debug(`Token URL: ${tokenUrl}`);
|
|
262
301
|
|
|
263
302
|
const tokenRequestBody: JSONObject = {
|
|
@@ -314,6 +353,7 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
314
353
|
appAccessToken: newAccessToken,
|
|
315
354
|
appAccessTokenExpiresAt: OneUptimeDate.toString(expiryDate),
|
|
316
355
|
lastAppTokenIssuedAt: OneUptimeDate.toString(now),
|
|
356
|
+
tenantId: data.tenantId,
|
|
317
357
|
};
|
|
318
358
|
|
|
319
359
|
logger.debug("Saving updated token to database");
|
|
@@ -322,7 +362,7 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
322
362
|
projectId: data.projectId,
|
|
323
363
|
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
324
364
|
authToken: newAccessToken,
|
|
325
|
-
workspaceProjectId: data.
|
|
365
|
+
workspaceProjectId: data.tenantId,
|
|
326
366
|
miscData: updatedMiscData as any,
|
|
327
367
|
});
|
|
328
368
|
|
|
@@ -780,6 +820,24 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
780
820
|
projectId: ObjectID;
|
|
781
821
|
teamId: string;
|
|
782
822
|
}): Promise<WorkspaceChannel | null> {
|
|
823
|
+
if (!data.projectId) {
|
|
824
|
+
throw new BadDataException(
|
|
825
|
+
"projectId is required to get Microsoft Teams channel by name",
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
if (!data.teamId) {
|
|
830
|
+
throw new BadDataException(
|
|
831
|
+
"teamId is required to get Microsoft Teams channel by name",
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
if (!data.channelName) {
|
|
836
|
+
throw new BadDataException(
|
|
837
|
+
"channelName is required to get Microsoft Teams channel by name",
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
|
|
783
841
|
logger.debug(`Getting workspace channel by name: ${data.channelName}`);
|
|
784
842
|
|
|
785
843
|
// Get project auth to get available teams
|
|
@@ -1004,6 +1062,36 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1004
1062
|
adaptiveCard: JSONObject;
|
|
1005
1063
|
projectId: ObjectID;
|
|
1006
1064
|
}): Promise<WorkspaceThread> {
|
|
1065
|
+
if (!data.projectId) {
|
|
1066
|
+
throw new BadDataException(
|
|
1067
|
+
"projectId is required to send Microsoft Teams adaptive card",
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (!data.teamId) {
|
|
1072
|
+
throw new BadDataException(
|
|
1073
|
+
"teamId is required to send Microsoft Teams adaptive card",
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
if (!data.workspaceChannel) {
|
|
1078
|
+
throw new BadDataException(
|
|
1079
|
+
"workspaceChannel is required to send Microsoft Teams adaptive card",
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
if (!data.workspaceChannel.id) {
|
|
1084
|
+
throw new BadDataException(
|
|
1085
|
+
"workspaceChannel.id is required to send Microsoft Teams adaptive card",
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (!data.adaptiveCard) {
|
|
1090
|
+
throw new BadDataException(
|
|
1091
|
+
"adaptiveCard is required to send Microsoft Teams adaptive card",
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1007
1095
|
logger.debug(
|
|
1008
1096
|
`Sending adaptive card to channel via Bot Framework: ${data.workspaceChannel.name} (${data.workspaceChannel.id})`,
|
|
1009
1097
|
);
|
|
@@ -1032,6 +1120,14 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1032
1120
|
);
|
|
1033
1121
|
}
|
|
1034
1122
|
|
|
1123
|
+
const tenantId: string | undefined = projectAuth.workspaceProjectId;
|
|
1124
|
+
|
|
1125
|
+
if (!tenantId) {
|
|
1126
|
+
throw new BadDataException(
|
|
1127
|
+
"Tenant ID not found in Microsoft Teams integration",
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1035
1131
|
// Check if app client ID is configured
|
|
1036
1132
|
if (!MicrosoftTeamsAppClientId) {
|
|
1037
1133
|
throw new BadDataException(
|
|
@@ -1042,7 +1138,7 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1042
1138
|
logger.debug(`Using bot ID: ${miscData.botId}`);
|
|
1043
1139
|
|
|
1044
1140
|
// Get Bot Framework adapter
|
|
1045
|
-
const adapter: CloudAdapter = this.getBotAdapter(
|
|
1141
|
+
const adapter: CloudAdapter = this.getBotAdapter(tenantId);
|
|
1046
1142
|
|
|
1047
1143
|
// Create conversation reference for the channel
|
|
1048
1144
|
const conversationReference: ConversationReference = {
|
|
@@ -1055,7 +1151,7 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1055
1151
|
name: data.workspaceChannel.name,
|
|
1056
1152
|
isGroup: true,
|
|
1057
1153
|
conversationType: "channel",
|
|
1058
|
-
tenantId:
|
|
1154
|
+
tenantId: tenantId,
|
|
1059
1155
|
},
|
|
1060
1156
|
channelId: "msteams",
|
|
1061
1157
|
serviceUrl: "https://smba.trafficmanager.net/teams/",
|
|
@@ -1113,6 +1209,25 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1113
1209
|
projectId: ObjectID;
|
|
1114
1210
|
}): Promise<WorkspaceChannel> {
|
|
1115
1211
|
logger.debug("=== getWorkspaceChannelFromChannelId called ===");
|
|
1212
|
+
|
|
1213
|
+
if (!data.projectId) {
|
|
1214
|
+
throw new BadDataException(
|
|
1215
|
+
"projectId is required to get Microsoft Teams channel by ID",
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
if (!data.teamId) {
|
|
1220
|
+
throw new BadDataException(
|
|
1221
|
+
"teamId is required to get Microsoft Teams channel by ID",
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
if (!data.channelId) {
|
|
1226
|
+
throw new BadDataException(
|
|
1227
|
+
"channelId is required to get Microsoft Teams channel by ID",
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1116
1231
|
logger.debug(`Channel ID: ${data.channelId}`);
|
|
1117
1232
|
logger.debug(`Team ID: ${data.teamId}`);
|
|
1118
1233
|
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
@@ -1638,13 +1753,11 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
1638
1753
|
await WorkspaceProjectAuthTokenService.findOneBy({
|
|
1639
1754
|
query: {
|
|
1640
1755
|
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1641
|
-
|
|
1642
|
-
tenantId: tenantId,
|
|
1643
|
-
} as any,
|
|
1756
|
+
workspaceProjectId: tenantId,
|
|
1644
1757
|
},
|
|
1645
1758
|
select: {
|
|
1646
1759
|
projectId: true,
|
|
1647
|
-
|
|
1760
|
+
workspaceProjectId: true,
|
|
1648
1761
|
},
|
|
1649
1762
|
props: {
|
|
1650
1763
|
isRoot: true,
|
|
@@ -2231,9 +2344,13 @@ All monitoring checks are passing normally.`;
|
|
|
2231
2344
|
await WorkspaceProjectAuthTokenService.findOneBy({
|
|
2232
2345
|
query: {
|
|
2233
2346
|
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
2234
|
-
|
|
2347
|
+
workspaceProjectId: tenantId,
|
|
2348
|
+
},
|
|
2349
|
+
select: {
|
|
2350
|
+
projectId: true,
|
|
2351
|
+
authToken: true,
|
|
2352
|
+
workspaceProjectId: true,
|
|
2235
2353
|
},
|
|
2236
|
-
select: { projectId: true, authToken: true },
|
|
2237
2354
|
props: { isRoot: true },
|
|
2238
2355
|
});
|
|
2239
2356
|
|
|
@@ -2683,6 +2800,13 @@ All monitoring checks are passing normally.`;
|
|
|
2683
2800
|
projectId: ObjectID;
|
|
2684
2801
|
}): Promise<Record<string, { id: string; name: string }>> {
|
|
2685
2802
|
logger.debug("=== refreshTeams called ===");
|
|
2803
|
+
|
|
2804
|
+
if (!data.projectId) {
|
|
2805
|
+
throw new BadDataException(
|
|
2806
|
+
"projectId is required to refresh Microsoft Teams teams",
|
|
2807
|
+
);
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2686
2810
|
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
2687
2811
|
|
|
2688
2812
|
try {
|
|
@@ -2699,10 +2823,19 @@ All monitoring checks are passing normally.`;
|
|
|
2699
2823
|
);
|
|
2700
2824
|
}
|
|
2701
2825
|
|
|
2826
|
+
const tenantId: string | undefined = projectAuth.workspaceProjectId;
|
|
2827
|
+
|
|
2828
|
+
if (!tenantId) {
|
|
2829
|
+
throw new BadDataException(
|
|
2830
|
+
"Microsoft Teams tenant ID not found for this project",
|
|
2831
|
+
);
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2702
2834
|
// Get a valid app access token
|
|
2703
2835
|
const accessToken: string | null = await this.refreshAccessToken({
|
|
2704
2836
|
projectId: data.projectId,
|
|
2705
2837
|
miscData: projectAuth.miscData as MicrosoftTeamsMiscData,
|
|
2838
|
+
tenantId,
|
|
2706
2839
|
});
|
|
2707
2840
|
|
|
2708
2841
|
if (!accessToken) {
|
|
@@ -2759,11 +2892,13 @@ All monitoring checks are passing normally.`;
|
|
|
2759
2892
|
const miscData: MicrosoftTeamsMiscData =
|
|
2760
2893
|
(projectAuth.miscData as MicrosoftTeamsMiscData) || {};
|
|
2761
2894
|
miscData.availableTeams = availableTeams;
|
|
2895
|
+
miscData.tenantId = tenantId;
|
|
2762
2896
|
|
|
2763
2897
|
await WorkspaceProjectAuthTokenService.updateOneById({
|
|
2764
2898
|
id: projectAuth.id!,
|
|
2765
2899
|
data: {
|
|
2766
2900
|
miscData: miscData,
|
|
2901
|
+
workspaceProjectId: tenantId,
|
|
2767
2902
|
},
|
|
2768
2903
|
props: {
|
|
2769
2904
|
isRoot: true,
|
package/Types/Permission.ts
CHANGED
|
@@ -80,9 +80,6 @@ enum Permission {
|
|
|
80
80
|
EditTelemetryServiceMetrics = "EditTelemetryServiceMetrics",
|
|
81
81
|
ReadTelemetryServiceMetrics = "ReadTelemetryServiceMetrics",
|
|
82
82
|
|
|
83
|
-
// Telemetry Attributes
|
|
84
|
-
DeleteTelemetryAttributes = "DeleteTelemetryAttributes",
|
|
85
|
-
|
|
86
83
|
// Billing Permissions (Owner Permission)
|
|
87
84
|
ManageProjectBilling = "ManageProjectBilling",
|
|
88
85
|
|
|
@@ -30,6 +30,9 @@ export interface ComponentProps<T extends GenericObject> {
|
|
|
30
30
|
isModalLoading?: boolean;
|
|
31
31
|
onFilterRefreshClick?: undefined | (() => void);
|
|
32
32
|
filterData?: FilterData<T> | undefined;
|
|
33
|
+
onAdvancedFiltersToggle?:
|
|
34
|
+
| undefined
|
|
35
|
+
| ((showAdvancedFilters: boolean) => void);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
type FilterComponentFunction = <T extends GenericObject>(
|
|
@@ -355,7 +358,7 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
|
|
|
355
358
|
<div>
|
|
356
359
|
{showViewer && (
|
|
357
360
|
<div>
|
|
358
|
-
<div className="mt-5 mb-5 bg-gray-50 rounded
|
|
361
|
+
<div className="mt-5 mb-5 bg-gray-50 rounded-xl p-5 border-2 border-gray-100">
|
|
359
362
|
<div className="flex mt-1 mb-2">
|
|
360
363
|
<div className="flex-auto py-0.5 text-sm leading-5">
|
|
361
364
|
<span className="font-semibold">
|
|
@@ -442,6 +445,7 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
|
|
|
442
445
|
onFilterChanged={(filterData: FilterData<T>) => {
|
|
443
446
|
setTempFilterDataForModal(filterData);
|
|
444
447
|
}}
|
|
448
|
+
onAdvancedFiltersToggle={props.onAdvancedFiltersToggle}
|
|
445
449
|
/>
|
|
446
450
|
</Modal>
|
|
447
451
|
)}
|
|
@@ -24,6 +24,9 @@ export interface ComponentProps<T extends GenericObject> {
|
|
|
24
24
|
isFilterLoading?: undefined | boolean;
|
|
25
25
|
filterError?: string | undefined;
|
|
26
26
|
onFilterRefreshClick?: undefined | (() => void);
|
|
27
|
+
onAdvancedFiltersToggle?:
|
|
28
|
+
| undefined
|
|
29
|
+
| ((showAdvancedFilters: boolean) => void);
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
type FiltersFormFunction = <T extends GenericObject>(
|
|
@@ -131,7 +134,11 @@ const FiltersForm: FiltersFormFunction = <T extends GenericObject>(
|
|
|
131
134
|
: "Show Advanced Filters"
|
|
132
135
|
}
|
|
133
136
|
onClick={() => {
|
|
134
|
-
setShowMoreFilters(
|
|
137
|
+
setShowMoreFilters((currentValue: boolean) => {
|
|
138
|
+
const newValue: boolean = !currentValue;
|
|
139
|
+
props.onAdvancedFiltersToggle?.(newValue);
|
|
140
|
+
return newValue;
|
|
141
|
+
});
|
|
135
142
|
}}
|
|
136
143
|
/>
|
|
137
144
|
)}
|
|
@@ -43,6 +43,9 @@ export interface ComponentProps<T extends GenericObject> {
|
|
|
43
43
|
onFilterRefreshClick?: undefined | (() => void);
|
|
44
44
|
onFilterModalClose?: (() => void) | undefined;
|
|
45
45
|
onFilterModalOpen?: (() => void) | undefined;
|
|
46
|
+
onAdvancedFiltersToggle?:
|
|
47
|
+
| undefined
|
|
48
|
+
| ((showAdvancedFilters: boolean) => void);
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
type ListFunction = <T extends GenericObject>(
|
|
@@ -118,6 +121,7 @@ const List: ListFunction = <T extends GenericObject>(
|
|
|
118
121
|
}}
|
|
119
122
|
singularLabel={props.singularLabel}
|
|
120
123
|
pluralLabel={props.pluralLabel}
|
|
124
|
+
onAdvancedFiltersToggle={props.onAdvancedFiltersToggle}
|
|
121
125
|
/>
|
|
122
126
|
</div>
|
|
123
127
|
<div className="">
|
|
@@ -10,7 +10,12 @@ import {
|
|
|
10
10
|
} from "../../../Types/FunctionTypes";
|
|
11
11
|
import Log from "../../../Models/AnalyticsModels/Log";
|
|
12
12
|
import LogSeverity from "../../../Types/Log/LogSeverity";
|
|
13
|
-
import React, {
|
|
13
|
+
import React, {
|
|
14
|
+
FunctionComponent,
|
|
15
|
+
ReactElement,
|
|
16
|
+
Ref,
|
|
17
|
+
useCallback,
|
|
18
|
+
} from "react";
|
|
14
19
|
import Toggle from "../Toggle/Toggle";
|
|
15
20
|
import Card from "../Card/Card";
|
|
16
21
|
import Button, { ButtonSize, ButtonStyleType } from "../Button/Button";
|
|
@@ -58,6 +63,13 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
|
|
|
58
63
|
React.useRef<HTMLDivElement>(null);
|
|
59
64
|
|
|
60
65
|
const [logAttributes, setLogAttributes] = React.useState<Array<string>>([]);
|
|
66
|
+
const [attributesLoaded, setAttributesLoaded] =
|
|
67
|
+
React.useState<boolean>(false);
|
|
68
|
+
const [attributesLoading, setAttributesLoading] =
|
|
69
|
+
React.useState<boolean>(false);
|
|
70
|
+
const [attributesError, setAttributesError] = React.useState<string>("");
|
|
71
|
+
const [areAdvancedFiltersVisible, setAreAdvancedFiltersVisible] =
|
|
72
|
+
React.useState<boolean>(false);
|
|
61
73
|
|
|
62
74
|
const [isPageLoading, setIsPageLoading] = React.useState<boolean>(true);
|
|
63
75
|
const [pageError, setPageError] = React.useState<string>("");
|
|
@@ -66,66 +78,83 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
|
|
|
66
78
|
Dictionary<TelemetryService>
|
|
67
79
|
>({});
|
|
68
80
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
setServiceMap(services);
|
|
94
|
-
|
|
95
|
-
const attributeRepsonse: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
|
96
|
-
await API.post({
|
|
97
|
-
url: URL.fromString(APP_API_URL.toString()).addRoute(
|
|
98
|
-
"/telemetry/logs/get-attributes",
|
|
99
|
-
),
|
|
100
|
-
data: {},
|
|
101
|
-
headers: {
|
|
102
|
-
...ModelAPI.getCommonHeaders(),
|
|
103
|
-
},
|
|
81
|
+
const loadTelemetryServices: PromiseVoidFunction =
|
|
82
|
+
useCallback(async (): Promise<void> => {
|
|
83
|
+
try {
|
|
84
|
+
setIsPageLoading(true);
|
|
85
|
+
setPageError("");
|
|
86
|
+
|
|
87
|
+
const telemetryServices: ListResult<TelemetryService> =
|
|
88
|
+
await ModelAPI.getList({
|
|
89
|
+
modelType: TelemetryService,
|
|
90
|
+
query: {},
|
|
91
|
+
select: {
|
|
92
|
+
name: true,
|
|
93
|
+
serviceColor: true,
|
|
94
|
+
},
|
|
95
|
+
limit: LIMIT_PER_PROJECT,
|
|
96
|
+
skip: 0,
|
|
97
|
+
sort: {
|
|
98
|
+
name: SortOrder.Ascending,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
const services: Dictionary<TelemetryService> = {};
|
|
102
|
+
|
|
103
|
+
telemetryServices.data.forEach((service: TelemetryService) => {
|
|
104
|
+
services[service.id!.toString()!] = service;
|
|
104
105
|
});
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
setServiceMap(services);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
setPageError(
|
|
110
|
+
`We couldn't load telemetry service metadata. ${API.getFriendlyErrorMessage(err as Error)}`,
|
|
111
|
+
);
|
|
112
|
+
} finally {
|
|
113
|
+
setIsPageLoading(false);
|
|
114
|
+
}
|
|
115
|
+
}, []);
|
|
116
|
+
|
|
117
|
+
const loadAttributes: PromiseVoidFunction =
|
|
118
|
+
useCallback(async (): Promise<void> => {
|
|
119
|
+
try {
|
|
120
|
+
setAttributesLoading(true);
|
|
121
|
+
setAttributesError("");
|
|
122
|
+
|
|
123
|
+
const attributeRepsonse: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
|
124
|
+
await API.post({
|
|
125
|
+
url: URL.fromString(APP_API_URL.toString()).addRoute(
|
|
126
|
+
"/telemetry/logs/get-attributes",
|
|
127
|
+
),
|
|
128
|
+
data: {},
|
|
129
|
+
headers: {
|
|
130
|
+
...ModelAPI.getCommonHeaders(),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (attributeRepsonse instanceof HTTPErrorResponse) {
|
|
135
|
+
throw attributeRepsonse;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const attributes: Array<string> = (attributeRepsonse.data[
|
|
110
139
|
"attributes"
|
|
111
|
-
] as Array<string>;
|
|
140
|
+
] || []) as Array<string>;
|
|
112
141
|
setLogAttributes(attributes);
|
|
142
|
+
setAttributesLoaded(true);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
setLogAttributes([]);
|
|
145
|
+
setAttributesLoaded(false);
|
|
146
|
+
setAttributesError(
|
|
147
|
+
`We couldn't load log attributes. Filters may be limited. ${API.getFriendlyErrorMessage(err as Error)}`,
|
|
148
|
+
);
|
|
149
|
+
} finally {
|
|
150
|
+
setAttributesLoading(false);
|
|
113
151
|
}
|
|
114
|
-
|
|
115
|
-
setIsPageLoading(false);
|
|
116
|
-
setPageError("");
|
|
117
|
-
} catch (err) {
|
|
118
|
-
setIsPageLoading(false);
|
|
119
|
-
setPageError(API.getFriendlyErrorMessage(err as Error));
|
|
120
|
-
}
|
|
121
|
-
};
|
|
152
|
+
}, []);
|
|
122
153
|
|
|
123
154
|
// Update the screen height when the window is resized
|
|
124
155
|
|
|
125
156
|
React.useEffect(() => {
|
|
126
|
-
|
|
127
|
-
setPageError(API.getFriendlyErrorMessage(err as Error));
|
|
128
|
-
});
|
|
157
|
+
void loadTelemetryServices();
|
|
129
158
|
|
|
130
159
|
const handleResize: any = (): void => {
|
|
131
160
|
setScreenHeight(window.innerHeight);
|
|
@@ -136,7 +165,7 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
|
|
|
136
165
|
return () => {
|
|
137
166
|
window.removeEventListener("resize", handleResize);
|
|
138
167
|
};
|
|
139
|
-
}, []);
|
|
168
|
+
}, [loadTelemetryServices]);
|
|
140
169
|
|
|
141
170
|
// Keep scroll to the bottom of the log
|
|
142
171
|
|
|
@@ -180,11 +209,9 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
|
|
|
180
209
|
if (isPageLoading) {
|
|
181
210
|
return <PageLoader isVisible={true} />;
|
|
182
211
|
}
|
|
183
|
-
|
|
184
212
|
if (pageError) {
|
|
185
213
|
return <ErrorMessage message={pageError} />;
|
|
186
214
|
}
|
|
187
|
-
|
|
188
215
|
return (
|
|
189
216
|
<div>
|
|
190
217
|
{props.showFilters && (
|
|
@@ -198,6 +225,26 @@ const LogsViewer: FunctionComponent<ComponentProps> = (
|
|
|
198
225
|
onFilterChanged={(filterData: Query<Log>) => {
|
|
199
226
|
setFilterData(filterData);
|
|
200
227
|
}}
|
|
228
|
+
onAdvancedFiltersToggle={(show: boolean) => {
|
|
229
|
+
setAreAdvancedFiltersVisible(show);
|
|
230
|
+
|
|
231
|
+
if (show && !attributesLoaded && !attributesLoading) {
|
|
232
|
+
void loadAttributes();
|
|
233
|
+
}
|
|
234
|
+
}}
|
|
235
|
+
isFilterLoading={areAdvancedFiltersVisible && attributesLoading}
|
|
236
|
+
filterError={
|
|
237
|
+
areAdvancedFiltersVisible && attributesError
|
|
238
|
+
? attributesError
|
|
239
|
+
: undefined
|
|
240
|
+
}
|
|
241
|
+
onFilterRefreshClick={
|
|
242
|
+
areAdvancedFiltersVisible && attributesError
|
|
243
|
+
? () => {
|
|
244
|
+
void loadAttributes();
|
|
245
|
+
}
|
|
246
|
+
: undefined
|
|
247
|
+
}
|
|
201
248
|
filters={[
|
|
202
249
|
{
|
|
203
250
|
key: "body",
|
|
@@ -14,7 +14,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
|
|
14
14
|
props: ComponentProps,
|
|
15
15
|
): ReactElement => {
|
|
16
16
|
return (
|
|
17
|
-
<div className="max-w-none
|
|
17
|
+
<div className="max-w-none">
|
|
18
18
|
<ReactMarkdown
|
|
19
19
|
components={{
|
|
20
20
|
// because tailwind does not supply <h1 ... /> styles https://tailwindcss.com/docs/preflight#headings-are-unstyled
|
|
@@ -224,6 +224,10 @@ export interface BaseTableProps<
|
|
|
224
224
|
|
|
225
225
|
formSummary?: FormSummaryConfig | undefined;
|
|
226
226
|
|
|
227
|
+
onAdvancedFiltersToggle?:
|
|
228
|
+
| undefined
|
|
229
|
+
| ((showAdvancedFilters: boolean) => void);
|
|
230
|
+
|
|
227
231
|
/*
|
|
228
232
|
* this key is used to save table user preferences in local storage.
|
|
229
233
|
* If you provide this key, the table will save the user preferences in local storage.
|
|
@@ -1516,6 +1520,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1516
1520
|
onFilterModalOpen={() => {
|
|
1517
1521
|
setShowFilterModal(true);
|
|
1518
1522
|
}}
|
|
1523
|
+
onAdvancedFiltersToggle={props.onAdvancedFiltersToggle}
|
|
1519
1524
|
onSortChanged={(
|
|
1520
1525
|
sortBy: keyof TBaseModel | null,
|
|
1521
1526
|
sortOrder: SortOrder,
|
|
@@ -1662,6 +1667,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1662
1667
|
onFilterModalOpen={() => {
|
|
1663
1668
|
setShowFilterModal(true);
|
|
1664
1669
|
}}
|
|
1670
|
+
onAdvancedFiltersToggle={props.onAdvancedFiltersToggle}
|
|
1665
1671
|
singularLabel={props.singularName || model.singularName || "Item"}
|
|
1666
1672
|
pluralLabel={props.pluralName || model.pluralName || "Items"}
|
|
1667
1673
|
error={error}
|
|
@@ -54,6 +54,9 @@ export interface ComponentProps<T extends GenericObject> {
|
|
|
54
54
|
onFilterModalClose?: (() => void) | undefined;
|
|
55
55
|
onFilterModalOpen?: (() => void) | undefined;
|
|
56
56
|
filterData?: undefined | FilterData<T>;
|
|
57
|
+
onAdvancedFiltersToggle?:
|
|
58
|
+
| undefined
|
|
59
|
+
| ((showAdvancedFilters: boolean) => void);
|
|
57
60
|
|
|
58
61
|
enableDragAndDrop?: boolean | undefined;
|
|
59
62
|
dragDropIndexField?: keyof T | undefined;
|
|
@@ -242,6 +245,7 @@ const Table: TableFunction = <T extends GenericObject>(
|
|
|
242
245
|
singularLabel={props.singularLabel}
|
|
243
246
|
pluralLabel={props.pluralLabel}
|
|
244
247
|
filterData={props.filterData}
|
|
248
|
+
onAdvancedFiltersToggle={props.onAdvancedFiltersToggle}
|
|
245
249
|
/>
|
|
246
250
|
{props.bulkActions?.buttons && (
|
|
247
251
|
<BulkUpdateForm
|