21702: Merge branch 'main' into 21702-keep-web-replace_files
[arvados.git] / services / workbench2 / src / store / data-explorer / data-explorer-middleware.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { Dispatch } from 'redux';
6 import { RootState } from 'store/store';
7 import { ServiceRepository } from 'services/services';
8 import { Middleware } from 'redux';
9 import {
10     dataExplorerActions,
11     bindDataExplorerActions,
12     DataTableRequestState,
13     couldNotFetchItemsAvailable,
14 } from './data-explorer-action';
15 import { getDataExplorer } from './data-explorer-reducer';
16 import { DataExplorerMiddlewareService } from './data-explorer-middleware-service';
17
18 export const dataExplorerMiddleware =
19     (service: DataExplorerMiddlewareService): Middleware =>
20         (api) =>
21             (next) => {
22                 const actions = bindDataExplorerActions(service.getId());
23
24                 return (action) => {
25                     const handleAction =
26                         <T extends { id: string }>(handler: (data: T) => void) =>
27                             (data: T) => {
28                                 next(action);
29                                 if (data.id === service.getId()) {
30                                     handler(data);
31                                 }
32                             };
33                     dataExplorerActions.match(action, {
34                         SET_PAGE: handleAction(() => {
35                             api.dispatch(actions.REQUEST_ITEMS(false));
36                         }),
37                         SET_ROWS_PER_PAGE: handleAction(() => {
38                             api.dispatch(actions.REQUEST_ITEMS(true));
39                         }),
40                         SET_FILTERS: handleAction(() => {
41                             api.dispatch(actions.RESET_PAGINATION());
42                             api.dispatch(actions.SET_LOADING_ITEMS_AVAILABLE(true));
43                             api.dispatch(actions.REQUEST_ITEMS(true));
44                         }),
45                         TOGGLE_SORT: handleAction(() => {
46                             api.dispatch(actions.REQUEST_ITEMS(true));
47                         }),
48                         SET_EXPLORER_SEARCH_VALUE: handleAction(() => {
49                             api.dispatch(actions.RESET_PAGINATION());
50                             api.dispatch(actions.SET_LOADING_ITEMS_AVAILABLE(true));
51                             api.dispatch(actions.REQUEST_ITEMS(true));
52                         }),
53                         REQUEST_ITEMS: handleAction(({ criteriaChanged = true, background }) => {
54                             api.dispatch<any>(async (
55                                 dispatch: Dispatch,
56                                 getState: () => RootState,
57                                 services: ServiceRepository
58                             ) => {
59                                 while (true) {
60                                     let de = getDataExplorer(
61                                         getState().dataExplorer,
62                                         service.getId()
63                                     );
64                                     switch (de.requestState) {
65                                         case DataTableRequestState.IDLE:
66                                             // Start a new request.
67                                             try {
68                                                 dispatch(
69                                                     actions.SET_REQUEST_STATE({
70                                                         requestState: DataTableRequestState.PENDING,
71                                                     })
72                                                 );
73
74                                                 // Fetch results
75                                                 const result = service.requestItems(api, criteriaChanged, background);
76
77                                                 // If criteria changed, fire off a count request
78                                                 if (criteriaChanged) {
79                                                     dispatch(actions.REQUEST_COUNT(criteriaChanged, background));
80                                                 }
81
82                                                 // Await results
83                                                 await result;
84
85                                             } catch {
86                                                 dispatch(
87                                                     actions.SET_REQUEST_STATE({
88                                                         requestState: DataTableRequestState.NEED_REFRESH,
89                                                     })
90                                                 );
91                                             }
92                                             // Now check if the state is still PENDING, if it moved to NEED_REFRESH
93                                             // then we need to reissue requestItems
94                                             de = getDataExplorer(
95                                                 getState().dataExplorer,
96                                                 service.getId()
97                                             );
98                                             const complete =
99                                                 de.requestState === DataTableRequestState.PENDING;
100                                             dispatch(
101                                                 actions.SET_REQUEST_STATE({
102                                                     requestState: DataTableRequestState.IDLE,
103                                                 })
104                                             );
105                                             if (complete) {
106                                                 return;
107                                             }
108                                             break;
109                                         case DataTableRequestState.PENDING:
110                                             // State is PENDING, move it to NEED_REFRESH so that when the current request finishes it starts a new one.
111                                             dispatch(
112                                                 actions.SET_REQUEST_STATE({
113                                                     requestState: DataTableRequestState.NEED_REFRESH,
114                                                 })
115                                             );
116                                             return;
117                                         case DataTableRequestState.NEED_REFRESH:
118                                             // Nothing to do right now.
119                                             return;
120                                     }
121                                 }
122                             });
123                         }),
124                         REQUEST_COUNT: handleAction(({ criteriaChanged = true, background }) => {
125                             api.dispatch<any>(async (
126                                 dispatch: Dispatch,
127                                 getState: () => RootState,
128                                 services: ServiceRepository
129                             ) => {
130                                 while (true) {
131                                     let de = getDataExplorer(
132                                         getState().dataExplorer,
133                                         service.getId()
134                                     );
135                                     switch (de.countRequestState) {
136                                         case DataTableRequestState.IDLE:
137                                             // Start new count request
138                                             dispatch(
139                                                 actions.SET_COUNT_REQUEST_STATE({
140                                                     countRequestState: DataTableRequestState.PENDING,
141                                                 })
142                                             );
143
144                                             // Enable loading indicator on non-background fetches
145                                             if (!background) {
146                                                 api.dispatch<any>(
147                                                     dataExplorerActions.SET_LOADING_ITEMS_AVAILABLE({
148                                                         id: service.getId(),
149                                                         loadingItemsAvailable: true
150                                                     })
151                                                 );
152                                             }
153
154                                             // Fetch count
155                                             await service.requestCount(api, criteriaChanged, background)
156                                                 .catch(() => {
157                                                     // Show error toast if count fetch failed
158                                                     couldNotFetchItemsAvailable();
159                                                 })
160                                                 .finally(() => {
161                                                     // Turn off itemsAvailable loading indicator when done
162                                                     api.dispatch<any>(
163                                                         dataExplorerActions.SET_LOADING_ITEMS_AVAILABLE({
164                                                             id: service.getId(),
165                                                             loadingItemsAvailable: false
166                                                         })
167                                                     );
168                                                 });
169
170                                             // Now check if the state is still PENDING, if it moved to NEED_REFRESH
171                                             // then we need to reissue requestCount
172                                             de = getDataExplorer(
173                                                 getState().dataExplorer,
174                                                 service.getId()
175                                             );
176                                             const complete =
177                                                 de.countRequestState === DataTableRequestState.PENDING;
178                                             dispatch(
179                                                 actions.SET_COUNT_REQUEST_STATE({
180                                                     countRequestState: DataTableRequestState.IDLE,
181                                                 })
182                                             );
183                                             if (complete) {
184                                                 return;
185                                             }
186                                             break;
187                                         case DataTableRequestState.PENDING:
188                                             // State is PENDING, move it to NEED_REFRESH so that when the current request finishes it starts a new one.
189                                             dispatch(
190                                                 actions.SET_COUNT_REQUEST_STATE({
191                                                     countRequestState: DataTableRequestState.NEED_REFRESH,
192                                                 })
193                                             );
194                                             return;
195                                         case DataTableRequestState.NEED_REFRESH:
196                                             // Nothing to do right now.
197                                             return;
198                                     }
199                                 }
200                             });
201                         }),
202                         default: () => next(action),
203                     });
204                 };
205             };