@oneuptime/common 8.0.5387 → 8.0.5403

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 (81) hide show
  1. package/Models/AnalyticsModels/Index.ts +0 -2
  2. package/Models/DatabaseModels/TelemetryException.ts +0 -7
  3. package/Server/API/UserOnCallLogTimelineAPI.ts +21 -17
  4. package/Server/DatabaseConfig.ts +7 -2
  5. package/Server/EnvironmentConfig.ts +20 -7
  6. package/Server/Infrastructure/GlobalCache.ts +12 -5
  7. package/Server/Services/AlertService.ts +0 -10
  8. package/Server/Services/AnalyticsDatabaseService.ts +5 -0
  9. package/Server/Services/IncidentService.ts +0 -10
  10. package/Server/Services/Index.ts +0 -2
  11. package/Server/Services/TeamMemberService.ts +11 -2
  12. package/Server/Services/TelemetryAttributeService.ts +261 -48
  13. package/Server/Utils/Monitor/MonitorResource.ts +0 -16
  14. package/Server/Utils/Telemetry/Telemetry.ts +0 -61
  15. package/Types/Permission.ts +0 -3
  16. package/UI/Components/Filters/FilterViewer.tsx +5 -1
  17. package/UI/Components/Filters/FiltersForm.tsx +8 -1
  18. package/UI/Components/List/List.tsx +4 -0
  19. package/UI/Components/LogsViewer/LogsViewer.tsx +102 -55
  20. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +1 -1
  21. package/UI/Components/ModelTable/BaseModelTable.tsx +6 -0
  22. package/UI/Components/Table/Table.tsx +4 -0
  23. package/UI/Config.ts +28 -19
  24. package/UI/Utils/API/ApiDocsAPI.ts +6 -1
  25. package/UI/Utils/API/DashboardAPI.ts +2 -1
  26. package/UI/Utils/API/IdentityAPI.ts +6 -1
  27. package/build/dist/Models/AnalyticsModels/Index.js +0 -2
  28. package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
  29. package/build/dist/Models/DatabaseModels/TelemetryException.js +0 -7
  30. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  31. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +6 -3
  32. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
  33. package/build/dist/Server/DatabaseConfig.js +3 -2
  34. package/build/dist/Server/DatabaseConfig.js.map +1 -1
  35. package/build/dist/Server/EnvironmentConfig.js +8 -7
  36. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  37. package/build/dist/Server/Infrastructure/GlobalCache.js +11 -9
  38. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  39. package/build/dist/Server/Services/AlertService.js +0 -9
  40. package/build/dist/Server/Services/AlertService.js.map +1 -1
  41. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  42. package/build/dist/Server/Services/IncidentService.js +0 -9
  43. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  44. package/build/dist/Server/Services/Index.js +0 -2
  45. package/build/dist/Server/Services/Index.js.map +1 -1
  46. package/build/dist/Server/Services/TeamMemberService.js +3 -2
  47. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  48. package/build/dist/Server/Services/TelemetryAttributeService.js +165 -46
  49. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  50. package/build/dist/Server/Utils/Monitor/MonitorResource.js +0 -15
  51. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  52. package/build/dist/Server/Utils/Telemetry/Telemetry.js +0 -41
  53. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  54. package/build/dist/Types/Permission.js +0 -2
  55. package/build/dist/Types/Permission.js.map +1 -1
  56. package/build/dist/UI/Components/Filters/FilterViewer.js +2 -2
  57. package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
  58. package/build/dist/UI/Components/Filters/FiltersForm.js +6 -1
  59. package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
  60. package/build/dist/UI/Components/List/List.js +1 -1
  61. package/build/dist/UI/Components/List/List.js.map +1 -1
  62. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +43 -16
  63. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  64. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +1 -1
  65. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  66. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
  67. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  68. package/build/dist/UI/Components/Table/Table.js +1 -1
  69. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  70. package/build/dist/UI/Config.js +20 -19
  71. package/build/dist/UI/Config.js.map +1 -1
  72. package/build/dist/UI/Utils/API/ApiDocsAPI.js +2 -1
  73. package/build/dist/UI/Utils/API/ApiDocsAPI.js.map +1 -1
  74. package/build/dist/UI/Utils/API/DashboardAPI.js +2 -1
  75. package/build/dist/UI/Utils/API/DashboardAPI.js.map +1 -1
  76. package/build/dist/UI/Utils/API/IdentityAPI.js +2 -1
  77. package/build/dist/UI/Utils/API/IdentityAPI.js.map +1 -1
  78. package/package.json +1 -1
  79. package/Models/AnalyticsModels/TelemetryAttribute.ts +0 -164
  80. package/build/dist/Models/AnalyticsModels/TelemetryAttribute.js +0 -154
  81. 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
 
@@ -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;
@@ -80,9 +80,6 @@ enum Permission {
80
80
  EditTelemetryServiceMetrics = "EditTelemetryServiceMetrics",
81
81
  ReadTelemetryServiceMetrics = "ReadTelemetryServiceMetrics",
82
82
 
83
- // Telemetry Attributes
84
- DeleteTelemetryAttributes = "DeleteTelemetryAttributes",
85
-
86
83
  // Billing Permissions (Owner Permission)
87
84
  ManageProjectBilling = "ManageProjectBilling",
88
85
 
@@ -30,6 +30,9 @@ export interface ComponentProps<T extends GenericObject> {
30
30
  isModalLoading?: boolean;
31
31
  onFilterRefreshClick?: undefined | (() => void);
32
32
  filterData?: FilterData<T> | undefined;
33
+ onAdvancedFiltersToggle?:
34
+ | undefined
35
+ | ((showAdvancedFilters: boolean) => void);
33
36
  }
34
37
 
35
38
  type FilterComponentFunction = <T extends GenericObject>(
@@ -355,7 +358,7 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
355
358
  <div>
356
359
  {showViewer && (
357
360
  <div>
358
- <div className="mt-5 mb-5 bg-gray-50 rounded rounded-xl p-5 border border-2 border-gray-100">
361
+ <div className="mt-5 mb-5 bg-gray-50 rounded-xl p-5 border-2 border-gray-100">
359
362
  <div className="flex mt-1 mb-2">
360
363
  <div className="flex-auto py-0.5 text-sm leading-5">
361
364
  <span className="font-semibold">
@@ -442,6 +445,7 @@ const FilterComponent: FilterComponentFunction = <T extends GenericObject>(
442
445
  onFilterChanged={(filterData: FilterData<T>) => {
443
446
  setTempFilterDataForModal(filterData);
444
447
  }}
448
+ onAdvancedFiltersToggle={props.onAdvancedFiltersToggle}
445
449
  />
446
450
  </Modal>
447
451
  )}
@@ -24,6 +24,9 @@ export interface ComponentProps<T extends GenericObject> {
24
24
  isFilterLoading?: undefined | boolean;
25
25
  filterError?: string | undefined;
26
26
  onFilterRefreshClick?: undefined | (() => void);
27
+ onAdvancedFiltersToggle?:
28
+ | undefined
29
+ | ((showAdvancedFilters: boolean) => void);
27
30
  }
28
31
 
29
32
  type FiltersFormFunction = <T extends GenericObject>(
@@ -131,7 +134,11 @@ const FiltersForm: FiltersFormFunction = <T extends GenericObject>(
131
134
  : "Show Advanced Filters"
132
135
  }
133
136
  onClick={() => {
134
- setShowMoreFilters(!showMoreFilters);
137
+ setShowMoreFilters((currentValue: boolean) => {
138
+ const newValue: boolean = !currentValue;
139
+ props.onAdvancedFiltersToggle?.(newValue);
140
+ return newValue;
141
+ });
135
142
  }}
136
143
  />
137
144
  )}
@@ -43,6 +43,9 @@ export interface ComponentProps<T extends GenericObject> {
43
43
  onFilterRefreshClick?: undefined | (() => void);
44
44
  onFilterModalClose?: (() => void) | undefined;
45
45
  onFilterModalOpen?: (() => void) | undefined;
46
+ onAdvancedFiltersToggle?:
47
+ | undefined
48
+ | ((showAdvancedFilters: boolean) => void);
46
49
  }
47
50
 
48
51
  type ListFunction = <T extends GenericObject>(
@@ -118,6 +121,7 @@ const List: ListFunction = <T extends GenericObject>(
118
121
  }}
119
122
  singularLabel={props.singularLabel}
120
123
  pluralLabel={props.pluralLabel}
124
+ onAdvancedFiltersToggle={props.onAdvancedFiltersToggle}
121
125
  />
122
126
  </div>
123
127
  <div className="">