@oneuptime/common 10.0.28 → 10.0.30

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.
Files changed (70) hide show
  1. package/Models/DatabaseModels/Index.ts +2 -0
  2. package/Models/DatabaseModels/LogSavedView.ts +466 -0
  3. package/Server/API/TelemetryAPI.ts +146 -0
  4. package/Server/Infrastructure/Postgres/SchemaMigrations/1772355000000-AddLogSavedView.ts +48 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1773344537755-MigrationName.ts +91 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  7. package/Server/Services/LogAggregationService.ts +387 -0
  8. package/Server/Services/LogSavedViewService.ts +109 -0
  9. package/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.ts +15 -0
  10. package/Server/Utils/Express.ts +1 -0
  11. package/Server/Utils/OpenAPI.ts +28 -0
  12. package/Server/Utils/StartServer.ts +20 -1
  13. package/UI/Components/LogsViewer/LogsViewer.tsx +204 -64
  14. package/UI/Components/LogsViewer/components/ColumnSelector.tsx +270 -0
  15. package/UI/Components/LogsViewer/components/LiveLogsToggle.tsx +3 -3
  16. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +2 -2
  17. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +699 -0
  18. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +46 -1
  19. package/UI/Components/LogsViewer/components/LogsFilterCard.tsx +3 -3
  20. package/UI/Components/LogsViewer/components/LogsTable.tsx +288 -103
  21. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +113 -11
  22. package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +175 -0
  23. package/UI/Components/LogsViewer/types.ts +96 -0
  24. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  25. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  26. package/build/dist/Models/DatabaseModels/LogSavedView.js +496 -0
  27. package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -0
  28. package/build/dist/Server/API/TelemetryAPI.js +88 -0
  29. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  30. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1772355000000-AddLogSavedView.js +44 -0
  31. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1772355000000-AddLogSavedView.js.map +1 -0
  32. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773344537755-MigrationName.js +38 -0
  33. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773344537755-MigrationName.js.map +1 -0
  34. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  35. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  36. package/build/dist/Server/Services/LogAggregationService.js +249 -0
  37. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  38. package/build/dist/Server/Services/LogSavedViewService.js +82 -0
  39. package/build/dist/Server/Services/LogSavedViewService.js.map +1 -0
  40. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js +15 -0
  41. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js.map +1 -1
  42. package/build/dist/Server/Utils/Express.js +1 -0
  43. package/build/dist/Server/Utils/Express.js.map +1 -1
  44. package/build/dist/Server/Utils/OpenAPI.js +24 -0
  45. package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
  46. package/build/dist/Server/Utils/StartServer.js +17 -2
  47. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  48. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +77 -8
  49. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  50. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +115 -0
  51. package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -0
  52. package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js +3 -3
  53. package/build/dist/UI/Components/LogsViewer/components/LiveLogsToggle.js.map +1 -1
  54. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +2 -2
  55. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  56. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +379 -0
  57. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -0
  58. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +27 -13
  59. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  60. package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js +3 -3
  61. package/build/dist/UI/Components/LogsViewer/components/LogsFilterCard.js.map +1 -1
  62. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +118 -49
  63. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  64. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +35 -11
  65. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  66. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +58 -0
  67. package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -0
  68. package/build/dist/UI/Components/LogsViewer/types.js +60 -1
  69. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  70. package/package.json +2 -2
@@ -36,6 +36,7 @@ import IncidentTemplateOwnerTeam from "./IncidentTemplateOwnerTeam";
36
36
  import IncidentTemplateOwnerUser from "./IncidentTemplateOwnerUser";
37
37
  //Labels.
38
38
  import Label from "./Label";
39
+ import LogSavedView from "./LogSavedView";
39
40
  // Monitors
40
41
  import Monitor from "./Monitor";
41
42
  import MonitorCustomField from "./MonitorCustomField";
@@ -250,6 +251,7 @@ const AllModelTypes: Array<{
250
251
  TeamComplianceSetting,
251
252
  ApiKey,
252
253
  Label,
254
+ LogSavedView,
253
255
  ApiKeyPermission,
254
256
  ProjectSmtpConfig,
255
257
  StatusPage,
@@ -0,0 +1,466 @@
1
+ import Project from "./Project";
2
+ import User from "./User";
3
+ import Log from "../AnalyticsModels/Log";
4
+ import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
5
+ import Route from "../../Types/API/Route";
6
+ import SortOrder from "../../Types/BaseDatabase/SortOrder";
7
+ import Query from "../../Types/BaseDatabase/Query";
8
+ import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
9
+ import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
10
+ import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
11
+ import ColumnLength from "../../Types/Database/ColumnLength";
12
+ import ColumnType from "../../Types/Database/ColumnType";
13
+ import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
14
+ import EnableDocumentation from "../../Types/Database/EnableDocumentation";
15
+ import TableColumn from "../../Types/Database/TableColumn";
16
+ import TableColumnType from "../../Types/Database/TableColumnType";
17
+ import TableMetadata from "../../Types/Database/TableMetadata";
18
+ import TenantColumn from "../../Types/Database/TenantColumn";
19
+ import IconProp from "../../Types/Icon/IconProp";
20
+ import ObjectID from "../../Types/ObjectID";
21
+ import Permission from "../../Types/Permission";
22
+ import { PlanType } from "../../Types/Billing/SubscriptionPlan";
23
+ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
24
+
25
+ @EnableDocumentation()
26
+ @TableBillingAccessControl({
27
+ create: PlanType.Free,
28
+ read: PlanType.Free,
29
+ update: PlanType.Free,
30
+ delete: PlanType.Free,
31
+ })
32
+ @TenantColumn("projectId")
33
+ @CrudApiEndpoint(new Route("/log-saved-view"))
34
+ @Entity({
35
+ name: "LogSavedView",
36
+ })
37
+ @TableMetadata({
38
+ tableName: "LogSavedView",
39
+ singularName: "Log Saved View",
40
+ pluralName: "Log Saved Views",
41
+ icon: IconProp.Window,
42
+ tableDescription:
43
+ "Save and reuse log explorer views, including the current filters, columns, sorting, and page size.",
44
+ })
45
+ @TableAccessControl({
46
+ create: [
47
+ Permission.ProjectOwner,
48
+ Permission.ProjectAdmin,
49
+ Permission.ProjectMember,
50
+ ],
51
+ read: [
52
+ Permission.ProjectOwner,
53
+ Permission.ProjectAdmin,
54
+ Permission.ProjectMember,
55
+ Permission.ReadAllProjectResources,
56
+ ],
57
+ delete: [
58
+ Permission.ProjectOwner,
59
+ Permission.ProjectAdmin,
60
+ Permission.ProjectMember,
61
+ ],
62
+ update: [
63
+ Permission.ProjectOwner,
64
+ Permission.ProjectAdmin,
65
+ Permission.ProjectMember,
66
+ ],
67
+ })
68
+ export default class LogSavedView extends BaseModel {
69
+ @ColumnAccessControl({
70
+ create: [
71
+ Permission.ProjectOwner,
72
+ Permission.ProjectAdmin,
73
+ Permission.ProjectMember,
74
+ ],
75
+ read: [
76
+ Permission.ProjectOwner,
77
+ Permission.ProjectAdmin,
78
+ Permission.ProjectMember,
79
+ Permission.ReadAllProjectResources,
80
+ ],
81
+ update: [],
82
+ })
83
+ @TableColumn({
84
+ manyToOneRelationColumn: "projectId",
85
+ type: TableColumnType.Entity,
86
+ modelType: Project,
87
+ title: "Project",
88
+ description: "Relation to the project this saved log view belongs to.",
89
+ })
90
+ @ManyToOne(
91
+ () => {
92
+ return Project;
93
+ },
94
+ {
95
+ eager: false,
96
+ nullable: true,
97
+ onDelete: "CASCADE",
98
+ orphanedRowAction: "nullify",
99
+ },
100
+ )
101
+ @JoinColumn({ name: "projectId" })
102
+ public project?: Project = undefined;
103
+
104
+ @ColumnAccessControl({
105
+ create: [
106
+ Permission.ProjectOwner,
107
+ Permission.ProjectAdmin,
108
+ Permission.ProjectMember,
109
+ ],
110
+ read: [
111
+ Permission.ProjectOwner,
112
+ Permission.ProjectAdmin,
113
+ Permission.ProjectMember,
114
+ Permission.ReadAllProjectResources,
115
+ ],
116
+ update: [],
117
+ })
118
+ @Index()
119
+ @TableColumn({
120
+ type: TableColumnType.ObjectID,
121
+ required: true,
122
+ canReadOnRelationQuery: true,
123
+ title: "Project ID",
124
+ description: "ID of the project this saved log view belongs to.",
125
+ })
126
+ @Column({
127
+ type: ColumnType.ObjectID,
128
+ nullable: false,
129
+ transformer: ObjectID.getDatabaseTransformer(),
130
+ })
131
+ public projectId?: ObjectID = undefined;
132
+
133
+ @ColumnAccessControl({
134
+ create: [
135
+ Permission.ProjectOwner,
136
+ Permission.ProjectAdmin,
137
+ Permission.ProjectMember,
138
+ ],
139
+ read: [
140
+ Permission.ProjectOwner,
141
+ Permission.ProjectAdmin,
142
+ Permission.ProjectMember,
143
+ Permission.ReadAllProjectResources,
144
+ ],
145
+ update: [
146
+ Permission.ProjectOwner,
147
+ Permission.ProjectAdmin,
148
+ Permission.ProjectMember,
149
+ ],
150
+ })
151
+ @TableColumn({
152
+ required: true,
153
+ type: TableColumnType.Name,
154
+ canReadOnRelationQuery: true,
155
+ title: "Name",
156
+ description: "Friendly name for this saved log view.",
157
+ })
158
+ @Column({
159
+ nullable: false,
160
+ type: ColumnType.Name,
161
+ length: ColumnLength.Name,
162
+ })
163
+ public name?: string = undefined;
164
+
165
+ @ColumnAccessControl({
166
+ create: [],
167
+ read: [
168
+ Permission.ProjectOwner,
169
+ Permission.ProjectAdmin,
170
+ Permission.ProjectMember,
171
+ Permission.ReadAllProjectResources,
172
+ ],
173
+ update: [],
174
+ })
175
+ @TableColumn({
176
+ manyToOneRelationColumn: "createdByUserId",
177
+ type: TableColumnType.Entity,
178
+ modelType: User,
179
+ title: "Created By User",
180
+ description: "Relation to the user who created this saved log view.",
181
+ })
182
+ @ManyToOne(
183
+ () => {
184
+ return User;
185
+ },
186
+ {
187
+ eager: false,
188
+ nullable: true,
189
+ onDelete: "SET NULL",
190
+ orphanedRowAction: "nullify",
191
+ },
192
+ )
193
+ @JoinColumn({ name: "createdByUserId" })
194
+ public createdByUser?: User = undefined;
195
+
196
+ @ColumnAccessControl({
197
+ create: [],
198
+ read: [
199
+ Permission.ProjectOwner,
200
+ Permission.ProjectAdmin,
201
+ Permission.ProjectMember,
202
+ Permission.ReadAllProjectResources,
203
+ ],
204
+ update: [],
205
+ })
206
+ @TableColumn({
207
+ type: TableColumnType.ObjectID,
208
+ title: "Created By User ID",
209
+ description: "ID of the user who created this saved log view.",
210
+ })
211
+ @Column({
212
+ type: ColumnType.ObjectID,
213
+ nullable: true,
214
+ transformer: ObjectID.getDatabaseTransformer(),
215
+ })
216
+ public createdByUserId?: ObjectID = undefined;
217
+
218
+ @ColumnAccessControl({
219
+ create: [],
220
+ read: [
221
+ Permission.ProjectOwner,
222
+ Permission.ProjectAdmin,
223
+ Permission.ProjectMember,
224
+ Permission.ReadAllProjectResources,
225
+ ],
226
+ update: [],
227
+ })
228
+ @TableColumn({
229
+ manyToOneRelationColumn: "deletedByUserId",
230
+ type: TableColumnType.Entity,
231
+ modelType: User,
232
+ title: "Deleted By User",
233
+ description: "Relation to the user who deleted this saved log view.",
234
+ })
235
+ @ManyToOne(
236
+ () => {
237
+ return User;
238
+ },
239
+ {
240
+ eager: false,
241
+ nullable: true,
242
+ onDelete: "SET NULL",
243
+ orphanedRowAction: "nullify",
244
+ },
245
+ )
246
+ @JoinColumn({ name: "deletedByUserId" })
247
+ public deletedByUser?: User = undefined;
248
+
249
+ @ColumnAccessControl({
250
+ create: [],
251
+ read: [
252
+ Permission.ProjectOwner,
253
+ Permission.ProjectAdmin,
254
+ Permission.ProjectMember,
255
+ Permission.ReadAllProjectResources,
256
+ ],
257
+ update: [],
258
+ })
259
+ @TableColumn({
260
+ type: TableColumnType.ObjectID,
261
+ title: "Deleted By User ID",
262
+ description: "ID of the user who deleted this saved log view.",
263
+ })
264
+ @Column({
265
+ type: ColumnType.ObjectID,
266
+ nullable: true,
267
+ transformer: ObjectID.getDatabaseTransformer(),
268
+ })
269
+ public deletedByUserId?: ObjectID = undefined;
270
+
271
+ @ColumnAccessControl({
272
+ create: [
273
+ Permission.ProjectOwner,
274
+ Permission.ProjectAdmin,
275
+ Permission.ProjectMember,
276
+ ],
277
+ read: [
278
+ Permission.ProjectOwner,
279
+ Permission.ProjectAdmin,
280
+ Permission.ProjectMember,
281
+ Permission.ReadAllProjectResources,
282
+ ],
283
+ update: [
284
+ Permission.ProjectOwner,
285
+ Permission.ProjectAdmin,
286
+ Permission.ProjectMember,
287
+ ],
288
+ })
289
+ @TableColumn({
290
+ title: "Query",
291
+ required: true,
292
+ type: TableColumnType.JSON,
293
+ canReadOnRelationQuery: true,
294
+ description: "Serialized log query for this saved view.",
295
+ })
296
+ @Column({
297
+ type: ColumnType.JSON,
298
+ nullable: false,
299
+ })
300
+ public query?: Query<Log> = undefined;
301
+
302
+ @ColumnAccessControl({
303
+ create: [
304
+ Permission.ProjectOwner,
305
+ Permission.ProjectAdmin,
306
+ Permission.ProjectMember,
307
+ ],
308
+ read: [
309
+ Permission.ProjectOwner,
310
+ Permission.ProjectAdmin,
311
+ Permission.ProjectMember,
312
+ Permission.ReadAllProjectResources,
313
+ ],
314
+ update: [
315
+ Permission.ProjectOwner,
316
+ Permission.ProjectAdmin,
317
+ Permission.ProjectMember,
318
+ ],
319
+ })
320
+ @TableColumn({
321
+ title: "Columns",
322
+ required: true,
323
+ type: TableColumnType.JSON,
324
+ canReadOnRelationQuery: true,
325
+ description: "Selected log table columns for this saved view.",
326
+ })
327
+ @Column({
328
+ type: ColumnType.JSON,
329
+ nullable: false,
330
+ default: () => {
331
+ return "'[]'";
332
+ },
333
+ })
334
+ public columns?: Array<string> = undefined;
335
+
336
+ @ColumnAccessControl({
337
+ create: [
338
+ Permission.ProjectOwner,
339
+ Permission.ProjectAdmin,
340
+ Permission.ProjectMember,
341
+ ],
342
+ read: [
343
+ Permission.ProjectOwner,
344
+ Permission.ProjectAdmin,
345
+ Permission.ProjectMember,
346
+ Permission.ReadAllProjectResources,
347
+ ],
348
+ update: [
349
+ Permission.ProjectOwner,
350
+ Permission.ProjectAdmin,
351
+ Permission.ProjectMember,
352
+ ],
353
+ })
354
+ @TableColumn({
355
+ required: false,
356
+ type: TableColumnType.ShortText,
357
+ canReadOnRelationQuery: true,
358
+ title: "Sort Field",
359
+ description: "Active sort field for this saved log view.",
360
+ })
361
+ @Column({
362
+ nullable: true,
363
+ type: ColumnType.ShortText,
364
+ length: ColumnLength.ShortText,
365
+ })
366
+ public sortField?: string = undefined;
367
+
368
+ @ColumnAccessControl({
369
+ create: [
370
+ Permission.ProjectOwner,
371
+ Permission.ProjectAdmin,
372
+ Permission.ProjectMember,
373
+ ],
374
+ read: [
375
+ Permission.ProjectOwner,
376
+ Permission.ProjectAdmin,
377
+ Permission.ProjectMember,
378
+ Permission.ReadAllProjectResources,
379
+ ],
380
+ update: [
381
+ Permission.ProjectOwner,
382
+ Permission.ProjectAdmin,
383
+ Permission.ProjectMember,
384
+ ],
385
+ })
386
+ @TableColumn({
387
+ required: false,
388
+ type: TableColumnType.ShortText,
389
+ canReadOnRelationQuery: true,
390
+ title: "Sort Order",
391
+ description: "Sort order for this saved log view.",
392
+ })
393
+ @Column({
394
+ nullable: true,
395
+ type: ColumnType.ShortText,
396
+ length: ColumnLength.ShortText,
397
+ })
398
+ public sortOrder?: SortOrder = undefined;
399
+
400
+ @ColumnAccessControl({
401
+ create: [
402
+ Permission.ProjectOwner,
403
+ Permission.ProjectAdmin,
404
+ Permission.ProjectMember,
405
+ ],
406
+ read: [
407
+ Permission.ProjectOwner,
408
+ Permission.ProjectAdmin,
409
+ Permission.ProjectMember,
410
+ Permission.ReadAllProjectResources,
411
+ ],
412
+ update: [
413
+ Permission.ProjectOwner,
414
+ Permission.ProjectAdmin,
415
+ Permission.ProjectMember,
416
+ ],
417
+ })
418
+ @TableColumn({
419
+ title: "Page Size",
420
+ required: true,
421
+ type: TableColumnType.Number,
422
+ canReadOnRelationQuery: true,
423
+ description: "Number of logs per page for this saved view.",
424
+ defaultValue: 100,
425
+ })
426
+ @Column({
427
+ type: ColumnType.Number,
428
+ nullable: false,
429
+ default: 100,
430
+ })
431
+ public pageSize?: number = undefined;
432
+
433
+ @ColumnAccessControl({
434
+ create: [
435
+ Permission.ProjectOwner,
436
+ Permission.ProjectAdmin,
437
+ Permission.ProjectMember,
438
+ ],
439
+ read: [
440
+ Permission.ProjectOwner,
441
+ Permission.ProjectAdmin,
442
+ Permission.ProjectMember,
443
+ Permission.ReadAllProjectResources,
444
+ ],
445
+ update: [
446
+ Permission.ProjectOwner,
447
+ Permission.ProjectAdmin,
448
+ Permission.ProjectMember,
449
+ ],
450
+ })
451
+ @Index()
452
+ @TableColumn({
453
+ required: true,
454
+ type: TableColumnType.Boolean,
455
+ canReadOnRelationQuery: true,
456
+ title: "Is Default",
457
+ description: "Whether this saved log view should be applied by default.",
458
+ defaultValue: false,
459
+ })
460
+ @Column({
461
+ nullable: false,
462
+ type: ColumnType.Boolean,
463
+ default: false,
464
+ })
465
+ public isDefault?: boolean = undefined;
466
+ }
@@ -16,6 +16,12 @@ import LogAggregationService, {
16
16
  HistogramRequest,
17
17
  FacetValue,
18
18
  FacetRequest,
19
+ AnalyticsRequest,
20
+ AnalyticsChartType,
21
+ AnalyticsAggregation,
22
+ AnalyticsTimeseriesRow,
23
+ AnalyticsTopItem,
24
+ AnalyticsTableRow,
19
25
  } from "../Services/LogAggregationService";
20
26
  import ObjectID from "../../Types/ObjectID";
21
27
  import OneUptimeDate from "../../Types/Date";
@@ -264,6 +270,146 @@ router.post(
264
270
  },
265
271
  );
266
272
 
273
+ // --- Log Analytics Endpoint ---
274
+
275
+ router.post(
276
+ "/telemetry/logs/analytics",
277
+ UserMiddleware.getUserMiddleware,
278
+ async (
279
+ req: ExpressRequest,
280
+ res: ExpressResponse,
281
+ next: NextFunction,
282
+ ): Promise<void> => {
283
+ try {
284
+ const databaseProps: DatabaseCommonInteractionProps =
285
+ await CommonAPI.getDatabaseCommonInteractionProps(req);
286
+
287
+ if (!databaseProps?.tenantId) {
288
+ return Response.sendErrorResponse(
289
+ req,
290
+ res,
291
+ new BadDataException("Invalid Project ID"),
292
+ );
293
+ }
294
+
295
+ const body: JSONObject = req.body as JSONObject;
296
+
297
+ const chartType: AnalyticsChartType =
298
+ (body["chartType"] as AnalyticsChartType) || "timeseries";
299
+
300
+ if (!["timeseries", "toplist", "table"].includes(chartType)) {
301
+ return Response.sendErrorResponse(
302
+ req,
303
+ res,
304
+ new BadDataException("Invalid chartType"),
305
+ );
306
+ }
307
+
308
+ const aggregation: AnalyticsAggregation =
309
+ (body["aggregation"] as AnalyticsAggregation) || "count";
310
+
311
+ if (!["count", "unique"].includes(aggregation)) {
312
+ return Response.sendErrorResponse(
313
+ req,
314
+ res,
315
+ new BadDataException("Invalid aggregation"),
316
+ );
317
+ }
318
+
319
+ const startTime: Date = body["startTime"]
320
+ ? OneUptimeDate.fromString(body["startTime"] as string)
321
+ : OneUptimeDate.addRemoveHours(OneUptimeDate.getCurrentDate(), -1);
322
+
323
+ const endTime: Date = body["endTime"]
324
+ ? OneUptimeDate.fromString(body["endTime"] as string)
325
+ : OneUptimeDate.getCurrentDate();
326
+
327
+ const bucketSizeInMinutes: number =
328
+ (body["bucketSizeInMinutes"] as number) ||
329
+ computeDefaultBucketSize(startTime, endTime);
330
+
331
+ const serviceIds: Array<ObjectID> | undefined = body["serviceIds"]
332
+ ? (body["serviceIds"] as Array<string>).map((id: string) => {
333
+ return new ObjectID(id);
334
+ })
335
+ : undefined;
336
+
337
+ const severityTexts: Array<string> | undefined = body["severityTexts"]
338
+ ? (body["severityTexts"] as Array<string>)
339
+ : undefined;
340
+
341
+ const bodySearchText: string | undefined = body["bodySearchText"]
342
+ ? (body["bodySearchText"] as string)
343
+ : undefined;
344
+
345
+ const traceIds: Array<string> | undefined = body["traceIds"]
346
+ ? (body["traceIds"] as Array<string>)
347
+ : undefined;
348
+
349
+ const spanIds: Array<string> | undefined = body["spanIds"]
350
+ ? (body["spanIds"] as Array<string>)
351
+ : undefined;
352
+
353
+ const groupBy: Array<string> | undefined = body["groupBy"]
354
+ ? (body["groupBy"] as Array<string>)
355
+ : undefined;
356
+
357
+ const aggregationField: string | undefined = body["aggregationField"]
358
+ ? (body["aggregationField"] as string)
359
+ : undefined;
360
+
361
+ const limit: number | undefined = body["limit"]
362
+ ? (body["limit"] as number)
363
+ : undefined;
364
+
365
+ const request: AnalyticsRequest = {
366
+ projectId: databaseProps.tenantId,
367
+ startTime,
368
+ endTime,
369
+ bucketSizeInMinutes,
370
+ chartType,
371
+ groupBy,
372
+ aggregation,
373
+ aggregationField,
374
+ serviceIds,
375
+ severityTexts,
376
+ bodySearchText,
377
+ traceIds,
378
+ spanIds,
379
+ limit,
380
+ };
381
+
382
+ if (chartType === "timeseries") {
383
+ const data: Array<AnalyticsTimeseriesRow> =
384
+ await LogAggregationService.getAnalyticsTimeseries(request);
385
+
386
+ return Response.sendJsonObjectResponse(req, res, {
387
+ data: data as unknown as JSONObject,
388
+ });
389
+ }
390
+
391
+ if (chartType === "toplist") {
392
+ const data: Array<AnalyticsTopItem> =
393
+ await LogAggregationService.getAnalyticsTopList(request);
394
+
395
+ return Response.sendJsonObjectResponse(req, res, {
396
+ data: data as unknown as JSONObject,
397
+ });
398
+ }
399
+
400
+ // table
401
+ const data: Array<AnalyticsTableRow> =
402
+ await LogAggregationService.getAnalyticsTable(request);
403
+
404
+ return Response.sendJsonObjectResponse(req, res, {
405
+ data: data as unknown as JSONObject,
406
+ });
407
+ } catch (err: unknown) {
408
+ next(err);
409
+ }
410
+ },
411
+ );
412
+
267
413
  // --- Helpers ---
268
414
 
269
415
  function computeDefaultBucketSize(startTime: Date, endTime: Date): number {
@@ -0,0 +1,48 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+ import CaptureSpan from "../../../Utils/Telemetry/CaptureSpan";
3
+
4
+ export class AddLogSavedView1772355000000 implements MigrationInterface {
5
+ public name = "AddLogSavedView1772355000000";
6
+
7
+ @CaptureSpan()
8
+ public async up(queryRunner: QueryRunner): Promise<void> {
9
+ await queryRunner.query(
10
+ `CREATE TABLE "LogSavedView" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "name" character varying(100) NOT NULL, "createdByUserId" uuid, "deletedByUserId" uuid, "query" jsonb NOT NULL, "columns" jsonb NOT NULL DEFAULT '[]', "sortField" character varying(100), "sortOrder" character varying(100), "pageSize" integer NOT NULL DEFAULT '100', "isDefault" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_b1d3249a8cce9f7168bded6d55a" PRIMARY KEY ("_id"))`,
11
+ );
12
+ await queryRunner.query(
13
+ `CREATE INDEX "IDX_56e1d744839c4e59c50de300a9" ON "LogSavedView" ("projectId") `,
14
+ );
15
+ await queryRunner.query(
16
+ `CREATE INDEX "IDX_80241afbecf0a3749cc775f93f" ON "LogSavedView" ("isDefault") `,
17
+ );
18
+ await queryRunner.query(
19
+ `ALTER TABLE "LogSavedView" ADD CONSTRAINT "FK_56e1d744839c4e59c50de300a9d" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
20
+ );
21
+ await queryRunner.query(
22
+ `ALTER TABLE "LogSavedView" ADD CONSTRAINT "FK_fa55f4b8cb6e6ce31554b7b021f" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
23
+ );
24
+ await queryRunner.query(
25
+ `ALTER TABLE "LogSavedView" ADD CONSTRAINT "FK_8bd2b62c5f269dc8b2c74da0f27" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
26
+ );
27
+ }
28
+
29
+ @CaptureSpan()
30
+ public async down(queryRunner: QueryRunner): Promise<void> {
31
+ await queryRunner.query(
32
+ `ALTER TABLE "LogSavedView" DROP CONSTRAINT "FK_8bd2b62c5f269dc8b2c74da0f27"`,
33
+ );
34
+ await queryRunner.query(
35
+ `ALTER TABLE "LogSavedView" DROP CONSTRAINT "FK_fa55f4b8cb6e6ce31554b7b021f"`,
36
+ );
37
+ await queryRunner.query(
38
+ `ALTER TABLE "LogSavedView" DROP CONSTRAINT "FK_56e1d744839c4e59c50de300a9d"`,
39
+ );
40
+ await queryRunner.query(
41
+ `DROP INDEX "public"."IDX_80241afbecf0a3749cc775f93f"`,
42
+ );
43
+ await queryRunner.query(
44
+ `DROP INDEX "public"."IDX_56e1d744839c4e59c50de300a9"`,
45
+ );
46
+ await queryRunner.query(`DROP TABLE "LogSavedView"`);
47
+ }
48
+ }