22141: Refactoring to reduce circular import dependencies
[arvados.git] / services / workbench2 / src / store / data-explorer / data-explorer-middleware-service.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { Dispatch, MiddlewareAPI } from 'redux';
6 import { RootState } from '../store';
7 import { DataExplorer, getSortColumn } from './data-explorer-reducer';
8 import { ListResults } from 'services/common-service/common-service';
9 import { createTree } from 'models/tree';
10 import { DataTableFilters } from 'components/data-table-filters/data-table-filters';
11 import { OrderBuilder, OrderDirection } from 'services/api/order-builder';
12 import { DataColumns, SortDirection } from 'components/data-table/data-column';
13 import { Resource } from 'models/resource';
14
15 export abstract class DataExplorerMiddlewareService {
16     protected readonly id: string;
17
18     protected constructor(id: string) {
19         this.id = id;
20     }
21
22     public getId() {
23         return this.id;
24     }
25
26     public getColumnFilters<T>(
27         columns: DataColumns<T, any>,
28         columnName: string
29     ): DataTableFilters {
30         return getDataExplorerColumnFilters(columns, columnName);
31     }
32
33     /**
34      * Consumers can use this method to request
35      * total count separately and in parallel
36      * @param api
37      * @param criteriaChanged
38      * @param background
39      */
40     abstract requestCount(
41         api: MiddlewareAPI<Dispatch, RootState>,
42         criteriaChanged?: boolean,
43         background?: boolean
44     ): Promise<void>;
45
46     abstract requestItems(
47         api: MiddlewareAPI<Dispatch, RootState>,
48         criteriaChanged?: boolean,
49         background?: boolean
50     ): Promise<void>;
51 }
52
53 export const getDataExplorerColumnFilters = <T>(
54     columns: DataColumns<T, any>,
55     columnName: string
56 ): DataTableFilters => {
57     const column = columns.find((c) => c.name === columnName);
58     return column ? column.filters : createTree();
59 };
60
61 export const dataExplorerToListParams = (dataExplorer: DataExplorer) => ({
62     limit: dataExplorer.rowsPerPage,
63     offset: dataExplorer.page * dataExplorer.rowsPerPage,
64 });
65
66 export const getOrder = <T extends Resource = Resource>(dataExplorer: DataExplorer) => {
67     const sortColumn = getSortColumn<T>(dataExplorer);
68     const order = new OrderBuilder<T>();
69     if (sortColumn && sortColumn.sort) {
70         const sortDirection = sortColumn.sort.direction === SortDirection.ASC
71             ? OrderDirection.ASC
72             : OrderDirection.DESC;
73
74         // Use createdAt as a secondary sort column so we break ties consistently.
75         return order
76             .addOrder(sortDirection, sortColumn.sort.field)
77             .addOrder(OrderDirection.DESC, "createdAt")
78             .getOrder();
79     } else {
80         return order.getOrder();
81     }
82 };
83
84 export type DataExplorerMeta = {
85     itemsAvailable?: number;
86     page: number;
87     rowsPerPage: number;
88 }
89
90 export const listResultsToDataExplorerItemsMeta = <R>({
91     itemsAvailable,
92     offset,
93     limit,
94 }: ListResults<R>): DataExplorerMeta => ({
95     itemsAvailable,
96     page: Math.floor(offset / limit),
97     rowsPerPage: limit,
98 });