Provide API client to shlink-web-component

This commit is contained in:
Alejandro Celaya
2023-07-24 09:48:41 +02:00
parent b3122219be
commit 2ac5236cc7
31 changed files with 220 additions and 192 deletions

View File

@@ -22,7 +22,7 @@ type LastVisitLoader = (excludeBots?: boolean) => Promise<Visit | undefined>;
interface VisitsAsyncThunkOptions<T extends LoadVisits = LoadVisits, R extends VisitsLoaded = VisitsLoaded> {
typePrefix: string;
createLoaders: (params: T, getState: () => ShlinkState) => [VisitsLoader, LastVisitLoader];
createLoaders: (params: T) => [VisitsLoader, LastVisitLoader];
getExtraFulfilledPayload: (params: T) => Partial<R>;
shouldCancel: (getState: () => ShlinkState) => boolean;
}
@@ -35,7 +35,7 @@ export const createVisitsAsyncThunk = <T extends LoadVisits = LoadVisits, R exte
const fallbackToInterval = createAction<DateInterval>(`${typePrefix}/fallbackToInterval`);
const asyncThunk = createAsyncThunk(typePrefix, async (params: T, { getState, dispatch }): Promise<Partial<R>> => {
const [visitsLoader, lastVisitLoader] = createLoaders(params, getState);
const [visitsLoader, lastVisitLoader] = createLoaders(params);
const loadVisitsInParallel = async (pages: number[]): Promise<Visit[]> =>
Promise.all(pages.map(async (page) => visitsLoader(page, ITEMS_PER_PAGE).then(prop('data')))).then(flatten);

View File

@@ -1,4 +1,4 @@
import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder';
import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient';
import { isBetween } from '../../../utils/helpers/date';
import { domainMatches } from '../../short-urls/helpers';
import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common';
@@ -26,10 +26,10 @@ const initialState: DomainVisits = {
progress: 0,
};
export const getDomainVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({
export const getDomainVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({
typePrefix: `${REDUCER_PREFIX}/getDomainVisits`,
createLoaders: ({ domain, query = {}, doIntervalFallback = false }: LoadDomainVisits, getState) => {
const { getDomainVisits: getVisits } = buildShlinkApiClient(getState);
createLoaders: ({ domain, query = {}, doIntervalFallback = false }: LoadDomainVisits) => {
const { getDomainVisits: getVisits } = apiClient;
const visitsLoader = async (page: number, itemsPerPage: number) => getVisits(
domain,
{ ...query, page, itemsPerPage },

View File

@@ -1,3 +1,4 @@
import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient';
import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder';
import { isBetween } from '../../../utils/helpers/date';
import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common';
@@ -14,10 +15,10 @@ const initialState: VisitsInfo = {
progress: 0,
};
export const getNonOrphanVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({
export const getNonOrphanVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({
typePrefix: `${REDUCER_PREFIX}/getNonOrphanVisits`,
createLoaders: ({ query = {}, doIntervalFallback = false }, getState) => {
const { getNonOrphanVisits: shlinkGetNonOrphanVisits } = buildShlinkApiClient(getState);
createLoaders: ({ query = {}, doIntervalFallback = false }) => {
const { getNonOrphanVisits: shlinkGetNonOrphanVisits } = apiClient;
const visitsLoader = async (page: number, itemsPerPage: number) =>
shlinkGetNonOrphanVisits({ ...query, page, itemsPerPage });
const lastVisitLoader = lastVisitLoaderForLoader(doIntervalFallback, shlinkGetNonOrphanVisits);

View File

@@ -1,3 +1,4 @@
import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient';
import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder';
import { isBetween } from '../../../utils/helpers/date';
import type { OrphanVisit, OrphanVisitType } from '../types';
@@ -23,10 +24,10 @@ const initialState: VisitsInfo = {
const matchesType = (visit: OrphanVisit, orphanVisitsType?: OrphanVisitType) =>
!orphanVisitsType || orphanVisitsType === visit.type;
export const getOrphanVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({
export const getOrphanVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({
typePrefix: `${REDUCER_PREFIX}/getOrphanVisits`,
createLoaders: ({ orphanVisitsType, query = {}, doIntervalFallback = false }: LoadOrphanVisits, getState) => {
const { getOrphanVisits: getVisits } = buildShlinkApiClient(getState);
createLoaders: ({ orphanVisitsType, query = {}, doIntervalFallback = false }: LoadOrphanVisits) => {
const { getOrphanVisits: getVisits } = apiClient;
const visitsLoader = async (page: number, itemsPerPage: number) => getVisits({ ...query, page, itemsPerPage })
.then((result) => {
const visits = result.data.filter((visit) => isOrphanVisit(visit) && matchesType(visit, orphanVisitsType));

View File

@@ -1,4 +1,4 @@
import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder';
import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient';
import { isBetween } from '../../../utils/helpers/date';
import type { ShortUrlIdentifier } from '../../short-urls/data';
import { shortUrlMatches } from '../../short-urls/helpers';
@@ -24,10 +24,10 @@ const initialState: ShortUrlVisits = {
progress: 0,
};
export const getShortUrlVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({
export const getShortUrlVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({
typePrefix: `${REDUCER_PREFIX}/getShortUrlVisits`,
createLoaders: ({ shortCode, query = {}, doIntervalFallback = false }: LoadShortUrlVisits, getState) => {
const { getShortUrlVisits: shlinkGetShortUrlVisits } = buildShlinkApiClient(getState);
createLoaders: ({ shortCode, query = {}, doIntervalFallback = false }: LoadShortUrlVisits) => {
const { getShortUrlVisits: shlinkGetShortUrlVisits } = apiClient;
const visitsLoader = async (page: number, itemsPerPage: number) => shlinkGetShortUrlVisits(
shortCode,
{ ...query, page, itemsPerPage },

View File

@@ -1,4 +1,4 @@
import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder';
import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient';
import { isBetween } from '../../../utils/helpers/date';
import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common';
import type { LoadVisits, VisitsInfo } from './types';
@@ -23,10 +23,10 @@ const initialState: TagVisits = {
progress: 0,
};
export const getTagVisits = (buildShlinkApiClient: ShlinkApiClientBuilder) => createVisitsAsyncThunk({
export const getTagVisits = (apiClient: ShlinkApiClient) => createVisitsAsyncThunk({
typePrefix: `${REDUCER_PREFIX}/getTagVisits`,
createLoaders: ({ tag, query = {}, doIntervalFallback = false }: LoadTagVisits, getState) => {
const { getTagVisits: getVisits } = buildShlinkApiClient(getState);
createLoaders: ({ tag, query = {}, doIntervalFallback = false }: LoadTagVisits) => {
const { getTagVisits: getVisits } = apiClient;
const visitsLoader = async (page: number, itemsPerPage: number) => getVisits(
tag,
{ ...query, page, itemsPerPage },

View File

@@ -1,6 +1,6 @@
import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
import type { ShlinkApiClientBuilder } from '../../../api/services/ShlinkApiClientBuilder';
import type { ShlinkApiClient } from '../../../api/services/ShlinkApiClient';
import type { ShlinkVisitsOverview } from '../../../api/types';
import { createAsyncThunk } from '../../../utils/helpers/redux';
import type { CreateVisit } from '../types';
@@ -40,19 +40,19 @@ const initialState: VisitsOverview = {
const countBots = (visits: CreateVisit[]) => visits.filter(({ visit }) => visit.potentialBot).length;
export const loadVisitsOverview = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk(
export const loadVisitsOverview = (apiClient: ShlinkApiClient) => createAsyncThunk(
`${REDUCER_PREFIX}/loadVisitsOverview`,
(_: void, { getState }): Promise<ParsedVisitsOverview> => buildShlinkApiClient(getState).getVisitsOverview().then(
(resp) => ({
(): Promise<ParsedVisitsOverview> => apiClient.getVisitsOverview().then(
({ nonOrphanVisits, visitsCount, orphanVisits, orphanVisitsCount }) => ({
nonOrphanVisits: {
total: resp.nonOrphanVisits?.total ?? resp.visitsCount,
nonBots: resp.nonOrphanVisits?.nonBots,
bots: resp.nonOrphanVisits?.bots,
total: nonOrphanVisits?.total ?? visitsCount,
nonBots: nonOrphanVisits?.nonBots,
bots: nonOrphanVisits?.bots,
},
orphanVisits: {
total: resp.orphanVisits?.total ?? resp.orphanVisitsCount,
nonBots: resp.orphanVisits?.nonBots,
bots: resp.orphanVisits?.bots,
total: orphanVisits?.total ?? orphanVisitsCount,
nonBots: orphanVisits?.nonBots,
bots: orphanVisits?.bots,
},
}),
),

View File

@@ -54,23 +54,23 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
bottle.serviceFactory('VisitsParser', () => visitsParser);
// Actions
bottle.serviceFactory('getShortUrlVisits', getShortUrlVisits, 'buildShlinkApiClient');
bottle.serviceFactory('getShortUrlVisits', getShortUrlVisits, 'apiClient');
bottle.serviceFactory('cancelGetShortUrlVisits', prop('cancelGetVisits'), 'shortUrlVisitsReducerCreator');
bottle.serviceFactory('getTagVisits', getTagVisits, 'buildShlinkApiClient');
bottle.serviceFactory('getTagVisits', getTagVisits, 'apiClient');
bottle.serviceFactory('cancelGetTagVisits', prop('cancelGetVisits'), 'tagVisitsReducerCreator');
bottle.serviceFactory('getDomainVisits', getDomainVisits, 'buildShlinkApiClient');
bottle.serviceFactory('getDomainVisits', getDomainVisits, 'apiClient');
bottle.serviceFactory('cancelGetDomainVisits', prop('cancelGetVisits'), 'domainVisitsReducerCreator');
bottle.serviceFactory('getOrphanVisits', getOrphanVisits, 'buildShlinkApiClient');
bottle.serviceFactory('getOrphanVisits', getOrphanVisits, 'apiClient');
bottle.serviceFactory('cancelGetOrphanVisits', prop('cancelGetVisits'), 'orphanVisitsReducerCreator');
bottle.serviceFactory('getNonOrphanVisits', getNonOrphanVisits, 'buildShlinkApiClient');
bottle.serviceFactory('getNonOrphanVisits', getNonOrphanVisits, 'apiClient');
bottle.serviceFactory('cancelGetNonOrphanVisits', prop('cancelGetVisits'), 'nonOrphanVisitsReducerCreator');
bottle.serviceFactory('createNewVisits', () => createNewVisits);
bottle.serviceFactory('loadVisitsOverview', loadVisitsOverview, 'buildShlinkApiClient');
bottle.serviceFactory('loadVisitsOverview', loadVisitsOverview, 'apiClient');
// Reducers
bottle.serviceFactory('visitsOverviewReducerCreator', visitsOverviewReducerCreator, 'loadVisitsOverview');