@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.
Files changed (90) hide show
  1. package/Models/AnalyticsModels/Index.ts +0 -2
  2. package/Models/DatabaseModels/TelemetryException.ts +0 -7
  3. package/Server/API/MicrosoftTeamsAPI.ts +3 -4
  4. package/Server/API/UserOnCallLogTimelineAPI.ts +21 -17
  5. package/Server/DatabaseConfig.ts +7 -2
  6. package/Server/EnvironmentConfig.ts +20 -7
  7. package/Server/Infrastructure/GlobalCache.ts +12 -5
  8. package/Server/Services/AlertService.ts +0 -10
  9. package/Server/Services/AnalyticsDatabaseService.ts +5 -0
  10. package/Server/Services/IncidentService.ts +0 -10
  11. package/Server/Services/Index.ts +0 -2
  12. package/Server/Services/TeamMemberService.ts +11 -2
  13. package/Server/Services/TelemetryAttributeService.ts +261 -48
  14. package/Server/Services/WorkspaceProjectAuthTokenService.ts +33 -0
  15. package/Server/Utils/Monitor/MonitorResource.ts +0 -16
  16. package/Server/Utils/Telemetry/Telemetry.ts +0 -61
  17. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +149 -14
  18. package/Types/Permission.ts +0 -3
  19. package/UI/Components/Filters/FilterViewer.tsx +5 -1
  20. package/UI/Components/Filters/FiltersForm.tsx +8 -1
  21. package/UI/Components/List/List.tsx +4 -0
  22. package/UI/Components/LogsViewer/LogsViewer.tsx +102 -55
  23. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +1 -1
  24. package/UI/Components/ModelTable/BaseModelTable.tsx +6 -0
  25. package/UI/Components/Table/Table.tsx +4 -0
  26. package/UI/Config.ts +28 -19
  27. package/UI/Utils/API/ApiDocsAPI.ts +6 -1
  28. package/UI/Utils/API/DashboardAPI.ts +2 -1
  29. package/UI/Utils/API/IdentityAPI.ts +6 -1
  30. package/build/dist/Models/AnalyticsModels/Index.js +0 -2
  31. package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
  32. package/build/dist/Models/DatabaseModels/TelemetryException.js +0 -7
  33. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  34. package/build/dist/Server/API/MicrosoftTeamsAPI.js +4 -4
  35. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  36. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +6 -3
  37. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
  38. package/build/dist/Server/DatabaseConfig.js +3 -2
  39. package/build/dist/Server/DatabaseConfig.js.map +1 -1
  40. package/build/dist/Server/EnvironmentConfig.js +8 -7
  41. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  42. package/build/dist/Server/Infrastructure/GlobalCache.js +11 -9
  43. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  44. package/build/dist/Server/Services/AlertService.js +0 -9
  45. package/build/dist/Server/Services/AlertService.js.map +1 -1
  46. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  47. package/build/dist/Server/Services/IncidentService.js +0 -9
  48. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  49. package/build/dist/Server/Services/Index.js +0 -2
  50. package/build/dist/Server/Services/Index.js.map +1 -1
  51. package/build/dist/Server/Services/TeamMemberService.js +3 -2
  52. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  53. package/build/dist/Server/Services/TelemetryAttributeService.js +165 -46
  54. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  55. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +25 -0
  56. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  57. package/build/dist/Server/Utils/Monitor/MonitorResource.js +0 -15
  58. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  59. package/build/dist/Server/Utils/Telemetry/Telemetry.js +0 -41
  60. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  61. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +84 -15
  62. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  63. package/build/dist/Types/Permission.js +0 -2
  64. package/build/dist/Types/Permission.js.map +1 -1
  65. package/build/dist/UI/Components/Filters/FilterViewer.js +2 -2
  66. package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
  67. package/build/dist/UI/Components/Filters/FiltersForm.js +6 -1
  68. package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
  69. package/build/dist/UI/Components/List/List.js +1 -1
  70. package/build/dist/UI/Components/List/List.js.map +1 -1
  71. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +43 -16
  72. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  73. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +1 -1
  74. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  75. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
  76. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  77. package/build/dist/UI/Components/Table/Table.js +1 -1
  78. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  79. package/build/dist/UI/Config.js +20 -19
  80. package/build/dist/UI/Config.js.map +1 -1
  81. package/build/dist/UI/Utils/API/ApiDocsAPI.js +2 -1
  82. package/build/dist/UI/Utils/API/ApiDocsAPI.js.map +1 -1
  83. package/build/dist/UI/Utils/API/DashboardAPI.js +2 -1
  84. package/build/dist/UI/Utils/API/DashboardAPI.js.map +1 -1
  85. package/build/dist/UI/Utils/API/IdentityAPI.js +2 -1
  86. package/build/dist/UI/Utils/API/IdentityAPI.js.map +1 -1
  87. package/package.json +1 -1
  88. package/Models/AnalyticsModels/TelemetryAttribute.ts +0 -164
  89. package/build/dist/Models/AnalyticsModels/TelemetryAttribute.js +0 -154
  90. package/build/dist/Models/AnalyticsModels/TelemetryAttribute.js.map +0 -1
@@ -1,13 +1,66 @@
1
+ import { SQL, Statement } from "../Utils/AnalyticsDatabase/Statement";
1
2
  import TelemetryType from "../../Types/Telemetry/TelemetryType";
2
- import ClickhouseDatabase from "../Infrastructure/ClickhouseDatabase";
3
- import AnalyticsDatabaseService from "./AnalyticsDatabaseService";
4
- import TelemetryAttribute from "../../Models/AnalyticsModels/TelemetryAttribute";
3
+ import LogDatabaseService from "./LogService";
4
+ import MetricDatabaseService from "./MetricService";
5
+ import SpanDatabaseService from "./SpanService";
6
+ import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
7
+ import { JSONObject } from "../../Types/JSON";
5
8
  import ObjectID from "../../Types/ObjectID";
9
+ import OneUptimeDate from "../../Types/Date";
10
+ import GlobalCache from "../Infrastructure/GlobalCache";
6
11
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
12
+ import AnalyticsDatabaseService, {
13
+ DbJSONResponse,
14
+ Results,
15
+ } from "./AnalyticsDatabaseService";
7
16
 
8
- export class TelemetryAttributeService extends AnalyticsDatabaseService<TelemetryAttribute> {
9
- public constructor(clickhouseDatabase?: ClickhouseDatabase | undefined) {
10
- super({ modelType: TelemetryAttribute, database: clickhouseDatabase });
17
+ type TelemetrySource = {
18
+ service: AnalyticsDatabaseService<any>;
19
+ tableName: string;
20
+ attributesColumn: string;
21
+ timeColumn: string;
22
+ };
23
+
24
+ type TelemetryAttributesCacheEntry = {
25
+ attributes: Array<string>;
26
+ refreshedAt: Date;
27
+ };
28
+
29
+ export class TelemetryAttributeService {
30
+ private static readonly ATTRIBUTES_LIMIT: number = 5000;
31
+ private static readonly ROW_SCAN_LIMIT: number = 10000;
32
+ private static readonly CACHE_NAMESPACE: string = "telemetry-attributes";
33
+ private static readonly CACHE_STALE_AFTER_MINUTES: number = 5;
34
+ private static readonly LOOKBACK_WINDOW_IN_DAYS: number = 30;
35
+
36
+ private getTelemetrySource(
37
+ telemetryType: TelemetryType,
38
+ ): TelemetrySource | null {
39
+ switch (telemetryType) {
40
+ case TelemetryType.Log:
41
+ return {
42
+ service: LogDatabaseService,
43
+ tableName: LogDatabaseService.model.tableName,
44
+ attributesColumn: "attributes",
45
+ timeColumn: "time",
46
+ };
47
+ case TelemetryType.Metric:
48
+ return {
49
+ service: MetricDatabaseService,
50
+ tableName: MetricDatabaseService.model.tableName,
51
+ attributesColumn: "attributes",
52
+ timeColumn: "time",
53
+ };
54
+ case TelemetryType.Trace:
55
+ return {
56
+ service: SpanDatabaseService,
57
+ tableName: SpanDatabaseService.model.tableName,
58
+ attributesColumn: "attributes",
59
+ timeColumn: "startTime",
60
+ };
61
+ default:
62
+ return null;
63
+ }
11
64
  }
12
65
 
13
66
  @CaptureSpan()
@@ -15,57 +68,217 @@ export class TelemetryAttributeService extends AnalyticsDatabaseService<Telemetr
15
68
  projectId: ObjectID;
16
69
  telemetryType: TelemetryType;
17
70
  }): Promise<string[]> {
18
- const telemetryAttribute: TelemetryAttribute | null = await this.findOneBy({
19
- query: {
71
+ const source: TelemetrySource | null = this.getTelemetrySource(
72
+ data.telemetryType,
73
+ );
74
+
75
+ if (!source) {
76
+ return [];
77
+ }
78
+
79
+ const cacheKey: string = TelemetryAttributeService.getCacheKey(
80
+ data.projectId,
81
+ data.telemetryType,
82
+ );
83
+
84
+ const cachedEntry: TelemetryAttributesCacheEntry | null =
85
+ await TelemetryAttributeService.getCachedAttributes(cacheKey);
86
+
87
+ if (cachedEntry && TelemetryAttributeService.isCacheFresh(cachedEntry)) {
88
+ return cachedEntry.attributes;
89
+ }
90
+
91
+ let attributes: Array<string> = [];
92
+
93
+ try {
94
+ attributes = await TelemetryAttributeService.fetchAttributesFromDatabase({
20
95
  projectId: data.projectId,
21
- telemetryType: data.telemetryType,
22
- },
23
- select: {
24
- attributes: true,
25
- },
26
- props: {
27
- isRoot: true,
96
+ source,
97
+ });
98
+ } catch (error) {
99
+ if (cachedEntry) {
100
+ return cachedEntry.attributes;
101
+ }
102
+
103
+ throw error;
104
+ }
105
+
106
+ await TelemetryAttributeService.storeAttributesInCache(
107
+ cacheKey,
108
+ attributes,
109
+ );
110
+
111
+ if (attributes.length === 0 && cachedEntry) {
112
+ return cachedEntry.attributes;
113
+ }
114
+
115
+ return attributes;
116
+ }
117
+
118
+ private static getCacheKey(
119
+ projectId: ObjectID,
120
+ telemetryType: TelemetryType,
121
+ ): string {
122
+ return `${projectId.toString()}:${telemetryType}`;
123
+ }
124
+
125
+ private static getLookbackStartDate(): Date {
126
+ return OneUptimeDate.addRemoveDays(
127
+ OneUptimeDate.getCurrentDate(),
128
+ -TelemetryAttributeService.LOOKBACK_WINDOW_IN_DAYS,
129
+ );
130
+ }
131
+
132
+ private static async getCachedAttributes(
133
+ cacheKey: string,
134
+ ): Promise<TelemetryAttributesCacheEntry | null> {
135
+ let payload: JSONObject | null = null;
136
+
137
+ try {
138
+ payload = await GlobalCache.getJSONObject(
139
+ TelemetryAttributeService.CACHE_NAMESPACE,
140
+ cacheKey,
141
+ );
142
+ } catch {
143
+ return null;
144
+ }
145
+
146
+ if (!payload) {
147
+ return null;
148
+ }
149
+
150
+ const attributesValue: JSONObject["attributes"] = payload["attributes"];
151
+ const refreshedAtValue: JSONObject["refreshedAt"] = payload["refreshedAt"];
152
+
153
+ if (
154
+ !Array.isArray(attributesValue) ||
155
+ typeof refreshedAtValue !== "string"
156
+ ) {
157
+ return null;
158
+ }
159
+
160
+ const attributeCandidates: Array<unknown> =
161
+ attributesValue as Array<unknown>;
162
+
163
+ const attributes: Array<string> = attributeCandidates.filter(
164
+ (attribute: unknown): attribute is string => {
165
+ return typeof attribute === "string";
28
166
  },
29
- });
167
+ );
30
168
 
31
- return telemetryAttribute &&
32
- telemetryAttribute.attributes &&
33
- telemetryAttribute
34
- ? telemetryAttribute.attributes
35
- : [];
169
+ return {
170
+ attributes,
171
+ refreshedAt: OneUptimeDate.fromString(refreshedAtValue),
172
+ };
36
173
  }
37
174
 
38
- @CaptureSpan()
39
- public async refreshAttributes(data: {
175
+ private static isCacheFresh(
176
+ cacheEntry: TelemetryAttributesCacheEntry,
177
+ ): boolean {
178
+ const now: Date = OneUptimeDate.getCurrentDate();
179
+ const minutesSinceRefresh: number = Math.abs(
180
+ OneUptimeDate.getNumberOfMinutesBetweenDates(cacheEntry.refreshedAt, now),
181
+ );
182
+
183
+ return (
184
+ minutesSinceRefresh <= TelemetryAttributeService.CACHE_STALE_AFTER_MINUTES
185
+ );
186
+ }
187
+
188
+ private static async storeAttributesInCache(
189
+ cacheKey: string,
190
+ attributes: Array<string>,
191
+ ): Promise<void> {
192
+ const payload: JSONObject = {
193
+ attributes,
194
+ refreshedAt: OneUptimeDate.getCurrentDate().toISOString(),
195
+ };
196
+
197
+ try {
198
+ await GlobalCache.setJSON(
199
+ TelemetryAttributeService.CACHE_NAMESPACE,
200
+ cacheKey,
201
+ payload,
202
+ {
203
+ expiresInSeconds:
204
+ TelemetryAttributeService.CACHE_STALE_AFTER_MINUTES * 60,
205
+ },
206
+ );
207
+ } catch {
208
+ return;
209
+ }
210
+ }
211
+
212
+ private static buildAttributesStatement(data: {
40
213
  projectId: ObjectID;
41
- telemetryType: TelemetryType;
42
- attributes: string[];
43
- }): Promise<void> {
44
- const { projectId, telemetryType, attributes } = data;
45
-
46
- // delete existing attributes
47
- await this.deleteBy({
48
- query: {
49
- projectId,
50
- telemetryType,
51
- },
52
- props: {
53
- isRoot: true,
54
- },
55
- });
214
+ tableName: string;
215
+ attributesColumn: string;
216
+ timeColumn: string;
217
+ }): Statement {
218
+ const lookbackStartDate: Date =
219
+ TelemetryAttributeService.getLookbackStartDate();
56
220
 
57
- const telemetryAttribute: TelemetryAttribute = new TelemetryAttribute();
221
+ const statement: Statement = SQL`
222
+ WITH filtered AS (
223
+ SELECT ${data.attributesColumn} AS attrs
224
+ FROM ${data.tableName}
225
+ WHERE projectId = ${{
226
+ type: TableColumnType.ObjectID,
227
+ value: data.projectId,
228
+ }}
229
+ AND ${data.attributesColumn} IS NOT NULL
230
+ AND ${data.attributesColumn} != ''
231
+ AND ${data.timeColumn} >= ${{
232
+ type: TableColumnType.Date,
233
+ value: lookbackStartDate,
234
+ }}
235
+ ORDER BY ${data.timeColumn} DESC
236
+ LIMIT ${{
237
+ type: TableColumnType.Number,
238
+ value: TelemetryAttributeService.ROW_SCAN_LIMIT,
239
+ }}
240
+ )
241
+ SELECT DISTINCT arrayJoin(JSONExtractKeys(attrs)) AS attribute
242
+ FROM filtered
243
+ ORDER BY attribute ASC
244
+ LIMIT ${{
245
+ type: TableColumnType.Number,
246
+ value: TelemetryAttributeService.ATTRIBUTES_LIMIT,
247
+ }}
248
+ `;
58
249
 
59
- telemetryAttribute.projectId = projectId;
60
- telemetryAttribute.telemetryType = telemetryType;
61
- telemetryAttribute.attributes = attributes;
250
+ return statement;
251
+ }
62
252
 
63
- await this.create({
64
- data: telemetryAttribute,
65
- props: {
66
- isRoot: true,
67
- },
68
- });
253
+ private static async fetchAttributesFromDatabase(data: {
254
+ projectId: ObjectID;
255
+ source: TelemetrySource;
256
+ }): Promise<Array<string>> {
257
+ const statement: Statement =
258
+ TelemetryAttributeService.buildAttributesStatement({
259
+ projectId: data.projectId,
260
+ tableName: data.source.tableName,
261
+ attributesColumn: data.source.attributesColumn,
262
+ timeColumn: data.source.timeColumn,
263
+ });
264
+
265
+ const dbResult: Results = await data.source.service.executeQuery(statement);
266
+ const response: DbJSONResponse = await dbResult.json<{
267
+ data?: Array<JSONObject>;
268
+ }>();
269
+
270
+ const rows: Array<JSONObject> = response.data || [];
271
+
272
+ const attributeKeys: Array<string> = rows
273
+ .map((row: JSONObject) => {
274
+ const attribute: unknown = row["attribute"];
275
+ return typeof attribute === "string" ? attribute.trim() : null;
276
+ })
277
+ .filter((attribute: string | null): attribute is string => {
278
+ return Boolean(attribute);
279
+ });
280
+
281
+ return Array.from(new Set(attributeKeys));
69
282
  }
70
283
  }
71
284
 
@@ -5,6 +5,7 @@ import Model, {
5
5
  WorkspaceMiscData,
6
6
  } from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
7
7
  import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
8
+ import BadDataException from "../../Types/Exception/BadDataException";
8
9
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
9
10
 
10
11
  export class Service extends DatabaseService<Model> {
@@ -17,6 +18,14 @@ export class Service extends DatabaseService<Model> {
17
18
  projectId: ObjectID;
18
19
  workspaceType: WorkspaceType;
19
20
  }): Promise<Model | null> {
21
+ if (!data.projectId) {
22
+ throw new BadDataException("projectId is required");
23
+ }
24
+
25
+ if (!data.workspaceType) {
26
+ throw new BadDataException("workspaceType is required");
27
+ }
28
+
20
29
  return await this.findOneBy({
21
30
  query: {
22
31
  projectId: data.projectId,
@@ -38,6 +47,10 @@ export class Service extends DatabaseService<Model> {
38
47
  public async getProjectAuths(data: {
39
48
  projectId: ObjectID;
40
49
  }): Promise<Array<Model>> {
50
+ if (!data.projectId) {
51
+ throw new BadDataException("projectId is required");
52
+ }
53
+
41
54
  return await this.findBy({
42
55
  query: {
43
56
  projectId: data.projectId,
@@ -72,6 +85,26 @@ export class Service extends DatabaseService<Model> {
72
85
  workspaceProjectId: string;
73
86
  miscData: WorkspaceMiscData;
74
87
  }): Promise<void> {
88
+ if (!data.projectId) {
89
+ throw new BadDataException("projectId is required");
90
+ }
91
+
92
+ if (!data.workspaceType) {
93
+ throw new BadDataException("workspaceType is required");
94
+ }
95
+
96
+ if (!data.authToken) {
97
+ throw new BadDataException("authToken is required");
98
+ }
99
+
100
+ if (!data.workspaceProjectId) {
101
+ throw new BadDataException("workspaceProjectId is required");
102
+ }
103
+
104
+ if (!data.miscData) {
105
+ throw new BadDataException("miscData is required");
106
+ }
107
+
75
108
  let projectAuth: Model | null = await this.findOneBy({
76
109
  query: {
77
110
  projectId: data.projectId,
@@ -1059,22 +1059,6 @@ export default class MonitorResourceUtil {
1059
1059
  logger.error(err);
1060
1060
  });
1061
1061
 
1062
- // index attributes.
1063
- TelemetryUtil.indexAttributes({
1064
- attributes: [
1065
- "monitorId",
1066
- "projectId",
1067
- "probeId",
1068
- "browserType",
1069
- "screenSizeType",
1070
- "diskPath",
1071
- ],
1072
- projectId: data.projectId,
1073
- telemetryType: TelemetryType.Metric,
1074
- }).catch((err: Error) => {
1075
- logger.error(err);
1076
- });
1077
-
1078
1062
  // save monitor log.
1079
1063
  const monitorLog: MonitorLog = new MonitorLog();
1080
1064
  monitorLog.monitorId = data.monitorId;
@@ -1,10 +1,6 @@
1
1
  import { JSONArray, JSONObject, JSONValue } from "../../../Types/JSON";
2
2
  import ObjectID from "../../../Types/ObjectID";
3
- import TelemetryType from "../../../Types/Telemetry/TelemetryType";
4
- import GlobalCache from "../../Infrastructure/GlobalCache";
5
- import TelemetryAttributeService from "../../Services/TelemetryAttributeService";
6
3
  import CaptureSpan from "./CaptureSpan";
7
- import logger from "../Logger";
8
4
  import MetricType from "../../../Models/DatabaseModels/MetricType";
9
5
  import MetricTypeService from "../../Services/MetricTypeService";
10
6
  import TelemetryService from "../../../Models/DatabaseModels/TelemetryService";
@@ -127,63 +123,6 @@ export default class TelemetryUtil {
127
123
  }
128
124
  }
129
125
 
130
- @CaptureSpan()
131
- public static async indexAttributes(data: {
132
- attributes: Array<string>;
133
- projectId: ObjectID;
134
- telemetryType: TelemetryType;
135
- }): Promise<void> {
136
- // index attributes
137
-
138
- logger.debug("Indexing attributes");
139
- logger.debug("data: " + JSON.stringify(data, null, 2));
140
-
141
- const cacheKey: string =
142
- data.projectId.toString() + "_" + data.telemetryType;
143
-
144
- // get keys from cache
145
- const cacheKeys: string[] =
146
- (await GlobalCache.getStringArray("telemetryAttributesKeys", cacheKey)) ||
147
- [];
148
-
149
- let isKeysMissingInCache: boolean = false;
150
-
151
- // check if keys are missing in cache
152
-
153
- const attributeKeys: string[] = data.attributes;
154
-
155
- for (const key of attributeKeys) {
156
- if (!cacheKeys.includes(key)) {
157
- isKeysMissingInCache = true;
158
- break;
159
- }
160
- }
161
-
162
- // merge keys and remove duplicates
163
- if (isKeysMissingInCache) {
164
- const dbKeys: string[] = await TelemetryAttributeService.fetchAttributes({
165
- projectId: data.projectId,
166
- telemetryType: data.telemetryType,
167
- });
168
-
169
- const mergedKeys: Array<string> = Array.from(
170
- new Set([...dbKeys, ...attributeKeys, ...cacheKeys]),
171
- );
172
-
173
- await GlobalCache.setStringArray(
174
- "telemetryAttributesKeys",
175
- cacheKey,
176
- mergedKeys,
177
- );
178
-
179
- await TelemetryAttributeService.refreshAttributes({
180
- projectId: data.projectId,
181
- telemetryType: data.telemetryType,
182
- attributes: mergedKeys,
183
- });
184
- }
185
- }
186
-
187
126
  public static getAttributesForServiceIdAndServiceName(data: {
188
127
  serviceId: ObjectID;
189
128
  serviceName: string;