Skip to content

Commit b9498f1

Browse files
ousmaneolaura-b-ggally47
authored
Add pluggable entity table column (#23132)
* add pluggable entity actions hook * use pluggable entity actions * add pluggable entity table elements * add pluggableColumns to dashboard, search, events * fix linter * Update graylog2-web-interface/src/components/permissions/types.ts Co-authored-by: Laura Bergenthal-Grotlüschen <[email protected]> * Update feature-flag.config * add pluggable expanded sections to search --------- Co-authored-by: Laura Bergenthal-Grotlüschen <[email protected]> Co-authored-by: Mohamed OULD HOCINE <[email protected]>
1 parent 3ccb69a commit b9498f1

File tree

17 files changed

+327
-114
lines changed

17 files changed

+327
-114
lines changed

graylog2-server/src/main/resources/org/graylog2/featureflag/feature-flag.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,4 @@ show_security_events_in_pedt=off
101101
show_executive_dashboard_page=off
102102

103103
# Show collections
104-
collections=on
104+
collections=off

graylog2-web-interface/src/components/event-definitions/constants.ts

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,50 @@
1414
* along with this program. If not, see
1515
* <http://www.mongodb.com/licensing/server-side-public-license>.
1616
*/
17-
import type { Sort } from 'stores/PaginationTypes';
17+
import type { Attribute, Sort } from 'stores/PaginationTypes';
1818

1919
export const SYSTEM_EVENT_DEFINITION_TYPE = 'system-notifications-v1';
2020

21-
export const DEFAULT_LAYOUT = {
22-
entityTableId: 'event_definitions',
23-
defaultPageSize: 20,
24-
defaultSort: { attributeId: 'title', direction: 'asc' } as Sort,
25-
defaultDisplayedAttributes: ['title', 'description', 'priority', 'scheduling', 'status', 'matched_at'],
26-
};
27-
export const COLUMNS_ORDER = ['title', 'description', 'priority', 'matched_at', 'status', 'scheduling'];
21+
const getEventDefinitionTableElements = (
22+
pluggableAttributes?: {
23+
attributeNames?: Array<string>;
24+
attributes?: Array<Attribute>;
25+
}) => {
26+
const defaultLayout = {
27+
entityTableId: 'event_definitions',
28+
defaultPageSize: 20,
29+
defaultSort: { attributeId: 'title', direction: 'asc' } as Sort,
30+
defaultDisplayedAttributes: [
31+
'title',
32+
'description',
33+
'priority',
34+
'scheduling',
35+
'status',
36+
'matched_at',
37+
...(pluggableAttributes?.attributeNames || []),
38+
],
39+
};
40+
const columnOrder = [
41+
'title',
42+
'description',
43+
'priority',
44+
'matched_at',
45+
'status',
46+
'scheduling',
47+
...(pluggableAttributes?.attributeNames || []),
48+
];
2849

29-
export const ADDITIONAL_ATTRIBUTES = [
30-
{ id: 'scheduling', title: 'Scheduling', sortable: false },
31-
{ id: 'matched_at', title: 'Last Matched', sortable: true },
32-
];
50+
const additionalAttributes = [
51+
{ id: 'scheduling', title: 'Scheduling', sortable: false },
52+
{ id: 'matched_at', title: 'Last Matched', sortable: true },
53+
...(pluggableAttributes?.attributes || []),
54+
];
55+
56+
return {
57+
defaultLayout,
58+
columnOrder,
59+
additionalAttributes,
60+
};
61+
}
62+
63+
export default getEventDefinitionTableElements;

graylog2-web-interface/src/components/event-definitions/event-definitions/EventDefinitionsContainer.tsx

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,44 +15,48 @@
1515
* <http://www.mongodb.com/licensing/server-side-public-license>.
1616
*/
1717
import * as React from 'react';
18+
import { useMemo } from 'react';
1819

1920
import { QueryHelper, RelativeTime, PaginatedEntityTable } from 'components/common';
2021
import { Link } from 'components/common/router';
2122
import Routes from 'routing/Routes';
2223
import FilterValueRenderers from 'components/streams/StreamsOverview/FilterValueRenderers';
2324
import { keyFn, fetchEventDefinitions } from 'components/event-definitions/hooks/useEventDefinitions';
2425
import BulkActions from 'components/event-definitions/event-definitions/BulkActions';
26+
import usePluggableEntityTableElements from 'hooks/usePluggableEntityTableElements';
27+
import type { ColumnRenderersByAttribute } from 'components/common/EntityDataTable/types';
2528

2629
import EventDefinitionActions from './EventDefinitionActions';
2730
import SchedulingCell from './SchedulingCell';
2831
import StatusCell from './StatusCell';
2932

3033
import type { EventDefinition } from '../event-definitions-types';
31-
import { DEFAULT_LAYOUT, ADDITIONAL_ATTRIBUTES, COLUMNS_ORDER } from '../constants';
34+
import getEventDefinitionTableElements from '../constants';
3235

33-
const customColumnRenderers = {
36+
const getCustomColumnRenderers = (pluggableColumnRenderers?: ColumnRenderersByAttribute<EventDefinition>) => ({
3437
attributes: {
3538
title: {
36-
renderCell: (title: string, eventDefinition) => (
39+
renderCell: (title: string, eventDefinition: EventDefinition) => (
3740
<Link to={Routes.ALERTS.DEFINITIONS.show(eventDefinition.id)}>{title}</Link>
3841
),
3942
},
4043
matched_at: {
41-
renderCell: (_matched_at: string, eventDefinition) =>
44+
renderCell: (_matched_at: string, eventDefinition: EventDefinition) =>
4245
eventDefinition.matched_at ? <RelativeTime dateTime={eventDefinition.matched_at} /> : 'Never',
4346
},
4447
scheduling: {
45-
renderCell: (_scheduling: string, eventDefinition) => <SchedulingCell definition={eventDefinition} />,
48+
renderCell: (_scheduling: string, eventDefinition: EventDefinition) => <SchedulingCell definition={eventDefinition} />,
4649
},
4750
status: {
48-
renderCell: (_status: string, eventDefinition) => <StatusCell eventDefinition={eventDefinition} />,
51+
renderCell: (_status: string, eventDefinition: EventDefinition) => <StatusCell eventDefinition={eventDefinition} />,
4952
staticWidth: 100,
5053
},
5154
priority: {
5255
staticWidth: 100,
5356
},
57+
...(pluggableColumnRenderers || {}),
5458
},
55-
};
59+
});
5660

5761
const bulkSelection = {
5862
actions: <BulkActions />,
@@ -61,21 +65,37 @@ const renderEventDefinitionActions = (listItem: EventDefinition) => (
6165
<EventDefinitionActions eventDefinition={listItem} />
6266
);
6367

64-
const EventDefinitionsContainer = () => (
65-
<PaginatedEntityTable<EventDefinition>
66-
humanName="event definitions"
67-
columnsOrder={COLUMNS_ORDER}
68-
additionalAttributes={ADDITIONAL_ATTRIBUTES}
69-
queryHelpComponent={<QueryHelper entityName="event definition" />}
70-
tableLayout={DEFAULT_LAYOUT}
71-
fetchEntities={fetchEventDefinitions}
72-
entityActions={renderEventDefinitionActions}
73-
keyFn={keyFn}
74-
entityAttributesAreCamelCase={false}
75-
filterValueRenderers={FilterValueRenderers}
76-
columnRenderers={customColumnRenderers}
77-
bulkSelection={bulkSelection}
78-
/>
79-
);
68+
const EventDefinitionsContainer = () => {
69+
const {
70+
pluggableColumnRenderers,
71+
pluggableAttributes,
72+
pluggableExpandedSections
73+
} = usePluggableEntityTableElements<EventDefinition>(null, 'event_definition');
74+
const { defaultLayout, columnOrder, additionalAttributes } = getEventDefinitionTableElements(pluggableAttributes);
75+
const expandedSections = useMemo(
76+
() => ({
77+
...pluggableExpandedSections,
78+
}),
79+
[pluggableExpandedSections],
80+
);
81+
82+
return (
83+
<PaginatedEntityTable<EventDefinition>
84+
humanName="event definitions"
85+
columnsOrder={columnOrder}
86+
additionalAttributes={additionalAttributes}
87+
queryHelpComponent={<QueryHelper entityName="event definition" />}
88+
tableLayout={defaultLayout}
89+
fetchEntities={fetchEventDefinitions}
90+
entityActions={renderEventDefinitionActions}
91+
keyFn={keyFn}
92+
entityAttributesAreCamelCase={false}
93+
expandedSectionsRenderer={expandedSections}
94+
filterValueRenderers={FilterValueRenderers}
95+
columnRenderers={getCustomColumnRenderers(pluggableColumnRenderers)}
96+
bulkSelection={bulkSelection}
97+
/>
98+
);
99+
};
80100

81101
export default EventDefinitionsContainer;

graylog2-web-interface/src/components/event-notifications/event-notifications/Constants.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,31 @@
1414
* along with this program. If not, see
1515
* <http://www.mongodb.com/licensing/server-side-public-license>.
1616
*/
17-
import type { Sort } from 'stores/PaginationTypes';
17+
import type { Attribute, Sort } from 'stores/PaginationTypes';
1818

19-
export const DEFAULT_LAYOUT = {
20-
entityTableId: 'event_notifications',
21-
defaultPageSize: 20,
22-
defaultSort: { attributeId: 'title', direction: 'asc' } as Sort,
23-
defaultDisplayedAttributes: ['title', 'description', 'type', 'created_at'],
24-
};
19+
const getEventNotificationTableElements = (
20+
pluggableAttributes?: {
21+
attributeNames?: Array<string>;
22+
attributes?: Array<Attribute>;
23+
}) => {
24+
const DEFAULT_LAYOUT = {
25+
entityTableId: 'event_notifications',
26+
defaultPageSize: 20,
27+
defaultSort: { attributeId: 'title', direction: 'asc' } as Sort,
28+
defaultDisplayedAttributes: ['title', 'description', 'type', 'created_at', ...(pluggableAttributes?.attributeNames || [])],
29+
};
2530

26-
export const COLUMNS_ORDER = ['title', 'description', 'type', 'created_at'];
31+
const COLUMNS_ORDER = ['title', 'description', 'type', 'created_at', ...(pluggableAttributes?.attributeNames || [])];
32+
33+
const additionalAttributes = [
34+
...(pluggableAttributes?.attributes || []),
35+
];
36+
37+
return {
38+
defaultLayout: DEFAULT_LAYOUT,
39+
columnOrder: COLUMNS_ORDER,
40+
additionalAttributes,
41+
};
42+
}
43+
44+
export default getEventNotificationTableElements;

graylog2-web-interface/src/components/event-notifications/event-notifications/EventNotificationsContainer.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,23 @@ import { useQueryClient } from '@tanstack/react-query';
2121
import type { ColumnRenderers } from 'components/common/EntityDataTable';
2222
import { QueryHelper, PaginatedEntityTable } from 'components/common';
2323
import type { EventNotification, TestResults } from 'stores/event-notifications/EventNotificationsStore';
24-
import { DEFAULT_LAYOUT, COLUMNS_ORDER } from 'components/event-notifications/event-notifications/Constants';
2524
import useSendTelemetry from 'logic/telemetry/useSendTelemetry';
2625
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';
2726
import { getPathnameWithoutId } from 'util/URLUtils';
2827
import useLocation from 'routing/useLocation';
28+
import type { ColumnRenderersByAttribute } from 'components/common/EntityDataTable/types';
29+
import usePluggableEntityTableElements from 'hooks/usePluggableEntityTableElements';
2930

3031
import NotificationConfigTypeCell from './NotificationConfigTypeCell';
3132
import NotificationTitle from './NotificationTitle';
3233
import EventNotificationActions from './EventNotificationActions';
3334
import BulkActions from './BulkActions';
35+
import getEventNotificationTableElements from './Constants';
3436

3537
import { keyFn, fetchEventNotifications } from '../hooks/useEventNotifications';
3638
import useNotificationTest from '../hooks/useNotificationTest';
3739

38-
const customColumnRenderers = (testResults: TestResults): ColumnRenderers<EventNotification> => ({
40+
const customColumnRenderers = (testResults: TestResults, pluggableColumnRenderers?: ColumnRenderersByAttribute<EventNotification>): ColumnRenderers<EventNotification> => ({
3941
attributes: {
4042
title: {
4143
renderCell: (_title: string, notification) => (
@@ -45,14 +47,22 @@ const customColumnRenderers = (testResults: TestResults): ColumnRenderers<EventN
4547
type: {
4648
renderCell: (_type: string, notification) => <NotificationConfigTypeCell notification={notification} />,
4749
},
50+
...(pluggableColumnRenderers || {}),
4851
},
4952
});
5053

5154
const EventNotificationsContainer = () => {
5255
const { isLoadingTest, testResults, getNotificationTest } = useNotificationTest();
5356
const sendTelemetry = useSendTelemetry();
5457
const { pathname } = useLocation();
55-
const columnRenderers = useMemo(() => customColumnRenderers(testResults), [testResults]);
58+
const {
59+
pluggableColumnRenderers,
60+
pluggableAttributes,
61+
pluggableExpandedSections
62+
} = usePluggableEntityTableElements<EventNotification>(null, 'notification');
63+
64+
const { defaultLayout, columnOrder, additionalAttributes } = getEventNotificationTableElements(pluggableAttributes);
65+
const columnRenderers = useMemo(() => customColumnRenderers(testResults, pluggableColumnRenderers), [testResults, pluggableColumnRenderers]);
5666
const queryClient = useQueryClient();
5767

5868
const handleTest = useCallback(
@@ -68,6 +78,12 @@ const EventNotificationsContainer = () => {
6878
},
6979
[getNotificationTest, pathname, queryClient, sendTelemetry],
7080
);
81+
const expandedSections = useMemo(
82+
() => ({
83+
...pluggableExpandedSections,
84+
}),
85+
[pluggableExpandedSections],
86+
);
7187

7288
const renderEvenNotificationActions = useCallback(
7389
(listItem: EventNotification) => (
@@ -79,11 +95,13 @@ const EventNotificationsContainer = () => {
7995
return (
8096
<PaginatedEntityTable<EventNotification>
8197
humanName="event notifications"
82-
columnsOrder={COLUMNS_ORDER}
98+
columnsOrder={columnOrder}
8399
queryHelpComponent={<QueryHelper entityName="notification" />}
84100
entityActions={renderEvenNotificationActions}
85-
tableLayout={DEFAULT_LAYOUT}
101+
tableLayout={defaultLayout}
86102
fetchEntities={fetchEventNotifications}
103+
additionalAttributes={additionalAttributes}
104+
expandedSectionsRenderer={expandedSections}
87105
keyFn={keyFn}
88106
bulkSelection={{ actions: <BulkActions /> }}
89107
entityAttributesAreCamelCase

graylog2-web-interface/src/components/permissions/type.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

graylog2-web-interface/src/components/permissions/types.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717

1818
import type React from 'react';
1919

20+
import type { Attribute } from 'stores/PaginationTypes';
21+
import type { ColumnRenderer, EntityBase, ExpandedSectionRenderer } from 'components/common/EntityDataTable/types';
22+
import type { EntitySharePayload } from 'actions/permissions/EntityShareActions';
23+
2024
export type ModalHandler = {
2125
toggle?: () => void;
2226
onConfirm?: () => void;
@@ -42,8 +46,31 @@ export type EntitySharedAction<T, M> = {
4246
useCondition?: () => boolean;
4347
};
4448

49+
export type TableElement<T extends EntityBase> = {
50+
attributeName: string;
51+
attributes: Array<Attribute>;
52+
getColumnRenderer: (entityType: string) => ColumnRenderer<T, unknown>;
53+
expandedSection: (entityType: string) => {[sectionName: string]: ExpandedSectionRenderer<T>};
54+
tableCellComponent: React.ComponentType<{
55+
entityId: string;
56+
entityType: string;
57+
}>;
58+
useCondition: () => true;
59+
}
60+
4561
declare module 'graylog-web-plugin/plugin' {
4662
export interface PluginExports {
4763
'components.shared.entityActions'?: Array<EntitySharedAction<unknown, unknown>>;
64+
'components.shared.entityTableElements'?: Array<TableElement<EntityBase>>;
65+
'components.collection'?: {
66+
AddCollectionFormGroup: React.ComponentType<{
67+
entityType?: string;
68+
label?: React.ReactElement | string;
69+
name?: string;
70+
error?: any;
71+
value?: Array<string>;
72+
onChange: (values: Pick<EntitySharePayload, 'selected_collections'>) => void;
73+
}>;
74+
};
4875
}
4976
}

0 commit comments

Comments
 (0)