13327: Fix table prefix
[arvados.git] / services / workbench2 / src / store / users / user-panel-middleware-service.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { ServiceRepository } from 'services/services';
6 import { MiddlewareAPI, Dispatch } from 'redux';
7 import { DataExplorerMiddlewareService, dataExplorerToListParams, listResultsToDataExplorerItemsMeta } from 'store/data-explorer/data-explorer-middleware-service';
8 import { RootState } from 'store/store';
9 import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
10 import { DataExplorer, getDataExplorer } from 'store/data-explorer/data-explorer-reducer';
11 import { updateResources } from 'store/resources/resources-actions';
12 import { FilterBuilder } from 'services/api/filter-builder';
13 import { SortDirection } from 'components/data-table/data-column';
14 import { OrderDirection, OrderBuilder } from 'services/api/order-builder';
15 import { ListArguments, ListResults } from 'services/common-service/common-service';
16 import { userBindedActions } from 'store/users/users-actions';
17 import { getSortColumn } from "store/data-explorer/data-explorer-reducer";
18 import { UserResource } from 'models/user';
19 import { UserPanelColumnNames } from 'views/user-panel/user-panel';
20 import { BuiltinGroups, getBuiltinGroupUuid } from 'models/group';
21 import { LinkClass } from 'models/link';
22 import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
23 import { couldNotFetchItemsAvailable } from 'store/data-explorer/data-explorer-action';
24
25 export class UserMiddlewareService extends DataExplorerMiddlewareService {
26     constructor(private services: ServiceRepository, id: string) {
27         super(id);
28     }
29
30     async requestItems(api: MiddlewareAPI<Dispatch, RootState>, criteriaChanged?: boolean, background?: boolean) {
31         const state = api.getState();
32         const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
33         try {
34             if (!background) { api.dispatch(progressIndicatorActions.START_WORKING(this.getId())); }
35             const users = await this.services.userService.list(getParams(dataExplorer));
36             api.dispatch(updateResources(users.items));
37             api.dispatch(setItems(users));
38
39             // Get "all users" group memberships for account status
40             const allUsersGroupUuid = getBuiltinGroupUuid(state.auth.localCluster, BuiltinGroups.ALL);
41             const allUserMemberships = await this.services.permissionService.list({
42                 filters: new FilterBuilder()
43                     .addEqual('head_uuid', allUsersGroupUuid)
44                     .addEqual('link_class', LinkClass.PERMISSION)
45                     .getFilters()
46             });
47             api.dispatch(updateResources(allUserMemberships.items));
48         } catch {
49             api.dispatch(couldNotFetchUsers());
50         } finally {
51             api.dispatch(progressIndicatorActions.STOP_WORKING(this.getId()));
52         }
53     }
54
55     async requestCount(api: MiddlewareAPI<Dispatch, RootState>, criteriaChanged?: boolean, background?: boolean) {
56         const state = api.getState();
57         const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
58
59         if (criteriaChanged) {
60             // Get itemsAvailable
61             return this.services.userService.list(getCountParams(dataExplorer))
62                 .then((results: ListResults<UserResource>) => {
63                     if (results.itemsAvailable !== undefined) {
64                         api.dispatch<any>(userBindedActions.SET_ITEMS_AVAILABLE(results.itemsAvailable));
65                     } else {
66                         couldNotFetchItemsAvailable();
67                     }
68                 });
69         }
70     }
71 }
72
73 const getFilters = (dataExplorer: DataExplorer) => (
74     new FilterBuilder()
75         .addFullTextSearch(dataExplorer.searchValue)
76         .getFilters()
77 );
78
79 const getParams = (dataExplorer: DataExplorer): ListArguments => ({
80     ...dataExplorerToListParams(dataExplorer),
81     order: getOrder(dataExplorer),
82     filters: getFilters(dataExplorer),
83     count: 'none',
84 });
85
86 const getCountParams = (dataExplorer: DataExplorer): ListArguments => ({
87     limit: 0,
88     count: 'exact',
89     filters: getFilters(dataExplorer),
90 });
91
92 const getOrder = (dataExplorer: DataExplorer) => {
93     const sortColumn = getSortColumn<UserResource>(dataExplorer);
94     const order = new OrderBuilder<UserResource>();
95     if (sortColumn && sortColumn.sort) {
96         const sortDirection = sortColumn.sort.direction === SortDirection.ASC
97             ? OrderDirection.ASC
98             : OrderDirection.DESC;
99
100         if (sortColumn.name === UserPanelColumnNames.NAME) {
101             order.addOrder(sortDirection, "firstName")
102                 .addOrder(sortDirection, "lastName");
103         } else {
104             order.addOrder(sortDirection, sortColumn.sort.field);
105         }
106
107         // Use createdAt as a secondary sort column so we break ties consistently.
108         order.addOrder(OrderDirection.DESC, "createdAt");
109     }
110     return order.getOrder();
111 };
112
113 export const setItems = (listResults: ListResults<UserResource>) =>
114     userBindedActions.SET_ITEMS({
115         ...listResultsToDataExplorerItemsMeta(listResults),
116         items: listResults.items.map(resource => resource.uuid),
117     });
118
119 const couldNotFetchUsers = () =>
120     snackbarActions.OPEN_SNACKBAR({
121         message: 'Could not fetch users.',
122         kind: SnackbarKind.ERROR
123     });