Passed
Push — main ( 329578...b9905c )
by Alejandro
03:46
created

src/visits/reducers/common.ts   A

Complexity

Total Complexity 5
Complexity/F 0

Size

Lines of Code 77
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 90%

Importance

Changes 0
Metric Value
wmc 5
eloc 65
mnd 5
bc 5
fnc 0
dl 0
loc 77
ccs 27
cts 30
cp 0.9
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import { flatten, prop, range, splitEvery } from 'ramda';
2
import { Action, Dispatch } from 'redux';
3
import { ShlinkPaginator, ShlinkVisits } from '../../utils/services/types';
4
import { Visit } from '../types';
5
6 2
const ITEMS_PER_PAGE = 5000;
7 2
const PARALLEL_REQUESTS_COUNT = 4;
8 2
const PARALLEL_STARTING_PAGE = 2;
9
10 6
const isLastPage = ({ currentPage, pagesCount }: ShlinkPaginator): boolean => currentPage >= pagesCount;
11 2
const calcProgress = (total: number, current: number): number => current * 100 / total;
12
13
type VisitsLoader = (page: number, itemsPerPage: number) => Promise<ShlinkVisits>;
14
interface ActionMap {
15
  start: string;
16
  large: string;
17
  finish: string;
18
  error: string;
19
  progress: string;
20
}
21
22 2
export const getVisitsWithLoader = async <T extends Action<string> & { visits: Visit[] }>(
23
  visitsLoader: VisitsLoader,
24
  extraFinishActionData: Partial<T>,
25
  actionMap: ActionMap,
26
  dispatch: Dispatch,
27
  shouldCancel: () => boolean,
28
) => {
29 8
  dispatch({ type: actionMap.start });
30
31 8
  const loadVisitsInParallel = async (pages: number[]): Promise<Visit[]> =>
32 2
    Promise.all(pages.map(async (page) => visitsLoader(page, ITEMS_PER_PAGE).then(prop('data')))).then(flatten);
33
34 8
  const loadPagesBlocks = async (pagesBlocks: number[][], index = 0): Promise<Visit[]> => {
35 2
    if (shouldCancel()) {
36
      return [];
37
    }
38
39 1
    const data = await loadVisitsInParallel(pagesBlocks[index]);
40
41 1
    dispatch({ type: actionMap.progress, progress: calcProgress(pagesBlocks.length, index + PARALLEL_STARTING_PAGE) });
42
43 2
    if (index < pagesBlocks.length - 1) {
44
      return data.concat(await loadPagesBlocks(pagesBlocks, index + 1));
45
    }
46
47 1
    return data;
48
  };
49
50 8
  const loadVisits = async (page = 1) => {
51 8
    const { pagination, data } = await visitsLoader(page, ITEMS_PER_PAGE);
52
53
    // If pagination was not returned, then this is an old shlink version. Just return data
54 6
    if (!pagination || isLastPage(pagination)) {
55 5
      return data;
56
    }
57
58
    // If there are more pages, make requests in blocks of 4
59 1
    const pagesRange = range(PARALLEL_STARTING_PAGE, pagination.pagesCount + 1);
60 1
    const pagesBlocks = splitEvery(PARALLEL_REQUESTS_COUNT, pagesRange);
61
62 2
    if (pagination.pagesCount - 1 > PARALLEL_REQUESTS_COUNT) {
63
      dispatch({ type: actionMap.large });
64
    }
65
66 1
    return data.concat(await loadPagesBlocks(pagesBlocks));
67
  };
68
69 8
  try {
70 8
    const visits = await loadVisits();
71
72 6
    dispatch({ ...extraFinishActionData, visits, type: actionMap.finish });
73
  } catch (e) {
74 2
    dispatch({ type: actionMap.error });
75
  }
76
};
77