@oneuptime/common 10.5.30 → 10.5.32

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 (52) hide show
  1. package/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +45 -0
  2. package/Models/AnalyticsModels/AuditLog.ts +3 -0
  3. package/Models/AnalyticsModels/ExceptionInstance.ts +3 -0
  4. package/Models/AnalyticsModels/Index.ts +2 -0
  5. package/Models/AnalyticsModels/Log.ts +3 -0
  6. package/Models/AnalyticsModels/Metric.ts +3 -0
  7. package/Models/AnalyticsModels/Span.ts +3 -0
  8. package/Models/DatabaseModels/Index.ts +4 -0
  9. package/Models/DatabaseModels/MetricSavedView.ts +334 -0
  10. package/Models/DatabaseModels/TraceSavedView.ts +334 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1780645560183-AddMetricAndTraceSavedView.ts +81 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  13. package/Server/Services/MetricSavedViewService.ts +109 -0
  14. package/Server/Services/TraceSavedViewService.ts +109 -0
  15. package/Types/Telemetry/TelemetrySavedViewState.ts +26 -0
  16. package/UI/Components/Table/TableHeader.tsx +6 -2
  17. package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +175 -0
  18. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +32 -0
  19. package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
  20. package/build/dist/Models/AnalyticsModels/AuditLog.js +2 -0
  21. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  22. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +2 -0
  23. package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
  24. package/build/dist/Models/AnalyticsModels/Index.js +2 -0
  25. package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
  26. package/build/dist/Models/AnalyticsModels/Log.js +2 -0
  27. package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
  28. package/build/dist/Models/AnalyticsModels/Metric.js +2 -0
  29. package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
  30. package/build/dist/Models/AnalyticsModels/Span.js +2 -0
  31. package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
  32. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  33. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  34. package/build/dist/Models/DatabaseModels/MetricSavedView.js +356 -0
  35. package/build/dist/Models/DatabaseModels/MetricSavedView.js.map +1 -0
  36. package/build/dist/Models/DatabaseModels/TraceSavedView.js +356 -0
  37. package/build/dist/Models/DatabaseModels/TraceSavedView.js.map +1 -0
  38. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1780645560183-AddMetricAndTraceSavedView.js +34 -0
  39. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1780645560183-AddMetricAndTraceSavedView.js.map +1 -0
  40. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  41. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  42. package/build/dist/Server/Services/MetricSavedViewService.js +82 -0
  43. package/build/dist/Server/Services/MetricSavedViewService.js.map +1 -0
  44. package/build/dist/Server/Services/TraceSavedViewService.js +82 -0
  45. package/build/dist/Server/Services/TraceSavedViewService.js.map +1 -0
  46. package/build/dist/Types/Telemetry/TelemetrySavedViewState.js +9 -0
  47. package/build/dist/Types/Telemetry/TelemetrySavedViewState.js.map +1 -0
  48. package/build/dist/UI/Components/Table/TableHeader.js +2 -2
  49. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  50. package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +58 -0
  51. package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -0
  52. package/package.json +1 -1
@@ -0,0 +1,334 @@
1
+ import Project from "./Project";
2
+ import User from "./User";
3
+ import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
4
+ import Route from "../../Types/API/Route";
5
+ import TelemetrySavedViewState from "../../Types/Telemetry/TelemetrySavedViewState";
6
+ import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
7
+ import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
8
+ import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
9
+ import ColumnLength from "../../Types/Database/ColumnLength";
10
+ import ColumnType from "../../Types/Database/ColumnType";
11
+ import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
12
+ import EnableDocumentation from "../../Types/Database/EnableDocumentation";
13
+ import TableColumn from "../../Types/Database/TableColumn";
14
+ import TableColumnType from "../../Types/Database/TableColumnType";
15
+ import TableMetadata from "../../Types/Database/TableMetadata";
16
+ import TenantColumn from "../../Types/Database/TenantColumn";
17
+ import IconProp from "../../Types/Icon/IconProp";
18
+ import ObjectID from "../../Types/ObjectID";
19
+ import Permission from "../../Types/Permission";
20
+ import { PlanType } from "../../Types/Billing/SubscriptionPlan";
21
+ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
22
+
23
+ @EnableDocumentation()
24
+ @TableBillingAccessControl({
25
+ create: PlanType.Free,
26
+ read: PlanType.Free,
27
+ update: PlanType.Free,
28
+ delete: PlanType.Free,
29
+ })
30
+ @TenantColumn("projectId")
31
+ @CrudApiEndpoint(new Route("/trace-saved-view"))
32
+ @Entity({
33
+ name: "TraceSavedView",
34
+ })
35
+ @TableMetadata({
36
+ tableName: "TraceSavedView",
37
+ singularName: "Trace Saved View",
38
+ pluralName: "Trace Saved Views",
39
+ icon: IconProp.Window,
40
+ tableDescription:
41
+ "Save and reuse traces explorer views, including the current search, filters, time range, and page size.",
42
+ })
43
+ @TableAccessControl({
44
+ create: [
45
+ Permission.ProjectOwner,
46
+ Permission.ProjectAdmin,
47
+ Permission.ProjectMember,
48
+ ],
49
+ read: [
50
+ Permission.ProjectOwner,
51
+ Permission.ProjectAdmin,
52
+ Permission.ProjectMember,
53
+ Permission.Viewer,
54
+ ],
55
+ delete: [
56
+ Permission.ProjectOwner,
57
+ Permission.ProjectAdmin,
58
+ Permission.ProjectMember,
59
+ ],
60
+ update: [
61
+ Permission.ProjectOwner,
62
+ Permission.ProjectAdmin,
63
+ Permission.ProjectMember,
64
+ ],
65
+ })
66
+ export default class TraceSavedView extends BaseModel {
67
+ @ColumnAccessControl({
68
+ create: [
69
+ Permission.ProjectOwner,
70
+ Permission.ProjectAdmin,
71
+ Permission.ProjectMember,
72
+ ],
73
+ read: [
74
+ Permission.ProjectOwner,
75
+ Permission.ProjectAdmin,
76
+ Permission.ProjectMember,
77
+ Permission.Viewer,
78
+ ],
79
+ update: [],
80
+ })
81
+ @TableColumn({
82
+ manyToOneRelationColumn: "projectId",
83
+ type: TableColumnType.Entity,
84
+ modelType: Project,
85
+ title: "Project",
86
+ description: "Relation to the project this saved trace view belongs to.",
87
+ })
88
+ @ManyToOne(
89
+ () => {
90
+ return Project;
91
+ },
92
+ {
93
+ eager: false,
94
+ nullable: true,
95
+ onDelete: "CASCADE",
96
+ orphanedRowAction: "nullify",
97
+ },
98
+ )
99
+ @JoinColumn({ name: "projectId" })
100
+ public project?: Project = undefined;
101
+
102
+ @ColumnAccessControl({
103
+ create: [
104
+ Permission.ProjectOwner,
105
+ Permission.ProjectAdmin,
106
+ Permission.ProjectMember,
107
+ ],
108
+ read: [
109
+ Permission.ProjectOwner,
110
+ Permission.ProjectAdmin,
111
+ Permission.ProjectMember,
112
+ Permission.Viewer,
113
+ ],
114
+ update: [],
115
+ })
116
+ @Index()
117
+ @TableColumn({
118
+ type: TableColumnType.ObjectID,
119
+ required: true,
120
+ canReadOnRelationQuery: true,
121
+ title: "Project ID",
122
+ description: "ID of the project this saved trace view belongs to.",
123
+ })
124
+ @Column({
125
+ type: ColumnType.ObjectID,
126
+ nullable: false,
127
+ transformer: ObjectID.getDatabaseTransformer(),
128
+ })
129
+ public projectId?: ObjectID = undefined;
130
+
131
+ @ColumnAccessControl({
132
+ create: [
133
+ Permission.ProjectOwner,
134
+ Permission.ProjectAdmin,
135
+ Permission.ProjectMember,
136
+ ],
137
+ read: [
138
+ Permission.ProjectOwner,
139
+ Permission.ProjectAdmin,
140
+ Permission.ProjectMember,
141
+ Permission.Viewer,
142
+ ],
143
+ update: [
144
+ Permission.ProjectOwner,
145
+ Permission.ProjectAdmin,
146
+ Permission.ProjectMember,
147
+ ],
148
+ })
149
+ @TableColumn({
150
+ required: true,
151
+ type: TableColumnType.Name,
152
+ canReadOnRelationQuery: true,
153
+ title: "Name",
154
+ description: "Friendly name for this saved trace view.",
155
+ })
156
+ @Column({
157
+ nullable: false,
158
+ type: ColumnType.Name,
159
+ length: ColumnLength.Name,
160
+ })
161
+ public name?: string = undefined;
162
+
163
+ @ColumnAccessControl({
164
+ create: [],
165
+ read: [
166
+ Permission.ProjectOwner,
167
+ Permission.ProjectAdmin,
168
+ Permission.ProjectMember,
169
+ Permission.Viewer,
170
+ ],
171
+ update: [],
172
+ })
173
+ @TableColumn({
174
+ manyToOneRelationColumn: "createdByUserId",
175
+ type: TableColumnType.Entity,
176
+ modelType: User,
177
+ title: "Created By User",
178
+ description: "Relation to the user who created this saved trace view.",
179
+ })
180
+ @ManyToOne(
181
+ () => {
182
+ return User;
183
+ },
184
+ {
185
+ eager: false,
186
+ nullable: true,
187
+ onDelete: "SET NULL",
188
+ orphanedRowAction: "nullify",
189
+ },
190
+ )
191
+ @JoinColumn({ name: "createdByUserId" })
192
+ public createdByUser?: User = undefined;
193
+
194
+ @ColumnAccessControl({
195
+ create: [],
196
+ read: [
197
+ Permission.ProjectOwner,
198
+ Permission.ProjectAdmin,
199
+ Permission.ProjectMember,
200
+ Permission.Viewer,
201
+ ],
202
+ update: [],
203
+ })
204
+ @TableColumn({
205
+ type: TableColumnType.ObjectID,
206
+ title: "Created By User ID",
207
+ description: "ID of the user who created this saved trace view.",
208
+ })
209
+ @Column({
210
+ type: ColumnType.ObjectID,
211
+ nullable: true,
212
+ transformer: ObjectID.getDatabaseTransformer(),
213
+ })
214
+ public createdByUserId?: ObjectID = undefined;
215
+
216
+ @ColumnAccessControl({
217
+ create: [],
218
+ read: [
219
+ Permission.ProjectOwner,
220
+ Permission.ProjectAdmin,
221
+ Permission.ProjectMember,
222
+ Permission.Viewer,
223
+ ],
224
+ update: [],
225
+ })
226
+ @TableColumn({
227
+ manyToOneRelationColumn: "deletedByUserId",
228
+ type: TableColumnType.Entity,
229
+ modelType: User,
230
+ title: "Deleted By User",
231
+ description: "Relation to the user who deleted this saved trace view.",
232
+ })
233
+ @ManyToOne(
234
+ () => {
235
+ return User;
236
+ },
237
+ {
238
+ eager: false,
239
+ nullable: true,
240
+ onDelete: "SET NULL",
241
+ orphanedRowAction: "nullify",
242
+ },
243
+ )
244
+ @JoinColumn({ name: "deletedByUserId" })
245
+ public deletedByUser?: User = undefined;
246
+
247
+ @ColumnAccessControl({
248
+ create: [],
249
+ read: [
250
+ Permission.ProjectOwner,
251
+ Permission.ProjectAdmin,
252
+ Permission.ProjectMember,
253
+ Permission.Viewer,
254
+ ],
255
+ update: [],
256
+ })
257
+ @TableColumn({
258
+ type: TableColumnType.ObjectID,
259
+ title: "Deleted By User ID",
260
+ description: "ID of the user who deleted this saved trace view.",
261
+ })
262
+ @Column({
263
+ type: ColumnType.ObjectID,
264
+ nullable: true,
265
+ transformer: ObjectID.getDatabaseTransformer(),
266
+ })
267
+ public deletedByUserId?: ObjectID = undefined;
268
+
269
+ @ColumnAccessControl({
270
+ create: [
271
+ Permission.ProjectOwner,
272
+ Permission.ProjectAdmin,
273
+ Permission.ProjectMember,
274
+ ],
275
+ read: [
276
+ Permission.ProjectOwner,
277
+ Permission.ProjectAdmin,
278
+ Permission.ProjectMember,
279
+ Permission.Viewer,
280
+ ],
281
+ update: [
282
+ Permission.ProjectOwner,
283
+ Permission.ProjectAdmin,
284
+ Permission.ProjectMember,
285
+ ],
286
+ })
287
+ @TableColumn({
288
+ title: "Query",
289
+ required: true,
290
+ type: TableColumnType.JSON,
291
+ canReadOnRelationQuery: true,
292
+ description:
293
+ "Serialized traces explorer view state (search, filters, time range, page size) for this saved view.",
294
+ })
295
+ @Column({
296
+ type: ColumnType.JSON,
297
+ nullable: false,
298
+ })
299
+ public query?: TelemetrySavedViewState = undefined;
300
+
301
+ @ColumnAccessControl({
302
+ create: [
303
+ Permission.ProjectOwner,
304
+ Permission.ProjectAdmin,
305
+ Permission.ProjectMember,
306
+ ],
307
+ read: [
308
+ Permission.ProjectOwner,
309
+ Permission.ProjectAdmin,
310
+ Permission.ProjectMember,
311
+ Permission.Viewer,
312
+ ],
313
+ update: [
314
+ Permission.ProjectOwner,
315
+ Permission.ProjectAdmin,
316
+ Permission.ProjectMember,
317
+ ],
318
+ })
319
+ @Index()
320
+ @TableColumn({
321
+ required: true,
322
+ type: TableColumnType.Boolean,
323
+ canReadOnRelationQuery: true,
324
+ title: "Is Default",
325
+ description: "Whether this saved trace view should be applied by default.",
326
+ defaultValue: false,
327
+ })
328
+ @Column({
329
+ nullable: false,
330
+ type: ColumnType.Boolean,
331
+ default: false,
332
+ })
333
+ public isDefault?: boolean = undefined;
334
+ }
@@ -0,0 +1,81 @@
1
+ import { MigrationInterface, QueryRunner } from "typeorm";
2
+
3
+ export class AddMetricAndTraceSavedView1780645560183
4
+ implements MigrationInterface
5
+ {
6
+ public name = "AddMetricAndTraceSavedView1780645560183";
7
+
8
+ public async up(queryRunner: QueryRunner): Promise<void> {
9
+ await queryRunner.query(
10
+ `CREATE TABLE "MetricSavedView" ("_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(50) NOT NULL, "createdByUserId" uuid, "deletedByUserId" uuid, "query" jsonb NOT NULL, "isDefault" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_c039adf718ddfc387e2f9182cd6" PRIMARY KEY ("_id"))`,
11
+ );
12
+ await queryRunner.query(
13
+ `CREATE INDEX "IDX_1ca87e6c282970f623562035f2" ON "MetricSavedView" ("projectId") `,
14
+ );
15
+ await queryRunner.query(
16
+ `CREATE INDEX "IDX_c68db47974161dff86906b987d" ON "MetricSavedView" ("isDefault") `,
17
+ );
18
+ await queryRunner.query(
19
+ `CREATE TABLE "TraceSavedView" ("_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(50) NOT NULL, "createdByUserId" uuid, "deletedByUserId" uuid, "query" jsonb NOT NULL, "isDefault" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_087d7140b86556085f9aebe8ee5" PRIMARY KEY ("_id"))`,
20
+ );
21
+ await queryRunner.query(
22
+ `CREATE INDEX "IDX_a5ab6a338198ce0171d7e9e767" ON "TraceSavedView" ("projectId") `,
23
+ );
24
+ await queryRunner.query(
25
+ `CREATE INDEX "IDX_24f5967407b37b1601e8aa95cd" ON "TraceSavedView" ("isDefault") `,
26
+ );
27
+ await queryRunner.query(
28
+ `ALTER TABLE "MetricSavedView" ADD CONSTRAINT "FK_1ca87e6c282970f623562035f2b" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
29
+ );
30
+ await queryRunner.query(
31
+ `ALTER TABLE "MetricSavedView" ADD CONSTRAINT "FK_5d6c800c0070ca19e75e664b46b" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
32
+ );
33
+ await queryRunner.query(
34
+ `ALTER TABLE "MetricSavedView" ADD CONSTRAINT "FK_84e306d0d03a2a4721fe767464d" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
35
+ );
36
+ await queryRunner.query(
37
+ `ALTER TABLE "TraceSavedView" ADD CONSTRAINT "FK_a5ab6a338198ce0171d7e9e767b" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
38
+ );
39
+ await queryRunner.query(
40
+ `ALTER TABLE "TraceSavedView" ADD CONSTRAINT "FK_e22965c32f7cb0e154d2fa76d24" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
41
+ );
42
+ await queryRunner.query(
43
+ `ALTER TABLE "TraceSavedView" ADD CONSTRAINT "FK_e98727a618620d359fc766c95fc" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
44
+ );
45
+ }
46
+
47
+ public async down(queryRunner: QueryRunner): Promise<void> {
48
+ await queryRunner.query(
49
+ `ALTER TABLE "TraceSavedView" DROP CONSTRAINT "FK_e98727a618620d359fc766c95fc"`,
50
+ );
51
+ await queryRunner.query(
52
+ `ALTER TABLE "TraceSavedView" DROP CONSTRAINT "FK_e22965c32f7cb0e154d2fa76d24"`,
53
+ );
54
+ await queryRunner.query(
55
+ `ALTER TABLE "TraceSavedView" DROP CONSTRAINT "FK_a5ab6a338198ce0171d7e9e767b"`,
56
+ );
57
+ await queryRunner.query(
58
+ `ALTER TABLE "MetricSavedView" DROP CONSTRAINT "FK_84e306d0d03a2a4721fe767464d"`,
59
+ );
60
+ await queryRunner.query(
61
+ `ALTER TABLE "MetricSavedView" DROP CONSTRAINT "FK_5d6c800c0070ca19e75e664b46b"`,
62
+ );
63
+ await queryRunner.query(
64
+ `ALTER TABLE "MetricSavedView" DROP CONSTRAINT "FK_1ca87e6c282970f623562035f2b"`,
65
+ );
66
+ await queryRunner.query(
67
+ `DROP INDEX "public"."IDX_24f5967407b37b1601e8aa95cd"`,
68
+ );
69
+ await queryRunner.query(
70
+ `DROP INDEX "public"."IDX_a5ab6a338198ce0171d7e9e767"`,
71
+ );
72
+ await queryRunner.query(`DROP TABLE "TraceSavedView"`);
73
+ await queryRunner.query(
74
+ `DROP INDEX "public"."IDX_c68db47974161dff86906b987d"`,
75
+ );
76
+ await queryRunner.query(
77
+ `DROP INDEX "public"."IDX_1ca87e6c282970f623562035f2"`,
78
+ );
79
+ await queryRunner.query(`DROP TABLE "MetricSavedView"`);
80
+ }
81
+ }
@@ -365,6 +365,7 @@ import { MigrationName1780381124553 } from "./1780381124553-MigrationName";
365
365
  import { MigrationName1780382837019 } from "./1780382837019-MigrationName";
366
366
  import { MigrationName1780387560604 } from "./1780387560604-MigrationName";
367
367
  import { MigrationName1780388219225 } from "./1780388219225-MigrationName";
368
+ import { AddMetricAndTraceSavedView1780645560183 } from "./1780645560183-AddMetricAndTraceSavedView";
368
369
 
369
370
  export default [
370
371
  InitialMigration,
@@ -734,4 +735,5 @@ export default [
734
735
  MigrationName1780382837019,
735
736
  MigrationName1780387560604,
736
737
  MigrationName1780388219225,
738
+ AddMetricAndTraceSavedView1780645560183,
737
739
  ];
@@ -0,0 +1,109 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/MetricSavedView";
3
+ import CreateBy from "../Types/Database/CreateBy";
4
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
5
+ import UpdateBy from "../Types/Database/UpdateBy";
6
+ import ObjectID from "../../Types/ObjectID";
7
+ import QueryHelper from "../Types/Database/QueryHelper";
8
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
9
+
10
+ export class Service extends DatabaseService<Model> {
11
+ public constructor() {
12
+ super(Model);
13
+ }
14
+
15
+ protected override async onBeforeCreate(
16
+ createBy: CreateBy<Model>,
17
+ ): Promise<OnCreate<Model>> {
18
+ if (createBy.data.isDefault === undefined && createBy.data.projectId) {
19
+ const existingDefaultView: Model | null = await this.findOneBy({
20
+ query: {
21
+ projectId: createBy.data.projectId,
22
+ isDefault: true,
23
+ },
24
+ select: {
25
+ _id: true,
26
+ },
27
+ props: {
28
+ isRoot: true,
29
+ },
30
+ });
31
+
32
+ createBy.data.isDefault = !existingDefaultView;
33
+ }
34
+
35
+ if (createBy.data.projectId) {
36
+ await this.unsetOtherDefaultsIfNeeded({
37
+ projectId: createBy.data.projectId,
38
+ isDefault: createBy.data.isDefault || false,
39
+ });
40
+ }
41
+
42
+ return { createBy, carryForward: null };
43
+ }
44
+
45
+ protected override async onBeforeUpdate(
46
+ updateBy: UpdateBy<Model>,
47
+ ): Promise<OnUpdate<Model>> {
48
+ if (updateBy.data.isDefault !== true) {
49
+ return { updateBy, carryForward: null };
50
+ }
51
+
52
+ const itemsToUpdate: Array<Model> = await this.findBy({
53
+ query: updateBy.query,
54
+ select: {
55
+ _id: true,
56
+ projectId: true,
57
+ },
58
+ props: {
59
+ isRoot: true,
60
+ },
61
+ limit: LIMIT_MAX,
62
+ skip: 0,
63
+ });
64
+
65
+ for (const item of itemsToUpdate) {
66
+ if (item.projectId) {
67
+ await this.unsetOtherDefaultsIfNeeded({
68
+ projectId: item.projectId,
69
+ isDefault: true,
70
+ excludeIds: item._id ? [item._id] : [],
71
+ });
72
+ }
73
+ }
74
+
75
+ return { updateBy, carryForward: null };
76
+ }
77
+
78
+ private async unsetOtherDefaultsIfNeeded(data: {
79
+ projectId?: ObjectID;
80
+ isDefault?: boolean;
81
+ excludeIds?: Array<string>;
82
+ }): Promise<void> {
83
+ if (!data.projectId || !data.isDefault) {
84
+ return;
85
+ }
86
+
87
+ await this.updateBy({
88
+ query: {
89
+ projectId: data.projectId,
90
+ isDefault: true,
91
+ ...(data.excludeIds && data.excludeIds.length > 0
92
+ ? {
93
+ _id: QueryHelper.notInOrNull(data.excludeIds),
94
+ }
95
+ : {}),
96
+ },
97
+ data: {
98
+ isDefault: false,
99
+ },
100
+ props: {
101
+ isRoot: true,
102
+ },
103
+ limit: LIMIT_MAX,
104
+ skip: 0,
105
+ });
106
+ }
107
+ }
108
+
109
+ export default new Service();
@@ -0,0 +1,109 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/TraceSavedView";
3
+ import CreateBy from "../Types/Database/CreateBy";
4
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
5
+ import UpdateBy from "../Types/Database/UpdateBy";
6
+ import ObjectID from "../../Types/ObjectID";
7
+ import QueryHelper from "../Types/Database/QueryHelper";
8
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
9
+
10
+ export class Service extends DatabaseService<Model> {
11
+ public constructor() {
12
+ super(Model);
13
+ }
14
+
15
+ protected override async onBeforeCreate(
16
+ createBy: CreateBy<Model>,
17
+ ): Promise<OnCreate<Model>> {
18
+ if (createBy.data.isDefault === undefined && createBy.data.projectId) {
19
+ const existingDefaultView: Model | null = await this.findOneBy({
20
+ query: {
21
+ projectId: createBy.data.projectId,
22
+ isDefault: true,
23
+ },
24
+ select: {
25
+ _id: true,
26
+ },
27
+ props: {
28
+ isRoot: true,
29
+ },
30
+ });
31
+
32
+ createBy.data.isDefault = !existingDefaultView;
33
+ }
34
+
35
+ if (createBy.data.projectId) {
36
+ await this.unsetOtherDefaultsIfNeeded({
37
+ projectId: createBy.data.projectId,
38
+ isDefault: createBy.data.isDefault || false,
39
+ });
40
+ }
41
+
42
+ return { createBy, carryForward: null };
43
+ }
44
+
45
+ protected override async onBeforeUpdate(
46
+ updateBy: UpdateBy<Model>,
47
+ ): Promise<OnUpdate<Model>> {
48
+ if (updateBy.data.isDefault !== true) {
49
+ return { updateBy, carryForward: null };
50
+ }
51
+
52
+ const itemsToUpdate: Array<Model> = await this.findBy({
53
+ query: updateBy.query,
54
+ select: {
55
+ _id: true,
56
+ projectId: true,
57
+ },
58
+ props: {
59
+ isRoot: true,
60
+ },
61
+ limit: LIMIT_MAX,
62
+ skip: 0,
63
+ });
64
+
65
+ for (const item of itemsToUpdate) {
66
+ if (item.projectId) {
67
+ await this.unsetOtherDefaultsIfNeeded({
68
+ projectId: item.projectId,
69
+ isDefault: true,
70
+ excludeIds: item._id ? [item._id] : [],
71
+ });
72
+ }
73
+ }
74
+
75
+ return { updateBy, carryForward: null };
76
+ }
77
+
78
+ private async unsetOtherDefaultsIfNeeded(data: {
79
+ projectId?: ObjectID;
80
+ isDefault?: boolean;
81
+ excludeIds?: Array<string>;
82
+ }): Promise<void> {
83
+ if (!data.projectId || !data.isDefault) {
84
+ return;
85
+ }
86
+
87
+ await this.updateBy({
88
+ query: {
89
+ projectId: data.projectId,
90
+ isDefault: true,
91
+ ...(data.excludeIds && data.excludeIds.length > 0
92
+ ? {
93
+ _id: QueryHelper.notInOrNull(data.excludeIds),
94
+ }
95
+ : {}),
96
+ },
97
+ data: {
98
+ isDefault: false,
99
+ },
100
+ props: {
101
+ isRoot: true,
102
+ },
103
+ limit: LIMIT_MAX,
104
+ skip: 0,
105
+ });
106
+ }
107
+ }
108
+
109
+ export default new Service();
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Serializable view state for the Metrics and Traces explorers. Stored in the
3
+ * `query` JSONB column of MetricSavedView / TraceSavedView. This is the same
4
+ * state each viewer already mirrors to the URL (search string, facet filter
5
+ * tuples, time range, page size), so capture/apply reuses logic that is proven
6
+ * to round-trip.
7
+ */
8
+
9
+ export interface TelemetrySavedViewTimeRange {
10
+ // A TimeRange enum value (e.g. "Past one hour", "Custom").
11
+ range: string;
12
+ // ISO strings — only present when range is "Custom".
13
+ startValue?: string | undefined;
14
+ endValue?: string | undefined;
15
+ }
16
+
17
+ export default interface TelemetrySavedViewState {
18
+ // Submitted search string from the explorer search bar.
19
+ search?: string | undefined;
20
+ // Active facet filters as [facetKey, value] tuples.
21
+ filters?: Array<[string, string]> | undefined;
22
+ // Selected time range.
23
+ timeRange?: TelemetrySavedViewTimeRange | undefined;
24
+ // Rows per page.
25
+ pageSize?: number | undefined;
26
+ }
@@ -58,12 +58,16 @@ const TableHeader: TableHeaderFunction = <T extends GenericObject>(
58
58
  <tr>
59
59
  {props.enableDragAndDrop && (
60
60
  <th scope="col">
61
- <span className="sr-only">Drag to reorder</span>
61
+ <span className="sr-only">
62
+ {translateString("Drag to reorder")}
63
+ </span>
62
64
  </th>
63
65
  )}
64
66
  {props.isBulkActionsEnabled && (
65
67
  <th scope="col">
66
- <span className="sr-only">Select all items</span>
68
+ <span className="sr-only">
69
+ {translateString("Select all items")}
70
+ </span>
67
71
  <div className="ml-5">
68
72
  <CheckboxElement
69
73
  disabled={!props.hasTableItems}