Skip to content

Commit 7945666

Browse files
Refactor: Migrate Tier 3 Reflux stores to react-query and direct API calls (#25320)
* Replace InputStaticFieldsStore with direct @graylog/server-api calls Use StaticFields.create/remove from @graylog/server-api instead of the Reflux store. Remove cross-store dependency from InputsStore since react-query cache invalidation already handles input list refresh. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate InputStatesStore from Reflux to react-query hooks Replace all usages of the Reflux InputStatesStore with the existing useInputsStates hook and useInputStateMutations hook. Components now use SystemInputStates from @graylog/server-api directly where hooks cannot be used (StartInputStep). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate InputTypesStore from Reflux to react-query hooks Replace all usages of InputTypesStore with existing hooks: - useInputTypesDescriptions for input type descriptions - useInputTypes (hooks/) for input type summaries - fetchInputType for fetching a single input type definition Move InputDescription type to hooks/useInputType.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate PipelineConnectionsStore from Reflux to react-query hooks Create usePipelineConnections hook for fetching connections and usePipelineConnectionMutation for updating pipeline-to-stream connections. Uses PipelinesConnections from @graylog/server-api. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate StreamRulesStore from Reflux to react-query Replace StreamRulesStore with useStreamRuleMutations hook for create/update/remove operations. Update useStreamRuleTypes to use generated API client directly. Remove onChange/unregister callbacks in favor of query invalidation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate GrokPatternsStore from Reflux to standalone API functions Replace GrokPatternsStore with exported functions in hooks/useGrokPatterns.ts that call the generated SystemGrok API client. Update all three consumers (GrokPatterns, BulkLoadPatternModal, GrokExtractorConfiguration) and replace CancellablePromise patterns with unmounted flags. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate OutputsStore from Reflux to react-query hooks Create useOutputMutations hook for save/update/remove operations using SystemOutputs API client. Move Output type to hooks/useOutputs. Update OutputsComponent to use useOutputs/useStreamOutputs hooks instead of store callbacks. Update OutputsList and AddOutputButton to use mutations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate ContentPacksStore from Reflux to API functions Replace ContentPacksActions with direct calls to SystemContentPacks API client. Replace useStore(ContentPacksStore) in EditContentPackPage with useContentPackRevisions hook. Use queryClient.invalidateQueries for cache refresh after upload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate LookupTables/Caches/DataAdapters stores from Reflux to direct API calls Replace LookupTablesStore, LookupTableCachesStore, and LookupTableDataAdaptersStore with direct REST calls via lookupTablesAPI.ts. Consumers now use react-query or call API functions directly instead of going through Reflux stores. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate IndexSetsStore from Reflux to direct API functions and react-query hooks Replace the Reflux store with standalone API functions (fetchIndexSets, createIndexSet, updateIndexSet, deleteIndexSet, setDefaultIndexSet, etc.) and leverage existing useIndexSetsList/useSingleIndexSet react-query hooks. Update all 20 consumer files including tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove useFetchLookupTables wrapper, use named exports directly Replace the useFetchLookupTables hook with direct named exports of fetchPaginatedLookupTables and lookupTablesKeyFn from useLookupTablesAPI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix type error in lookup table list test mock Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fixing linter hints. * Fix stale imports from earlier Reflux store migrations - Update InputState type import to hooks/useInputsStates (InputStatesStore was removed in Tier 2) - Update DataAdapter test mock to use lookupDataAdapter API function (LookupTableDataAdaptersStore was removed in Tier 3) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Waiting for index sets to have loaded before showing modal. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a0e2a8a commit 7945666

File tree

94 files changed

+1062
-2833
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+1062
-2833
lines changed

graylog2-web-interface/src/components/content-packs/ContentPackUploadControls.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@
1515
* <http://www.mongodb.com/licensing/server-side-public-license>.
1616
*/
1717
import React, { useState, useRef } from 'react';
18+
import { useQueryClient } from '@tanstack/react-query';
1819

1920
import UserNotification from 'util/UserNotification';
2021
import { BootstrapModalForm, Input, Button } from 'components/bootstrap';
21-
import { ContentPacksActions } from 'stores/content-packs/ContentPacksStore';
22+
import { createContentPack } from 'hooks/useContentPackMutations';
2223
import useProductName from 'brand-customization/useProductName';
2324

2425
import style from './ContentPackUploadControls.css';
2526

2627
const ContentPackUploadControls = () => {
2728
const productName = useProductName();
29+
const queryClient = useQueryClient();
2830
const [isOpen, setIsOpen] = useState(false);
2931
const uploadInputRef = useRef(null);
3032

@@ -44,10 +46,10 @@ const ContentPackUploadControls = () => {
4446
reader.onload = (evt) => {
4547
const request = evt.target.result;
4648

47-
ContentPacksActions.create.triggerPromise(request as string).then(
49+
createContentPack(request as string).then(
4850
() => {
4951
UserNotification.success('Content pack imported successfully', 'Success!');
50-
ContentPacksActions.list();
52+
queryClient.invalidateQueries({ queryKey: ['content-packs'] });
5153
},
5254
(response) => {
5355
const message = `Error importing content pack, please ensure it is a valid JSON file. Check your ${productName} server logs for more information.`;

graylog2-web-interface/src/components/event-definitions/event-definition-types/FilterForm.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ import moment from 'moment';
2626
import { OrderedMap } from 'immutable';
2727
import type * as Immutable from 'immutable';
2828
import type { Permission } from 'graylog-web-plugin/plugin';
29+
import { useQuery } from '@tanstack/react-query';
2930

3031
import { describeExpression } from 'util/CronUtils';
3132
import { getPathnameWithoutId } from 'util/URLUtils';
3233
import { isPermitted } from 'util/PermissionsMixin';
3334
import * as FormsUtils from 'util/FormsUtils';
3435
import FormWarningsContext from 'contexts/FormWarningsContext';
35-
import { useStore } from 'stores/connect';
3636
import Store from 'logic/local-storage/Store';
3737
import { MultiSelect, TimeUnitInput, SearchFiltersFormControls, TimezoneSelect } from 'components/common';
3838
import Query from 'views/logic/queries/Query';
@@ -43,7 +43,7 @@ import { Alert, ButtonToolbar, ControlLabel, FormGroup, HelpBlock, Input } from
4343
import RelativeTime from 'components/common/RelativeTime';
4444
import type { LookupTableParameterJson } from 'views/logic/parameters/LookupTableParameter';
4545
import LookupTableParameter from 'views/logic/parameters/LookupTableParameter';
46-
import { LookupTablesActions, LookupTablesStore } from 'stores/lookup-tables/LookupTablesStore';
46+
import { fetchAllLookupTables } from 'components/lookup-tables/hooks/api/lookupTablesAPI';
4747
import validateQuery from 'views/components/searchbar/queryvalidation/validateQuery';
4848
import generateId from 'logic/generateId';
4949
import parseSearch from 'views/logic/slices/parseSearch';
@@ -139,7 +139,11 @@ type QueryParametersProps = {
139139
validation: Props['validation'];
140140
};
141141
const QueryParameters = ({ eventDefinition, onChange, userCanViewLookupTables, validation }: QueryParametersProps) => {
142-
const { tables = {} } = useStore(LookupTablesStore);
142+
const { data: tables = [] } = useQuery({
143+
queryKey: ['lookup-tables', 'all'],
144+
queryFn: () => fetchAllLookupTables(),
145+
enabled: userCanViewLookupTables,
146+
});
143147
const queryParameters = eventDefinition?.config?.query_parameters ?? [];
144148

145149
const onChangeQueryParameters = useCallback(
@@ -161,7 +165,7 @@ const QueryParameters = ({ eventDefinition, onChange, userCanViewLookupTables, v
161165
queryParameter={LookupTableParameter.fromJSON(queryParam)}
162166
embryonic={!!(queryParam as LookupTableParameterJsonEmbryonic).embryonic}
163167
queryParameters={queryParameters}
164-
lookupTables={Object.values(tables)}
168+
lookupTables={tables}
165169
onChange={onChangeQueryParameters}
166170
/>
167171
));
@@ -253,12 +257,6 @@ const FilterForm = ({ currentUser, eventDefinition, onChange, streams, validatio
253257
[setFieldWarning],
254258
);
255259

256-
useEffect(() => {
257-
if (userCanViewLookupTables) {
258-
LookupTablesActions.searchPaginated(1, 0, undefined, false);
259-
}
260-
}, [userCanViewLookupTables]);
261-
262260
useEffect(() => {
263261
validateQueryString(
264262
eventDefinition.config.query,

graylog2-web-interface/src/components/extractors/converters_configuration/LookupTableConverterConfiguration.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { Link, Select, Spinner } from 'components/common';
2020
import { Row, Col, Input } from 'components/bootstrap';
2121
import Routes from 'routing/Routes';
2222
import { getValueFromInput } from 'util/FormsUtils';
23-
import { LookupTablesActions } from 'stores/lookup-tables/LookupTablesStore';
23+
import { fetchAllLookupTables } from 'components/lookup-tables/hooks/api/lookupTablesAPI';
2424

2525
type Props = {
2626
type: string;
@@ -40,8 +40,8 @@ class LookupTableConverterConfiguration extends React.Component<Props, { lookupT
4040
this.props.onChange(this.props.type, this._getConverterObject());
4141

4242
// TODO the 10k items is bad. we need a searchable/scrollable long list select box
43-
LookupTablesActions.searchPaginated(1, 10000, null).then((result) => {
44-
this.setState({ lookupTables: result.lookup_tables });
43+
fetchAllLookupTables().then((result) => {
44+
this.setState({ lookupTables: result });
4545
});
4646
}
4747

graylog2-web-interface/src/components/extractors/extractors_configuration/GrokExtractorConfiguration.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ import GrokPatternInput from 'components/grok-patterns/GrokPatternInput';
2222
import UserNotification from 'util/UserNotification';
2323
import { getValueFromInput } from 'util/FormsUtils';
2424
import ToolsStore from 'stores/tools/ToolsStore';
25-
import { GrokPatternsStore } from 'stores/grok-patterns/GrokPatternsStore';
26-
import type CancellablePromise from 'logic/rest/CancellablePromise';
25+
import { loadGrokPatterns } from 'hooks/useGrokPatterns';
2726

2827
import Style from './GrokExtractorConfiguration.css';
2928

@@ -58,18 +57,14 @@ class GrokExtractorConfiguration extends React.Component<
5857
}
5958

6059
componentWillUnmount() {
61-
if (this.loadPromise) {
62-
this.loadPromise.cancel();
63-
}
60+
this._unmounted = true;
6461
}
6562

66-
private loadPromise: CancellablePromise<void>;
63+
private _unmounted = false;
6764

6865
loadData = () => {
69-
this.loadPromise = GrokPatternsStore.loadPatterns((patterns) => {
70-
if (!this.loadPromise.isCancelled()) {
71-
this.loadPromise = undefined;
72-
66+
loadGrokPatterns((patterns) => {
67+
if (!this._unmounted) {
7368
this.setState({
7469
patterns: patterns,
7570
});

graylog2-web-interface/src/components/extractors/extractors_configuration/LookupTableExtractorConfiguration.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { Row, Col, Button, Input } from 'components/bootstrap';
2121
import Routes from 'routing/Routes';
2222
import UserNotification from 'util/UserNotification';
2323
import ToolsStore from 'stores/tools/ToolsStore';
24-
import { LookupTablesActions } from 'stores/lookup-tables/LookupTablesStore';
24+
import { fetchAllLookupTables } from 'components/lookup-tables/hooks/api/lookupTablesAPI';
2525

2626
type Props = {
2727
configuration: any;
@@ -51,8 +51,8 @@ class LookupTableExtractorConfiguration extends React.Component<
5151

5252
componentDidMount() {
5353
// TODO the 10k items is bad. we need a searchable/scrollable long list select box
54-
LookupTablesActions.searchPaginated(1, 10000, null).then((result) => {
55-
this.setState({ lookupTables: result.lookup_tables });
54+
fetchAllLookupTables().then((result) => {
55+
this.setState({ lookupTables: result });
5656
});
5757
}
5858

graylog2-web-interface/src/components/grok-patterns/BulkLoadPatternModal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import React from 'react';
1919
import { Button, Input } from 'components/bootstrap';
2020
import UserNotification from 'util/UserNotification';
2121
import BootstrapModalForm from 'components/bootstrap/BootstrapModalForm';
22-
import { GrokPatternsStore } from 'stores/grok-patterns/GrokPatternsStore';
22+
import { bulkImportGrokPatterns } from 'hooks/useGrokPatterns';
2323
import withTelemetry from 'logic/telemetry/withTelemetry';
2424
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';
2525

@@ -67,7 +67,7 @@ class BulkLoadPatternModal extends React.Component<
6767
reader.onload = (loaded) => {
6868
const request = loaded.target.result;
6969

70-
GrokPatternsStore.bulkImport(request, importStrategy).then(() => {
70+
bulkImportGrokPatterns(request as string, importStrategy).then(() => {
7171
UserNotification.success('Grok Patterns imported successfully', 'Success!');
7272
this._closeModal();
7373

graylog2-web-interface/src/components/grok-patterns/GrokPatterns.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ import { Button, ButtonToolbar, Col, Row } from 'components/bootstrap';
2222
import EditPatternModal from 'components/grok-patterns/EditPatternModal';
2323
import BulkLoadPatternModal from 'components/grok-patterns/BulkLoadPatternModal';
2424
import withPaginationQueryParameter from 'components/common/withPaginationQueryParameter';
25-
import { GrokPatternsStore } from 'stores/grok-patterns/GrokPatternsStore';
25+
import {
26+
searchGrokPatternsPaginated,
27+
testGrokPattern,
28+
saveGrokPattern,
29+
deleteGrokPattern,
30+
} from 'hooks/useGrokPatterns';
2631
import withTelemetry from 'logic/telemetry/withTelemetry';
2732
import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants';
28-
import type CancellablePromise from 'logic/rest/CancellablePromise';
2933

3034
import GrokPatternQueryHelper from './GrokPatternQueryHelper';
3135

@@ -39,8 +43,8 @@ const GrokPatternsList = styled(DataTable)`
3943
}
4044
`;
4145

42-
const testPattern = (pattern, callback, errCallback) => {
43-
GrokPatternsStore.testPattern(pattern, callback, errCallback);
46+
const _testPattern = (pattern, callback, errCallback) => {
47+
testGrokPattern(pattern, callback, errCallback);
4448
};
4549

4650
const _headerCellFormatter = (header) => {
@@ -93,12 +97,10 @@ class GrokPatterns extends React.Component<
9397
}
9498

9599
componentWillUnmount() {
96-
if (this.loadPromise) {
97-
this.loadPromise.cancel();
98-
}
100+
this._unmounted = true;
99101
}
100102

101-
private loadPromise: CancellablePromise<unknown>;
103+
private _unmounted = false;
102104

103105
loadData = (
104106
callback?,
@@ -109,15 +111,13 @@ class GrokPatterns extends React.Component<
109111
pagination: { query },
110112
} = this.state;
111113

112-
this.loadPromise = GrokPatternsStore.searchPaginated(page, perPage, query).then(({ patterns, pagination }) => {
114+
searchGrokPatternsPaginated(page, perPage, query).then((result) => {
113115
if (callback) {
114116
callback();
115117
}
116118

117-
if (!this.loadPromise?.isCancelled()) {
118-
this.loadPromise = undefined;
119-
120-
this.setState({ patterns, pagination });
119+
if (!this._unmounted && result) {
120+
this.setState({ patterns: result.patterns, pagination: result.pagination });
121121
}
122122
});
123123
};
@@ -130,7 +130,7 @@ class GrokPatterns extends React.Component<
130130
};
131131

132132
savePattern = (pattern, callback) => {
133-
GrokPatternsStore.savePattern(pattern, () => {
133+
saveGrokPattern(pattern, () => {
134134
callback();
135135
this.loadData();
136136
});
@@ -161,7 +161,7 @@ class GrokPatterns extends React.Component<
161161
`Really delete the grok pattern ${pattern.name}?\nIt will be removed from the system and unavailable for any extractor. If it is still in use by extractors those will fail to work.`,
162162
)
163163
) {
164-
GrokPatternsStore.deletePattern(pattern, () => {
164+
deleteGrokPattern(pattern, () => {
165165
this.props.sendTelemetry(TELEMETRY_EVENT_TYPE.GROK_PATTERN.DELETED, {
166166
app_pathname: 'grokpatterns',
167167
app_section: 'grokpatterns',
@@ -187,7 +187,7 @@ class GrokPatterns extends React.Component<
187187
id={pattern.id}
188188
name={pattern.name}
189189
pattern={pattern.pattern}
190-
testPattern={testPattern}
190+
testPattern={_testPattern}
191191
patterns={patterns}
192192
create={false}
193193
reload={this.loadData}
@@ -228,7 +228,7 @@ class GrokPatterns extends React.Component<
228228
pattern=""
229229
patterns={patterns}
230230
create
231-
testPattern={testPattern}
231+
testPattern={_testPattern}
232232
reload={this.loadData}
233233
savePattern={this.savePattern}
234234
validPatternName={this.validPatternName}

graylog2-web-interface/src/components/indices/CreateIndexSet.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import AppConfig from 'util/AppConfig';
2020
import { Spinner } from 'components/common';
2121
import { IndexSetConfigurationForm } from 'components/indices';
2222
import Routes from 'routing/Routes';
23-
import { IndexSetsActions } from 'stores/indices/IndexSetsStore';
23+
import { createIndexSet } from 'stores/indices/IndexSetsStore';
2424
import type { IndexSet } from 'stores/indices/IndexSetsStore';
2525
import SelectIndexSetTemplateModal from 'components/indices/IndexSetTemplates/SelectIndexSetTemplateModal';
2626
import { adjustFormat } from 'util/DateTime';
@@ -53,7 +53,7 @@ const CreateIndexSet = ({ showSelectTemplateModal, setShowSelectTemplateModal }:
5353

5454
copy.creation_date = adjustFormat(new Date(), 'internal');
5555

56-
return IndexSetsActions.create(copy).then(() => {
56+
return createIndexSet(copy).then(() => {
5757
history.push(Routes.SYSTEM.INDICES.LIST);
5858
});
5959
};

graylog2-web-interface/src/components/indices/IndexSetFieldTypeProfiles/ProfilesList.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ import type { IndexSetFieldTypeProfile } from 'components/indices/IndexSetFieldT
2424
import { fetchIndexSetFieldTypeProfiles, keyFn } from 'components/indices/IndexSetFieldTypeProfiles/hooks/useProfiles';
2525
import useCustomColumnRenderers from 'components/indices/IndexSetFieldTypeProfiles/helpers/useCustomColumnRenderers';
2626
import profileActions from 'components/indices/IndexSetFieldTypeProfiles/helpers/profileActions';
27-
import { useStore } from 'stores/connect';
28-
import { IndexSetsStore } from 'stores/indices/IndexSetsStore';
27+
import useIndexSetsList from 'components/indices/hooks/useIndexSetsList';
2928
import ExpandedCustomFieldTypes from 'components/indices/IndexSetFieldTypeProfiles/ExpandedCustomFieldTypes';
3029

3130
export const DEFAULT_LAYOUT = {
@@ -46,7 +45,9 @@ const expandedSections = {
4645
};
4746

4847
const ProfilesList = () => {
49-
const { indexSets } = useStore(IndexSetsStore);
48+
const {
49+
data: { indexSets },
50+
} = useIndexSetsList(false);
5051
const normalizedIndexSetsTitles = useMemo(() => mapValues(keyBy(indexSets, 'id'), 'title'), [indexSets]);
5152
const customColumnRenderers = useCustomColumnRenderers(normalizedIndexSetsTitles);
5253

graylog2-web-interface/src/components/indices/IndexSetFieldTypes/IndexSetCustomFieldTypeRemoveModal.test.tsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import * as React from 'react';
1818
import { render, screen } from 'wrappedTestingLibrary';
1919
import userEvent from '@testing-library/user-event';
2020

21-
import { MockStore } from 'helpers/mocking';
2221
import asMock from 'helpers/mocking/AsMock';
2322
import TestStoreProvider from 'views/test/TestStoreProvider';
2423
import useViewsPlugin from 'views/test/testViewsPlugin';
@@ -35,17 +34,12 @@ const renderIndexSetCustomFieldTypeRemoveModal = () =>
3534
</TestStoreProvider>,
3635
);
3736

38-
jest.mock('stores/indices/IndexSetsStore', () => ({
39-
IndexSetsActions: {
40-
list: jest.fn(),
41-
},
42-
IndexSetsStore: MockStore([
43-
'getInitialState',
44-
() => ({
45-
indexSets: [{ id: '111', title: 'index set title' }],
46-
}),
47-
]),
48-
}));
37+
jest.mock('components/indices/hooks/useIndexSetsList', () => jest.fn(() => ({
38+
data: { indexSets: [{ id: '111', title: 'index set title' }], indexSetsCount: 1, indexSetStats: null },
39+
refetch: jest.fn(),
40+
isSuccess: true,
41+
isInitialLoading: false,
42+
})));
4943

5044
jest.mock('components/indices/IndexSetFieldTypes/hooks/useRemoveCustomFieldTypeMutation', () => jest.fn());
5145
jest.mock('components/common/EntityDataTable/hooks/useSelectedEntities');

0 commit comments

Comments
 (0)