@oneuptime/common 10.4.8 → 10.4.9
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/Server/API/TelemetryExceptionAPI.ts +62 -0
- package/Server/Services/TelemetryExceptionService.ts +225 -0
- package/Server/Types/Database/QueryHelper.ts +35 -0
- package/Server/Types/Database/QueryUtil.ts +74 -2
- package/Types/Dashboard/DashboardComponents/DashboardAlertListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerContainerListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerHostListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerImageListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerNetworkListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerVolumeListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardHostListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardIncidentListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesCronJobListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesDaemonSetListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesDeploymentListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesJobListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesNamespaceListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesNodeListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesPodListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesStatefulSetListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardTraceListComponent.ts +1 -0
- package/Types/Time/RangeStartAndEndDateTime.ts +8 -0
- package/Types/Time/TimeRange.ts +1 -0
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +1 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +64 -17
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +1 -0
- package/Utils/Dashboard/Components/DashboardAlertListComponent.ts +5 -0
- package/Utils/Dashboard/Components/DashboardDockerContainerListComponent.ts +7 -0
- package/Utils/Dashboard/Components/DashboardDockerHostListComponent.ts +5 -0
- package/Utils/Dashboard/Components/DashboardDockerImageListComponent.ts +5 -0
- package/Utils/Dashboard/Components/DashboardDockerNetworkListComponent.ts +5 -0
- package/Utils/Dashboard/Components/DashboardDockerVolumeListComponent.ts +5 -0
- package/Utils/Dashboard/Components/DashboardHostListComponent.ts +3 -0
- package/Utils/Dashboard/Components/DashboardIncidentListComponent.ts +5 -0
- package/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.ts +3 -0
- package/Utils/Dashboard/Components/DashboardListSharedArgs.ts +31 -0
- package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +5 -0
- package/Utils/Dashboard/Components/DashboardTraceListComponent.ts +5 -0
- package/build/dist/Server/API/TelemetryExceptionAPI.js +37 -1
- package/build/dist/Server/API/TelemetryExceptionAPI.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +175 -0
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +35 -0
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +58 -2
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Types/Time/RangeStartAndEndDateTime.js +4 -0
- package/build/dist/Types/Time/RangeStartAndEndDateTime.js.map +1 -1
- package/build/dist/Types/Time/TimeRange.js +1 -0
- package/build/dist/Types/Time/TimeRange.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +1 -0
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +48 -13
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardAlertListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardAlertListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDockerContainerListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerContainerListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDockerHostListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerHostListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDockerImageListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerImageListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDockerNetworkListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerNetworkListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDockerVolumeListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerVolumeListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardHostListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardHostListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardIncidentListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardIncidentListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardListSharedArgs.js +21 -0
- package/build/dist/Utils/Dashboard/Components/DashboardListSharedArgs.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardTraceListComponent.js +2 -0
- package/build/dist/Utils/Dashboard/Components/DashboardTraceListComponent.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import TelemetryException from "../../Models/DatabaseModels/TelemetryException";
|
|
2
|
+
import TelemetryServiceModel from "../../Models/DatabaseModels/Service";
|
|
2
3
|
import AIAgentTask from "../../Models/DatabaseModels/AIAgentTask";
|
|
3
4
|
import AIAgentTaskTelemetryException from "../../Models/DatabaseModels/AIAgentTaskTelemetryException";
|
|
4
5
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
5
6
|
import ObjectID from "../../Types/ObjectID";
|
|
7
|
+
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
6
8
|
import TelemetryExceptionService, {
|
|
9
|
+
DashboardServiceSummary,
|
|
10
|
+
DashboardSummaryResult,
|
|
7
11
|
Service as TelemetryExceptionServiceType,
|
|
8
12
|
} from "../Services/TelemetryExceptionService";
|
|
9
13
|
import AIAgentTaskTelemetryExceptionService from "../Services/AIAgentTaskTelemetryExceptionService";
|
|
@@ -21,6 +25,7 @@ import AIAgentTaskStatus, {
|
|
|
21
25
|
AIAgentTaskStatusHelper,
|
|
22
26
|
} from "../../Types/AI/AIAgentTaskStatus";
|
|
23
27
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
28
|
+
import { JSONArray, JSONObject } from "../../Types/JSON";
|
|
24
29
|
|
|
25
30
|
export default class TelemetryExceptionAPI extends BaseAPI<
|
|
26
31
|
TelemetryException,
|
|
@@ -58,6 +63,23 @@ export default class TelemetryExceptionAPI extends BaseAPI<
|
|
|
58
63
|
}
|
|
59
64
|
},
|
|
60
65
|
);
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* Aggregated dashboard summary for the Exceptions overview page.
|
|
69
|
+
* Returns counts, top/recent exceptions, and per-service summaries
|
|
70
|
+
* in a single round-trip with one SQL GROUP BY for service aggregation.
|
|
71
|
+
*/
|
|
72
|
+
this.router.post(
|
|
73
|
+
`${new this.entityType().getCrudApiPath()?.toString()}/dashboard-summary`,
|
|
74
|
+
UserMiddleware.getUserMiddleware,
|
|
75
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
76
|
+
try {
|
|
77
|
+
await this.getDashboardSummary(req, res);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
next(err);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
);
|
|
61
83
|
}
|
|
62
84
|
|
|
63
85
|
private async createAIAgentTask(
|
|
@@ -166,4 +188,44 @@ export default class TelemetryExceptionAPI extends BaseAPI<
|
|
|
166
188
|
},
|
|
167
189
|
});
|
|
168
190
|
}
|
|
191
|
+
|
|
192
|
+
private async getDashboardSummary(
|
|
193
|
+
req: ExpressRequest,
|
|
194
|
+
res: ExpressResponse,
|
|
195
|
+
): Promise<void> {
|
|
196
|
+
const props: DatabaseCommonInteractionProps =
|
|
197
|
+
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
198
|
+
|
|
199
|
+
const summary: DashboardSummaryResult =
|
|
200
|
+
await this.service.getDashboardSummary(props);
|
|
201
|
+
|
|
202
|
+
const topExceptionsJson: JSONArray = BaseModel.toJSONArray(
|
|
203
|
+
summary.topExceptions,
|
|
204
|
+
TelemetryException,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const recentExceptionsJson: JSONArray = BaseModel.toJSONArray(
|
|
208
|
+
summary.recentExceptions,
|
|
209
|
+
TelemetryException,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const serviceSummariesJson: JSONArray = summary.serviceSummaries.map(
|
|
213
|
+
(entry: DashboardServiceSummary): JSONObject => {
|
|
214
|
+
return {
|
|
215
|
+
service: BaseModel.toJSON(entry.service, TelemetryServiceModel),
|
|
216
|
+
unresolvedCount: entry.unresolvedCount,
|
|
217
|
+
totalOccurrences: entry.totalOccurrences,
|
|
218
|
+
};
|
|
219
|
+
},
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
return Response.sendJsonObjectResponse(req, res, {
|
|
223
|
+
unresolvedCount: summary.unresolvedCount,
|
|
224
|
+
resolvedCount: summary.resolvedCount,
|
|
225
|
+
archivedCount: summary.archivedCount,
|
|
226
|
+
topExceptions: topExceptionsJson,
|
|
227
|
+
recentExceptions: recentExceptionsJson,
|
|
228
|
+
serviceSummaries: serviceSummariesJson,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
169
231
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import DatabaseService from "./DatabaseService";
|
|
2
2
|
import Model from "../../Models/DatabaseModels/TelemetryException";
|
|
3
|
+
import TelemetryServiceModel from "../../Models/DatabaseModels/Service";
|
|
3
4
|
import AIAgentTask from "../../Models/DatabaseModels/AIAgentTask";
|
|
4
5
|
import AIAgentTaskTelemetryException from "../../Models/DatabaseModels/AIAgentTaskTelemetryException";
|
|
5
6
|
import ObjectID from "../../Types/ObjectID";
|
|
7
|
+
import PositiveNumber from "../../Types/PositiveNumber";
|
|
8
|
+
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
6
9
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
7
10
|
import AIAgentTaskType from "../../Types/AI/AIAgentTaskType";
|
|
8
11
|
import AIAgentTaskStatus from "../../Types/AI/AIAgentTaskStatus";
|
|
@@ -10,7 +13,9 @@ import { FixExceptionTaskMetadata } from "../../Types/AI/AIAgentTaskMetadata";
|
|
|
10
13
|
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
11
14
|
import AIAgentTaskService from "./AIAgentTaskService";
|
|
12
15
|
import AIAgentTaskTelemetryExceptionService from "./AIAgentTaskTelemetryExceptionService";
|
|
16
|
+
import ServiceService from "./ServiceService";
|
|
13
17
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
18
|
+
import ModelPermission from "../Types/Database/Permissions/Index";
|
|
14
19
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
15
20
|
|
|
16
21
|
export interface CreateAIAgentTaskForExceptionParams {
|
|
@@ -18,6 +23,21 @@ export interface CreateAIAgentTaskForExceptionParams {
|
|
|
18
23
|
props: DatabaseCommonInteractionProps;
|
|
19
24
|
}
|
|
20
25
|
|
|
26
|
+
export interface DashboardServiceSummary {
|
|
27
|
+
service: TelemetryServiceModel;
|
|
28
|
+
unresolvedCount: number;
|
|
29
|
+
totalOccurrences: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DashboardSummaryResult {
|
|
33
|
+
unresolvedCount: number;
|
|
34
|
+
resolvedCount: number;
|
|
35
|
+
archivedCount: number;
|
|
36
|
+
topExceptions: Array<Model>;
|
|
37
|
+
recentExceptions: Array<Model>;
|
|
38
|
+
serviceSummaries: Array<DashboardServiceSummary>;
|
|
39
|
+
}
|
|
40
|
+
|
|
21
41
|
export class Service extends DatabaseService<Model> {
|
|
22
42
|
public constructor() {
|
|
23
43
|
super(Model);
|
|
@@ -141,6 +161,211 @@ export class Service extends DatabaseService<Model> {
|
|
|
141
161
|
return createdTask;
|
|
142
162
|
}
|
|
143
163
|
|
|
164
|
+
@CaptureSpan()
|
|
165
|
+
public async getDashboardSummary(
|
|
166
|
+
props: DatabaseCommonInteractionProps,
|
|
167
|
+
): Promise<DashboardSummaryResult> {
|
|
168
|
+
if (!props.tenantId) {
|
|
169
|
+
throw new BadDataException("Project ID is required");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const projectId: ObjectID = props.tenantId;
|
|
173
|
+
|
|
174
|
+
const exceptionSelect: any = {
|
|
175
|
+
_id: true,
|
|
176
|
+
message: true,
|
|
177
|
+
exceptionType: true,
|
|
178
|
+
fingerprint: true,
|
|
179
|
+
isResolved: true,
|
|
180
|
+
isArchived: true,
|
|
181
|
+
occuranceCount: true,
|
|
182
|
+
lastSeenAt: true,
|
|
183
|
+
firstSeenAt: true,
|
|
184
|
+
environment: true,
|
|
185
|
+
service: {
|
|
186
|
+
_id: true,
|
|
187
|
+
name: true,
|
|
188
|
+
serviceColor: true,
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const [
|
|
193
|
+
unresolvedCount,
|
|
194
|
+
resolvedCount,
|
|
195
|
+
archivedCount,
|
|
196
|
+
topExceptions,
|
|
197
|
+
recentExceptions,
|
|
198
|
+
serviceSummaries,
|
|
199
|
+
] = await Promise.all([
|
|
200
|
+
this.countBy({
|
|
201
|
+
query: {
|
|
202
|
+
projectId,
|
|
203
|
+
isResolved: false,
|
|
204
|
+
isArchived: false,
|
|
205
|
+
},
|
|
206
|
+
props,
|
|
207
|
+
}),
|
|
208
|
+
this.countBy({
|
|
209
|
+
query: {
|
|
210
|
+
projectId,
|
|
211
|
+
isResolved: true,
|
|
212
|
+
isArchived: false,
|
|
213
|
+
},
|
|
214
|
+
props,
|
|
215
|
+
}),
|
|
216
|
+
this.countBy({
|
|
217
|
+
query: {
|
|
218
|
+
projectId,
|
|
219
|
+
isArchived: true,
|
|
220
|
+
},
|
|
221
|
+
props,
|
|
222
|
+
}),
|
|
223
|
+
this.findBy({
|
|
224
|
+
query: {
|
|
225
|
+
projectId,
|
|
226
|
+
isResolved: false,
|
|
227
|
+
isArchived: false,
|
|
228
|
+
},
|
|
229
|
+
select: exceptionSelect,
|
|
230
|
+
limit: 10,
|
|
231
|
+
skip: 0,
|
|
232
|
+
sort: {
|
|
233
|
+
occuranceCount: SortOrder.Descending,
|
|
234
|
+
},
|
|
235
|
+
props,
|
|
236
|
+
}),
|
|
237
|
+
this.findBy({
|
|
238
|
+
query: {
|
|
239
|
+
projectId,
|
|
240
|
+
isResolved: false,
|
|
241
|
+
isArchived: false,
|
|
242
|
+
},
|
|
243
|
+
select: exceptionSelect,
|
|
244
|
+
limit: 5,
|
|
245
|
+
skip: 0,
|
|
246
|
+
sort: {
|
|
247
|
+
lastSeenAt: SortOrder.Descending,
|
|
248
|
+
},
|
|
249
|
+
props,
|
|
250
|
+
}),
|
|
251
|
+
this.aggregateUnresolvedByService(projectId, props),
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
unresolvedCount: unresolvedCount.toNumber(),
|
|
256
|
+
resolvedCount: resolvedCount.toNumber(),
|
|
257
|
+
archivedCount: archivedCount.toNumber(),
|
|
258
|
+
topExceptions,
|
|
259
|
+
recentExceptions,
|
|
260
|
+
serviceSummaries,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@CaptureSpan()
|
|
265
|
+
private async aggregateUnresolvedByService(
|
|
266
|
+
projectId: ObjectID,
|
|
267
|
+
props: DatabaseCommonInteractionProps,
|
|
268
|
+
): Promise<Array<DashboardServiceSummary>> {
|
|
269
|
+
/*
|
|
270
|
+
* Assert the caller has read permission on TelemetryException. We don't
|
|
271
|
+
* use the returned filtered query — we run a raw GROUP BY below for
|
|
272
|
+
* efficiency — but this throws if the user lacks read access.
|
|
273
|
+
*/
|
|
274
|
+
await ModelPermission.checkReadQueryPermission(
|
|
275
|
+
Model,
|
|
276
|
+
{
|
|
277
|
+
projectId,
|
|
278
|
+
isResolved: false,
|
|
279
|
+
isArchived: false,
|
|
280
|
+
},
|
|
281
|
+
null,
|
|
282
|
+
props,
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
interface AggregateRow {
|
|
286
|
+
serviceId: string | null;
|
|
287
|
+
unresolvedCount: string;
|
|
288
|
+
totalOccurrences: string | null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const rows: Array<AggregateRow> = (await this.getQueryBuilder(
|
|
292
|
+
"TelemetryException",
|
|
293
|
+
)
|
|
294
|
+
.select(`"TelemetryException"."serviceId"`, "serviceId")
|
|
295
|
+
.addSelect(`COUNT(*)`, "unresolvedCount")
|
|
296
|
+
.addSelect(
|
|
297
|
+
`COALESCE(SUM("TelemetryException"."occuranceCount"), 0)`,
|
|
298
|
+
"totalOccurrences",
|
|
299
|
+
)
|
|
300
|
+
.where(`"TelemetryException"."projectId" = :projectId`, {
|
|
301
|
+
projectId: projectId.toString(),
|
|
302
|
+
})
|
|
303
|
+
.andWhere(`"TelemetryException"."isResolved" = false`)
|
|
304
|
+
.andWhere(`"TelemetryException"."isArchived" = false`)
|
|
305
|
+
.andWhere(`"TelemetryException"."deletedAt" IS NULL`)
|
|
306
|
+
.andWhere(`"TelemetryException"."serviceId" IS NOT NULL`)
|
|
307
|
+
.groupBy(`"TelemetryException"."serviceId"`)
|
|
308
|
+
.orderBy(`"unresolvedCount"`, "DESC")
|
|
309
|
+
.getRawMany()) as Array<AggregateRow>;
|
|
310
|
+
|
|
311
|
+
if (rows.length === 0) {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const serviceIds: Array<string> = [];
|
|
316
|
+
for (const row of rows) {
|
|
317
|
+
if (row.serviceId) {
|
|
318
|
+
serviceIds.push(row.serviceId);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (serviceIds.length === 0) {
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const services: Array<TelemetryServiceModel> = await ServiceService.findBy({
|
|
327
|
+
query: {
|
|
328
|
+
projectId,
|
|
329
|
+
_id: QueryHelper.any(serviceIds),
|
|
330
|
+
},
|
|
331
|
+
select: {
|
|
332
|
+
_id: true,
|
|
333
|
+
name: true,
|
|
334
|
+
serviceColor: true,
|
|
335
|
+
},
|
|
336
|
+
limit: new PositiveNumber(serviceIds.length),
|
|
337
|
+
skip: new PositiveNumber(0),
|
|
338
|
+
props,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const serviceById: Map<string, TelemetryServiceModel> = new Map();
|
|
342
|
+
for (const service of services) {
|
|
343
|
+
if (service._id) {
|
|
344
|
+
serviceById.set(service._id, service);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const summaries: Array<DashboardServiceSummary> = [];
|
|
349
|
+
for (const row of rows) {
|
|
350
|
+
if (!row.serviceId) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const service: TelemetryServiceModel | undefined = serviceById.get(
|
|
354
|
+
row.serviceId,
|
|
355
|
+
);
|
|
356
|
+
if (!service) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
summaries.push({
|
|
360
|
+
service,
|
|
361
|
+
unresolvedCount: parseInt(row.unresolvedCount, 10) || 0,
|
|
362
|
+
totalOccurrences: parseInt(row.totalOccurrences || "0", 10) || 0,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return summaries;
|
|
367
|
+
}
|
|
368
|
+
|
|
144
369
|
private buildFixExceptionMetadata(params: {
|
|
145
370
|
telemetryException: Model;
|
|
146
371
|
telemetryExceptionId: ObjectID;
|
|
@@ -289,6 +289,41 @@ export default class QueryHelper {
|
|
|
289
289
|
);
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Matches owner rows that have no rows in the join table — i.e. the
|
|
294
|
+
* many-to-many collection is empty. Apply to the owner's primary id column.
|
|
295
|
+
*/
|
|
296
|
+
@CaptureSpan()
|
|
297
|
+
public static noEntitiesInManyToMany(data: {
|
|
298
|
+
joinTableName: string;
|
|
299
|
+
ownerColumnName: string;
|
|
300
|
+
}): FindWhereProperty<any> {
|
|
301
|
+
const joinTable: string = data.joinTableName.replace(/"/g, '""');
|
|
302
|
+
const ownerCol: string = data.ownerColumnName.replace(/"/g, '""');
|
|
303
|
+
|
|
304
|
+
return Raw((alias: string) => {
|
|
305
|
+
return `(${alias} NOT IN (SELECT "${joinTable}"."${ownerCol}" FROM "${joinTable}" WHERE "${joinTable}"."${ownerCol}" IS NOT NULL))`;
|
|
306
|
+
}, {});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Matches owner rows that have at least one row in the join table — i.e.
|
|
311
|
+
* the many-to-many collection has at least one entry. Apply to the
|
|
312
|
+
* owner's primary id column.
|
|
313
|
+
*/
|
|
314
|
+
@CaptureSpan()
|
|
315
|
+
public static anyEntitiesInManyToMany(data: {
|
|
316
|
+
joinTableName: string;
|
|
317
|
+
ownerColumnName: string;
|
|
318
|
+
}): FindWhereProperty<any> {
|
|
319
|
+
const joinTable: string = data.joinTableName.replace(/"/g, '""');
|
|
320
|
+
const ownerCol: string = data.ownerColumnName.replace(/"/g, '""');
|
|
321
|
+
|
|
322
|
+
return Raw((alias: string) => {
|
|
323
|
+
return `(${alias} IN (SELECT "${joinTable}"."${ownerCol}" FROM "${joinTable}" WHERE "${joinTable}"."${ownerCol}" IS NOT NULL))`;
|
|
324
|
+
}, {});
|
|
325
|
+
}
|
|
326
|
+
|
|
292
327
|
/**
|
|
293
328
|
* Returns a filter that matches owner rows that are linked to *all* of the
|
|
294
329
|
* provided related entity ids through a many-to-many join table. The
|
|
@@ -131,7 +131,45 @@ export default class QueryUtil {
|
|
|
131
131
|
query[key] instanceof NotNull &&
|
|
132
132
|
tableColumnMetadata
|
|
133
133
|
) {
|
|
134
|
-
|
|
134
|
+
if (tableColumnMetadata.type === TableColumnType.EntityArray) {
|
|
135
|
+
const manyToManyMeta: {
|
|
136
|
+
joinTableName: string;
|
|
137
|
+
ownerColumnName: string;
|
|
138
|
+
relationColumnName: string;
|
|
139
|
+
} | null = QueryUtil.getManyToManyRelationMetadata(modelType, key);
|
|
140
|
+
|
|
141
|
+
if (manyToManyMeta) {
|
|
142
|
+
const subqueryFilter: any = QueryHelper.anyEntitiesInManyToMany({
|
|
143
|
+
joinTableName: manyToManyMeta.joinTableName,
|
|
144
|
+
ownerColumnName: manyToManyMeta.ownerColumnName,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
delete query[key];
|
|
148
|
+
|
|
149
|
+
const existingIdFilter: any = (query as any)._id;
|
|
150
|
+
if (existingIdFilter instanceof FindOperator) {
|
|
151
|
+
(query as any)._id = And(existingIdFilter, subqueryFilter);
|
|
152
|
+
} else if (
|
|
153
|
+
existingIdFilter &&
|
|
154
|
+
typeof existingIdFilter === Typeof.String
|
|
155
|
+
) {
|
|
156
|
+
(query as any)._id = And(
|
|
157
|
+
QueryHelper.equalTo(existingIdFilter as string),
|
|
158
|
+
subqueryFilter,
|
|
159
|
+
);
|
|
160
|
+
} else {
|
|
161
|
+
(query as any)._id = subqueryFilter;
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
/*
|
|
165
|
+
* Metadata unavailable — drop the filter rather than emit
|
|
166
|
+
* invalid SQL against a relation that isn't a real column.
|
|
167
|
+
*/
|
|
168
|
+
delete query[key];
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
query[key] = QueryHelper.notNull();
|
|
172
|
+
}
|
|
135
173
|
} else if (
|
|
136
174
|
query[key] &&
|
|
137
175
|
query[key] instanceof EqualToOrNull &&
|
|
@@ -234,7 +272,41 @@ export default class QueryUtil {
|
|
|
234
272
|
query[key] instanceof IsNull &&
|
|
235
273
|
tableColumnMetadata
|
|
236
274
|
) {
|
|
237
|
-
|
|
275
|
+
if (tableColumnMetadata.type === TableColumnType.EntityArray) {
|
|
276
|
+
const manyToManyMeta: {
|
|
277
|
+
joinTableName: string;
|
|
278
|
+
ownerColumnName: string;
|
|
279
|
+
relationColumnName: string;
|
|
280
|
+
} | null = QueryUtil.getManyToManyRelationMetadata(modelType, key);
|
|
281
|
+
|
|
282
|
+
if (manyToManyMeta) {
|
|
283
|
+
const subqueryFilter: any = QueryHelper.noEntitiesInManyToMany({
|
|
284
|
+
joinTableName: manyToManyMeta.joinTableName,
|
|
285
|
+
ownerColumnName: manyToManyMeta.ownerColumnName,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
delete query[key];
|
|
289
|
+
|
|
290
|
+
const existingIdFilter: any = (query as any)._id;
|
|
291
|
+
if (existingIdFilter instanceof FindOperator) {
|
|
292
|
+
(query as any)._id = And(existingIdFilter, subqueryFilter);
|
|
293
|
+
} else if (
|
|
294
|
+
existingIdFilter &&
|
|
295
|
+
typeof existingIdFilter === Typeof.String
|
|
296
|
+
) {
|
|
297
|
+
(query as any)._id = And(
|
|
298
|
+
QueryHelper.equalTo(existingIdFilter as string),
|
|
299
|
+
subqueryFilter,
|
|
300
|
+
);
|
|
301
|
+
} else {
|
|
302
|
+
(query as any)._id = subqueryFilter;
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
delete query[key];
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
query[key] = QueryHelper.isNull() as any;
|
|
309
|
+
}
|
|
238
310
|
} else if (
|
|
239
311
|
query[key] &&
|
|
240
312
|
query[key] instanceof InBetween &&
|
|
@@ -8,6 +8,7 @@ export default interface DashboardAlertListComponent extends BaseComponent {
|
|
|
8
8
|
arguments: {
|
|
9
9
|
title?: string | undefined;
|
|
10
10
|
maxRows?: number | undefined;
|
|
11
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
11
12
|
stateFilter?: string | undefined;
|
|
12
13
|
severityIds?: Array<string> | undefined;
|
|
13
14
|
stateIds?: Array<string> | undefined;
|
|
@@ -9,6 +9,7 @@ export default interface DashboardDockerContainerListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
dockerHostIds?: Array<string> | undefined;
|
|
13
14
|
imageName?: string | undefined;
|
|
14
15
|
};
|
|
@@ -9,6 +9,7 @@ export default interface DashboardDockerImageListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
dockerHostIds?: Array<string> | undefined;
|
|
13
14
|
nameSearch?: string | undefined;
|
|
14
15
|
};
|
|
@@ -8,6 +8,7 @@ export default interface DashboardHostListComponent extends BaseComponent {
|
|
|
8
8
|
arguments: {
|
|
9
9
|
title?: string | undefined;
|
|
10
10
|
maxRows?: number | undefined;
|
|
11
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
11
12
|
statusFilter?: string | undefined;
|
|
12
13
|
osTypeFilter?: string | undefined;
|
|
13
14
|
};
|
|
@@ -8,6 +8,7 @@ export default interface DashboardIncidentListComponent extends BaseComponent {
|
|
|
8
8
|
arguments: {
|
|
9
9
|
title?: string | undefined;
|
|
10
10
|
maxRows?: number | undefined;
|
|
11
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
11
12
|
stateFilter?: string | undefined;
|
|
12
13
|
severityIds?: Array<string> | undefined;
|
|
13
14
|
stateIds?: Array<string> | undefined;
|
|
@@ -9,6 +9,7 @@ export default interface DashboardKubernetesCronJobListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
kubernetesClusterIds?: Array<string> | undefined;
|
|
13
14
|
namespaces?: string | undefined;
|
|
14
15
|
};
|
|
@@ -9,6 +9,7 @@ export default interface DashboardKubernetesDaemonSetListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
kubernetesClusterIds?: Array<string> | undefined;
|
|
13
14
|
namespaces?: string | undefined;
|
|
14
15
|
};
|
|
@@ -9,6 +9,7 @@ export default interface DashboardKubernetesDeploymentListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
kubernetesClusterIds?: Array<string> | undefined;
|
|
13
14
|
namespaces?: string | undefined;
|
|
14
15
|
};
|
|
@@ -9,6 +9,7 @@ export default interface DashboardKubernetesJobListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
kubernetesClusterIds?: Array<string> | undefined;
|
|
13
14
|
namespaces?: string | undefined;
|
|
14
15
|
};
|
|
@@ -9,6 +9,7 @@ export default interface DashboardKubernetesNodeListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
kubernetesClusterIds?: Array<string> | undefined;
|
|
13
14
|
readinessFilter?: string | undefined;
|
|
14
15
|
};
|
|
@@ -9,6 +9,7 @@ export default interface DashboardKubernetesPodListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
kubernetesClusterIds?: Array<string> | undefined;
|
|
13
14
|
namespaces?: string | undefined;
|
|
14
15
|
podPhases?: Array<string> | undefined;
|
|
@@ -9,6 +9,7 @@ export default interface DashboardKubernetesStatefulSetListComponent
|
|
|
9
9
|
arguments: {
|
|
10
10
|
title?: string | undefined;
|
|
11
11
|
maxRows?: number | undefined;
|
|
12
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
12
13
|
kubernetesClusterIds?: Array<string> | undefined;
|
|
13
14
|
namespaces?: string | undefined;
|
|
14
15
|
};
|
|
@@ -8,6 +8,7 @@ export default interface DashboardMonitorListComponent extends BaseComponent {
|
|
|
8
8
|
arguments: {
|
|
9
9
|
title?: string | undefined;
|
|
10
10
|
maxRows?: number | undefined;
|
|
11
|
+
viewMode?: "list" | "honeycomb" | undefined;
|
|
11
12
|
statusFilter?: string | undefined;
|
|
12
13
|
monitorStatusIds?: Array<string> | undefined;
|
|
13
14
|
monitorTypes?: Array<string> | undefined;
|
|
@@ -13,6 +13,14 @@ export class RangeStartAndEndDateTimeUtil {
|
|
|
13
13
|
): InBetween<Date> {
|
|
14
14
|
const currentDate: Date = OneUptimeDate.getCurrentDate();
|
|
15
15
|
|
|
16
|
+
// 5 mins.
|
|
17
|
+
if (dashboardStartAndEndDate.range === TimeRange.PAST_FIVE_MINS) {
|
|
18
|
+
return new InBetween<Date>(
|
|
19
|
+
OneUptimeDate.addRemoveMinutes(currentDate, -5),
|
|
20
|
+
currentDate,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
// 30 mins.
|
|
17
25
|
if (dashboardStartAndEndDate.range === TimeRange.PAST_THIRTY_MINS) {
|
|
18
26
|
return new InBetween<Date>(
|
package/Types/Time/TimeRange.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface LogTimeRangePickerProps {
|
|
|
22
22
|
|
|
23
23
|
// Preset options to show in the dropdown (ordered for log investigation use)
|
|
24
24
|
const PRESET_OPTIONS: Array<{ range: TimeRange; label: string }> = [
|
|
25
|
+
{ range: TimeRange.PAST_FIVE_MINS, label: "Past 5 Minutes" },
|
|
25
26
|
{ range: TimeRange.PAST_THIRTY_MINS, label: "Past 30 Minutes" },
|
|
26
27
|
{ range: TimeRange.PAST_ONE_HOUR, label: "Past 1 Hour" },
|
|
27
28
|
{ range: TimeRange.PAST_TWO_HOURS, label: "Past 2 Hours" },
|