Updated getDomainVisits action so that it expects a signle DTO param

This commit is contained in:
Alejandro Celaya 2022-11-12 09:18:37 +01:00
parent 32f7374d92
commit 3b96b89492
20 changed files with 68 additions and 57 deletions

View file

@ -13,9 +13,9 @@ import { ShortUrlVisits } from '../visits/reducers/shortUrlVisits';
import { TagVisits } from '../visits/reducers/tagVisits';
import { DomainsList } from '../domains/reducers/domainsList';
import { VisitsOverview } from '../visits/reducers/visitsOverview';
import { VisitsInfo } from '../visits/types';
import { Sidebar } from '../common/reducers/sidebar';
import { DomainVisits } from '../visits/reducers/domainVisits';
import { VisitsInfo } from '../visits/reducers/types';
export interface ShlinkState {
servers: ServersMap;

View file

@ -1,7 +1,7 @@
import { useParams } from 'react-router-dom';
import { CommonVisitsProps } from './types/CommonVisitsProps';
import { ShlinkVisitsParams } from '../api/types';
import { DomainVisits as DomainVisitsState } from './reducers/domainVisits';
import { DomainVisits as DomainVisitsState, LoadDomainVisits } from './reducers/domainVisits';
import { ReportExporter } from '../common/services/ReportExporter';
import { boundToMercureHub } from '../mercure/helpers/boundToMercureHub';
import { Topics } from '../mercure/helpers/Topics';
@ -12,7 +12,7 @@ import { VisitsStats } from './VisitsStats';
import { VisitsHeader } from './VisitsHeader';
export interface DomainVisitsProps extends CommonVisitsProps {
getDomainVisits: (domain: string, query?: ShlinkVisitsParams, doIntervalFallback?: boolean) => void;
getDomainVisits: (params: LoadDomainVisits) => void;
domainVisits: DomainVisitsState;
cancelGetDomainVisits: () => void;
}
@ -28,7 +28,7 @@ export const DomainVisits = ({ exportVisits }: ReportExporter) => boundToMercure
const { domain = '' } = useParams();
const [authority, domainId = authority] = domain.split('_');
const loadVisits = (params: ShlinkVisitsParams, doIntervalFallback?: boolean) =>
getDomainVisits(domainId, toApiParams(params), doIntervalFallback);
getDomainVisits({ domain: domainId, query: toApiParams(params), doIntervalFallback });
const exportCsv = (visits: NormalizedVisit[]) => exportVisits(`domain_${authority}_visits.csv`, visits);
return (

View file

@ -4,10 +4,11 @@ import { Topics } from '../mercure/helpers/Topics';
import { useGoBack } from '../utils/helpers/hooks';
import { ReportExporter } from '../common/services/ReportExporter';
import { VisitsStats } from './VisitsStats';
import { NormalizedVisit, VisitsInfo, VisitsParams } from './types';
import { NormalizedVisit, VisitsParams } from './types';
import { CommonVisitsProps } from './types/CommonVisitsProps';
import { toApiParams } from './types/helpers';
import { VisitsHeader } from './VisitsHeader';
import { VisitsInfo } from './reducers/types';
export interface NonOrphanVisitsProps extends CommonVisitsProps {
getNonOrphanVisits: (params?: ShlinkVisitsParams, doIntervalFallback?: boolean) => void;

View file

@ -4,10 +4,11 @@ import { Topics } from '../mercure/helpers/Topics';
import { useGoBack } from '../utils/helpers/hooks';
import { ReportExporter } from '../common/services/ReportExporter';
import { VisitsStats } from './VisitsStats';
import { NormalizedVisit, OrphanVisitType, VisitsInfo, VisitsParams } from './types';
import { NormalizedVisit, OrphanVisitType, VisitsParams } from './types';
import { CommonVisitsProps } from './types/CommonVisitsProps';
import { toApiParams } from './types/helpers';
import { VisitsHeader } from './VisitsHeader';
import { VisitsInfo } from './reducers/types';
export interface OrphanVisitsProps extends CommonVisitsProps {
getOrphanVisits: (

View file

@ -19,13 +19,14 @@ import { NavPillItem, NavPills } from '../utils/NavPills';
import { ExportBtn } from '../utils/ExportBtn';
import { LineChartCard } from './charts/LineChartCard';
import { VisitsTable } from './VisitsTable';
import { NormalizedOrphanVisit, NormalizedVisit, VisitsFilter, VisitsInfo, VisitsParams } from './types';
import { NormalizedOrphanVisit, NormalizedVisit, VisitsFilter, VisitsParams } from './types';
import { OpenMapModalBtn } from './helpers/OpenMapModalBtn';
import { normalizeVisits, processStatsFromVisits } from './services/VisitsParser';
import { VisitsFilterDropdown } from './helpers/VisitsFilterDropdown';
import { HighlightableProps, highlightedVisitsToStats } from './types/helpers';
import { DoughnutChartCard } from './charts/DoughnutChartCard';
import { SortableBarChartCard } from './charts/SortableBarChartCard';
import { VisitsInfo } from './reducers/types';
export type VisitsStatsProps = PropsWithChildren<{
getVisits: (params: VisitsParams, doIntervalFallback?: boolean) => void;

View file

@ -1,10 +1,11 @@
import { flatten, prop, range, splitEvery } from 'ramda';
import { Action, Dispatch } from 'redux';
import { ShlinkPaginator, ShlinkVisits, ShlinkVisitsParams } from '../../api/types';
import { Visit, VisitsLoadProgressChangedAction } from '../types';
import { Visit } from '../types';
import { parseApiError } from '../../api/utils';
import { ApiErrorAction } from '../../api/types/actions';
import { dateToMatchingInterval } from '../../utils/dates/types';
import { VisitsLoadProgressChangedAction } from './types';
const ITEMS_PER_PAGE = 5000;
const PARALLEL_REQUESTS_COUNT = 4;

View file

@ -1,6 +1,6 @@
import { createAction } from '@reduxjs/toolkit';
import { Action, Dispatch } from 'redux';
import { Visit, VisitsFallbackIntervalAction, VisitsInfo, VisitsLoadProgressChangedAction } from '../types';
import { Visit } from '../types';
import { buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
import { GetState } from '../../container/types';
@ -10,6 +10,7 @@ import { isBetween } from '../../utils/helpers/date';
import { getVisitsWithLoader, lastVisitLoaderForLoader } from './common';
import { createNewVisits, CreateVisitsAction } from './visitCreation';
import { domainMatches } from '../../short-urls/helpers';
import { LoadVisits, VisitsFallbackIntervalAction, VisitsInfo, VisitsLoadProgressChangedAction } from './types';
const REDUCER_PREFIX = 'shlink/domainVisits';
export const GET_DOMAIN_VISITS_START = `${REDUCER_PREFIX}/getDomainVisits/pending`;
@ -26,6 +27,10 @@ export interface DomainVisits extends VisitsInfo {
domain: string;
}
export interface LoadDomainVisits extends LoadVisits {
domain: string;
}
export interface DomainVisitsAction extends Action<string> {
visits: Visit[];
domain: string;
@ -73,9 +78,7 @@ export default buildReducer<DomainVisits, DomainVisitsCombinedAction>({
}, initialState);
export const getDomainVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => (
domain: string,
query: ShlinkVisitsParams = {},
doIntervalFallback = false,
{ domain, query = {}, doIntervalFallback = false }: LoadDomainVisits,
) => async (dispatch: Dispatch, getState: GetState) => {
const { getDomainVisits: getVisits } = buildShlinkApiClient(getState);
const visitsLoader = async (page: number, itemsPerPage: number) => getVisits(

View file

@ -1,11 +1,6 @@
import { createAction } from '@reduxjs/toolkit';
import { Action, Dispatch } from 'redux';
import {
Visit,
VisitsFallbackIntervalAction,
VisitsInfo,
VisitsLoadProgressChangedAction,
} from '../types';
import { Visit } from '../types';
import { buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
import { GetState } from '../../container/types';
@ -14,6 +9,7 @@ import { ApiErrorAction } from '../../api/types/actions';
import { isBetween } from '../../utils/helpers/date';
import { getVisitsWithLoader, lastVisitLoaderForLoader } from './common';
import { createNewVisits, CreateVisitsAction } from './visitCreation';
import { VisitsFallbackIntervalAction, VisitsInfo, VisitsLoadProgressChangedAction } from './types';
const REDUCER_PREFIX = 'shlink/orphanVisits';
export const GET_NON_ORPHAN_VISITS_START = `${REDUCER_PREFIX}/getNonOrphanVisits/pending`;

View file

@ -1,13 +1,6 @@
import { createAction } from '@reduxjs/toolkit';
import { Action, Dispatch } from 'redux';
import {
OrphanVisit,
OrphanVisitType,
Visit,
VisitsFallbackIntervalAction,
VisitsInfo,
VisitsLoadProgressChangedAction,
} from '../types';
import { OrphanVisit, OrphanVisitType, Visit } from '../types';
import { buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
import { GetState } from '../../container/types';
@ -17,6 +10,7 @@ import { ApiErrorAction } from '../../api/types/actions';
import { isBetween } from '../../utils/helpers/date';
import { getVisitsWithLoader, lastVisitLoaderForLoader } from './common';
import { createNewVisits, CreateVisitsAction } from './visitCreation';
import { VisitsFallbackIntervalAction, VisitsInfo, VisitsLoadProgressChangedAction } from './types';
const REDUCER_PREFIX = 'shlink/orphanVisits';
export const GET_ORPHAN_VISITS_START = `${REDUCER_PREFIX}/getOrphanVisits/pending`;

View file

@ -1,7 +1,7 @@
import { createAction } from '@reduxjs/toolkit';
import { Action, Dispatch } from 'redux';
import { shortUrlMatches } from '../../short-urls/helpers';
import { Visit, VisitsFallbackIntervalAction, VisitsInfo, VisitsLoadProgressChangedAction } from '../types';
import { Visit } from '../types';
import { ShortUrlIdentifier } from '../../short-urls/data';
import { buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
@ -11,6 +11,7 @@ import { ApiErrorAction } from '../../api/types/actions';
import { isBetween } from '../../utils/helpers/date';
import { getVisitsWithLoader, lastVisitLoaderForLoader } from './common';
import { createNewVisits, CreateVisitsAction } from './visitCreation';
import { VisitsFallbackIntervalAction, VisitsInfo, VisitsLoadProgressChangedAction } from './types';
const REDUCER_PREFIX = 'shlink/shortUrlVisits';
export const GET_SHORT_URL_VISITS_START = `${REDUCER_PREFIX}/getShortUrlVisits/pending`;

View file

@ -1,6 +1,6 @@
import { createAction } from '@reduxjs/toolkit';
import { Action, Dispatch } from 'redux';
import { Visit, VisitsFallbackIntervalAction, VisitsInfo, VisitsLoadProgressChangedAction } from '../types';
import { Visit } from '../types';
import { buildReducer } from '../../utils/helpers/redux';
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
import { GetState } from '../../container/types';
@ -9,6 +9,7 @@ import { ApiErrorAction } from '../../api/types/actions';
import { isBetween } from '../../utils/helpers/date';
import { getVisitsWithLoader, lastVisitLoaderForLoader } from './common';
import { createNewVisits, CreateVisitsAction } from './visitCreation';
import { VisitsFallbackIntervalAction, VisitsInfo, VisitsLoadProgressChangedAction } from './types';
const REDUCER_PREFIX = 'shlink/tagVisits';
export const GET_TAG_VISITS_START = `${REDUCER_PREFIX}/getTagVisits/pending`;

View file

@ -0,0 +1,26 @@
import { PayloadAction } from '@reduxjs/toolkit';
import { ShlinkVisitsParams } from '../../../api/types';
import { DateInterval } from '../../../utils/dates/types';
import { ProblemDetailsError } from '../../../api/types/errors';
import { Visit } from '../../types';
export interface VisitsInfo {
visits: Visit[];
loading: boolean;
loadingLarge: boolean;
error: boolean;
errorData?: ProblemDetailsError;
progress: number;
cancelLoad: boolean;
query?: ShlinkVisitsParams;
fallbackInterval?: DateInterval;
}
export interface LoadVisits {
query?: ShlinkVisitsParams;
doIntervalFallback?: boolean;
}
export type VisitsLoadProgressChangedAction = PayloadAction<number>;
export type VisitsFallbackIntervalAction = PayloadAction<DateInterval>;

View file

@ -1,24 +1,5 @@
import { PayloadAction } from '@reduxjs/toolkit';
import { ShortUrl } from '../../short-urls/data';
import { ShlinkVisitsParams } from '../../api/types';
import { DateInterval, DateRange } from '../../utils/dates/types';
import { ProblemDetailsError } from '../../api/types/errors';
export interface VisitsInfo {
visits: Visit[];
loading: boolean;
loadingLarge: boolean;
error: boolean;
errorData?: ProblemDetailsError;
progress: number;
cancelLoad: boolean;
query?: ShlinkVisitsParams;
fallbackInterval?: DateInterval;
}
export type VisitsLoadProgressChangedAction = PayloadAction<number>;
export type VisitsFallbackIntervalAction = PayloadAction<DateInterval>;
import { DateRange } from '../../utils/dates/types';
export type OrphanVisitType = 'base_url' | 'invalid_short_url' | 'regular_404';

View file

@ -38,7 +38,7 @@ describe('<DomainVisits />', () => {
it('wraps visits stats and header', () => {
setUp();
expect(screen.getByRole('heading', { name: '"foo.com" visits' })).toBeInTheDocument();
expect(getDomainVisits).toHaveBeenCalledWith('DEFAULT', expect.anything(), expect.anything());
expect(getDomainVisits).toHaveBeenCalledWith(expect.objectContaining({ domain: 'DEFAULT' }));
});
it('exports visits when clicking the button', async () => {

View file

@ -4,11 +4,12 @@ import { Mock } from 'ts-mockery';
import { formatISO } from 'date-fns';
import { NonOrphanVisits as createNonOrphanVisits } from '../../src/visits/NonOrphanVisits';
import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import { Visit, VisitsInfo } from '../../src/visits/types';
import { Visit } from '../../src/visits/types';
import { Settings } from '../../src/settings/reducers/settings';
import { ReportExporter } from '../../src/common/services/ReportExporter';
import { SelectedServer } from '../../src/servers/data';
import { renderWithEvents } from '../__helpers__/setUpTest';
import { VisitsInfo } from '../../src/visits/reducers/types';
describe('<NonOrphanVisits />', () => {
const exportVisits = jest.fn();

View file

@ -4,11 +4,12 @@ import { Mock } from 'ts-mockery';
import { formatISO } from 'date-fns';
import { OrphanVisits as createOrphanVisits } from '../../src/visits/OrphanVisits';
import { MercureBoundProps } from '../../src/mercure/helpers/boundToMercureHub';
import { Visit, VisitsInfo } from '../../src/visits/types';
import { Visit } from '../../src/visits/types';
import { Settings } from '../../src/settings/reducers/settings';
import { ReportExporter } from '../../src/common/services/ReportExporter';
import { SelectedServer } from '../../src/servers/data';
import { renderWithEvents } from '../__helpers__/setUpTest';
import { VisitsInfo } from '../../src/visits/reducers/types';
describe('<OrphanVisits />', () => {
const getOrphanVisits = jest.fn();

View file

@ -3,11 +3,12 @@ import { Mock } from 'ts-mockery';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { VisitsStats } from '../../src/visits/VisitsStats';
import { Visit, VisitsInfo } from '../../src/visits/types';
import { Visit } from '../../src/visits/types';
import { Settings } from '../../src/settings/reducers/settings';
import { SelectedServer } from '../../src/servers/data';
import { renderWithEvents } from '../__helpers__/setUpTest';
import { rangeOf } from '../../src/utils/utils';
import { VisitsInfo } from '../../src/visits/reducers/types';
describe('<VisitsStats />', () => {
const visits = rangeOf(3, () => Mock.of<Visit>({ date: '2020-01-01' }));

View file

@ -175,7 +175,7 @@ describe('domainVisitsReducer', () => {
it('dispatches start and error when promise is rejected', async () => {
const shlinkApiClient = buildApiClientMock(Promise.reject(new Error()));
await getDomainVisits(() => shlinkApiClient)('foo.com')(dispatchMock, getState);
await getDomainVisits(() => shlinkApiClient)({ domain })(dispatchMock, getState);
expect(dispatchMock).toHaveBeenCalledTimes(2);
expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_DOMAIN_VISITS_START });
@ -197,7 +197,7 @@ describe('domainVisitsReducer', () => {
},
}));
await getDomainVisits(() => shlinkApiClient)(domain, query)(dispatchMock, getState);
await getDomainVisits(() => shlinkApiClient)({ domain, query })(dispatchMock, getState);
expect(dispatchMock).toHaveBeenCalledTimes(2);
expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_DOMAIN_VISITS_START });
@ -229,7 +229,7 @@ describe('domainVisitsReducer', () => {
.mockResolvedValueOnce(buildVisitsResult(lastVisits));
const ShlinkApiClient = Mock.of<ShlinkApiClient>({ getDomainVisits: getShlinkDomainVisits });
await getDomainVisits(() => ShlinkApiClient)(domain, {}, true)(dispatchMock, getState);
await getDomainVisits(() => ShlinkApiClient)({ domain, doIntervalFallback: true })(dispatchMock, getState);
expect(dispatchMock).toHaveBeenCalledTimes(2);
expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_DOMAIN_VISITS_START });

View file

@ -12,13 +12,14 @@ import reducer, {
GET_NON_ORPHAN_VISITS_FALLBACK_TO_INTERVAL,
} from '../../../src/visits/reducers/nonOrphanVisits';
import { rangeOf } from '../../../src/utils/utils';
import { Visit, VisitsInfo } from '../../../src/visits/types';
import { Visit } from '../../../src/visits/types';
import { ShlinkVisits } from '../../../src/api/types';
import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import { ShlinkState } from '../../../src/container/types';
import { formatIsoDate } from '../../../src/utils/helpers/date';
import { DateInterval } from '../../../src/utils/dates/types';
import { createNewVisits } from '../../../src/visits/reducers/visitCreation';
import { VisitsInfo } from '../../../src/visits/reducers/types';
describe('nonOrphanVisitsReducer', () => {
const now = new Date();

View file

@ -12,13 +12,14 @@ import reducer, {
GET_ORPHAN_VISITS_FALLBACK_TO_INTERVAL,
} from '../../../src/visits/reducers/orphanVisits';
import { rangeOf } from '../../../src/utils/utils';
import { Visit, VisitsInfo } from '../../../src/visits/types';
import { Visit } from '../../../src/visits/types';
import { ShlinkVisits } from '../../../src/api/types';
import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
import { ShlinkState } from '../../../src/container/types';
import { formatIsoDate } from '../../../src/utils/helpers/date';
import { DateInterval } from '../../../src/utils/dates/types';
import { createNewVisits } from '../../../src/visits/reducers/visitCreation';
import { VisitsInfo } from '../../../src/visits/reducers/types';
describe('orphanVisitsReducer', () => {
const now = new Date();