mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-03-13 19:13:46 +00:00
Extract shlink-web-component outside of src folder
This commit is contained in:
107
shlink-web-component/visits/services/VisitsParser.ts
Normal file
107
shlink-web-component/visits/services/VisitsParser.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { isNil, map } from 'ramda';
|
||||
import { hasValue } from '../../../src/utils/utils';
|
||||
import type { CityStats, NormalizedVisit, Stats, Visit, VisitsStats } from '../types';
|
||||
import { isNormalizedOrphanVisit, isOrphanVisit } from '../types/helpers';
|
||||
import { extractDomain, parseUserAgent } from '../utils';
|
||||
|
||||
/* eslint-disable no-param-reassign */
|
||||
const visitHasProperty = (visit: NormalizedVisit, propertyName: keyof NormalizedVisit) =>
|
||||
!isNil(visit) && hasValue(visit[propertyName]);
|
||||
|
||||
const optionalNumericToNumber = (numeric: string | number | null | undefined): number => {
|
||||
if (typeof numeric === 'number') {
|
||||
return numeric;
|
||||
}
|
||||
|
||||
return numeric ? parseFloat(numeric) : 0;
|
||||
};
|
||||
|
||||
const updateOsStatsForVisit = (osStats: Stats, { os }: NormalizedVisit) => {
|
||||
osStats[os] = (osStats[os] || 0) + 1;
|
||||
};
|
||||
|
||||
const updateBrowsersStatsForVisit = (browsersStats: Stats, { browser }: NormalizedVisit) => {
|
||||
browsersStats[browser] = (browsersStats[browser] || 0) + 1;
|
||||
};
|
||||
|
||||
const updateReferrersStatsForVisit = (referrersStats: Stats, { referer: domain }: NormalizedVisit) => {
|
||||
referrersStats[domain] = (referrersStats[domain] || 0) + 1;
|
||||
};
|
||||
|
||||
const updateLocationsStatsForVisit = (propertyName: 'country' | 'city') => (stats: Stats, visit: NormalizedVisit) => {
|
||||
const hasLocationProperty = visitHasProperty(visit, propertyName);
|
||||
const value = hasLocationProperty ? visit[propertyName] : 'Unknown';
|
||||
|
||||
stats[value] = (stats[value] || 0) + 1;
|
||||
};
|
||||
|
||||
const updateCountriesStatsForVisit = updateLocationsStatsForVisit('country');
|
||||
const updateCitiesStatsForVisit = updateLocationsStatsForVisit('city');
|
||||
|
||||
const updateCitiesForMapForVisit = (citiesForMapStats: Record<string, CityStats>, visit: NormalizedVisit) => {
|
||||
if (!visitHasProperty(visit, 'city') || visit.city === 'Unknown') {
|
||||
return;
|
||||
}
|
||||
|
||||
const { city, latitude, longitude } = visit;
|
||||
const currentCity = citiesForMapStats[city] || {
|
||||
cityName: city,
|
||||
count: 0,
|
||||
latLong: [optionalNumericToNumber(latitude), optionalNumericToNumber(longitude)],
|
||||
};
|
||||
|
||||
currentCity.count += 1;
|
||||
|
||||
citiesForMapStats[city] = currentCity;
|
||||
};
|
||||
|
||||
const updateVisitedUrlsForVisit = (visitedUrlsStats: Stats, visit: NormalizedVisit) => {
|
||||
if (!isNormalizedOrphanVisit(visit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { visitedUrl } = visit;
|
||||
|
||||
visitedUrlsStats[visitedUrl] = (visitedUrlsStats[visitedUrl] || 0) + 1;
|
||||
};
|
||||
|
||||
export const processStatsFromVisits = (visits: NormalizedVisit[]) => visits.reduce(
|
||||
(stats: VisitsStats, visit: NormalizedVisit) => {
|
||||
// We mutate the original object because it has a big performance impact when large data sets are processed
|
||||
updateOsStatsForVisit(stats.os, visit);
|
||||
updateBrowsersStatsForVisit(stats.browsers, visit);
|
||||
updateReferrersStatsForVisit(stats.referrers, visit);
|
||||
updateCountriesStatsForVisit(stats.countries, visit);
|
||||
updateCitiesStatsForVisit(stats.cities, visit);
|
||||
updateCitiesForMapForVisit(stats.citiesForMap, visit);
|
||||
updateVisitedUrlsForVisit(stats.visitedUrls, visit);
|
||||
|
||||
return stats;
|
||||
},
|
||||
{ os: {}, browsers: {}, referrers: {}, countries: {}, cities: {}, citiesForMap: {}, visitedUrls: {} },
|
||||
);
|
||||
|
||||
export const normalizeVisits = map((visit: Visit): NormalizedVisit => {
|
||||
const { userAgent, date, referer, visitLocation, potentialBot } = visit;
|
||||
const common = {
|
||||
date,
|
||||
potentialBot,
|
||||
...parseUserAgent(userAgent),
|
||||
referer: extractDomain(referer),
|
||||
country: visitLocation?.countryName || 'Unknown', // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing
|
||||
city: visitLocation?.cityName || 'Unknown', // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing
|
||||
latitude: visitLocation?.latitude,
|
||||
longitude: visitLocation?.longitude,
|
||||
};
|
||||
|
||||
if (!isOrphanVisit(visit)) {
|
||||
return common;
|
||||
}
|
||||
|
||||
return { ...common, type: visit.type, visitedUrl: visit.visitedUrl };
|
||||
});
|
||||
|
||||
export interface VisitsParser {
|
||||
processStatsFromVisits: (normalizedVisits: NormalizedVisit[]) => VisitsStats;
|
||||
normalizeVisits: (visits: Visit[]) => NormalizedVisit[];
|
||||
}
|
||||
93
shlink-web-component/visits/services/provideServices.ts
Normal file
93
shlink-web-component/visits/services/provideServices.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import type Bottle from 'bottlejs';
|
||||
import { prop } from 'ramda';
|
||||
import type { ConnectDecorator } from '../../container';
|
||||
import { DomainVisits } from '../DomainVisits';
|
||||
import { MapModal } from '../helpers/MapModal';
|
||||
import { NonOrphanVisits } from '../NonOrphanVisits';
|
||||
import { OrphanVisits } from '../OrphanVisits';
|
||||
import { domainVisitsReducerCreator, getDomainVisits } from '../reducers/domainVisits';
|
||||
import { getNonOrphanVisits, nonOrphanVisitsReducerCreator } from '../reducers/nonOrphanVisits';
|
||||
import { getOrphanVisits, orphanVisitsReducerCreator } from '../reducers/orphanVisits';
|
||||
import { getShortUrlVisits, shortUrlVisitsReducerCreator } from '../reducers/shortUrlVisits';
|
||||
import { getTagVisits, tagVisitsReducerCreator } from '../reducers/tagVisits';
|
||||
import { createNewVisits } from '../reducers/visitCreation';
|
||||
import { loadVisitsOverview, visitsOverviewReducerCreator } from '../reducers/visitsOverview';
|
||||
import { ShortUrlVisits } from '../ShortUrlVisits';
|
||||
import { TagVisits } from '../TagVisits';
|
||||
import * as visitsParser from './VisitsParser';
|
||||
|
||||
export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||
// Components
|
||||
bottle.serviceFactory('MapModal', () => MapModal);
|
||||
|
||||
bottle.serviceFactory('ShortUrlVisits', ShortUrlVisits, 'ReportExporter');
|
||||
bottle.decorator('ShortUrlVisits', connect(
|
||||
['shortUrlVisits', 'shortUrlDetail', 'mercureInfo'],
|
||||
['getShortUrlVisits', 'getShortUrlDetail', 'cancelGetShortUrlVisits', 'createNewVisits', 'loadMercureInfo'],
|
||||
));
|
||||
|
||||
bottle.serviceFactory('TagVisits', TagVisits, 'ColorGenerator', 'ReportExporter');
|
||||
bottle.decorator('TagVisits', connect(
|
||||
['tagVisits', 'mercureInfo'],
|
||||
['getTagVisits', 'cancelGetTagVisits', 'createNewVisits', 'loadMercureInfo'],
|
||||
));
|
||||
|
||||
bottle.serviceFactory('DomainVisits', DomainVisits, 'ReportExporter');
|
||||
bottle.decorator('DomainVisits', connect(
|
||||
['domainVisits', 'mercureInfo'],
|
||||
['getDomainVisits', 'cancelGetDomainVisits', 'createNewVisits', 'loadMercureInfo'],
|
||||
));
|
||||
|
||||
bottle.serviceFactory('OrphanVisits', OrphanVisits, 'ReportExporter');
|
||||
bottle.decorator('OrphanVisits', connect(
|
||||
['orphanVisits', 'mercureInfo'],
|
||||
['getOrphanVisits', 'cancelGetOrphanVisits', 'createNewVisits', 'loadMercureInfo'],
|
||||
));
|
||||
|
||||
bottle.serviceFactory('NonOrphanVisits', NonOrphanVisits, 'ReportExporter');
|
||||
bottle.decorator('NonOrphanVisits', connect(
|
||||
['nonOrphanVisits', 'mercureInfo'],
|
||||
['getNonOrphanVisits', 'cancelGetNonOrphanVisits', 'createNewVisits', 'loadMercureInfo'],
|
||||
));
|
||||
|
||||
// Services
|
||||
bottle.serviceFactory('VisitsParser', () => visitsParser);
|
||||
|
||||
// Actions
|
||||
bottle.serviceFactory('getShortUrlVisits', getShortUrlVisits, 'apiClient');
|
||||
bottle.serviceFactory('cancelGetShortUrlVisits', prop('cancelGetVisits'), 'shortUrlVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('getTagVisits', getTagVisits, 'apiClient');
|
||||
bottle.serviceFactory('cancelGetTagVisits', prop('cancelGetVisits'), 'tagVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('getDomainVisits', getDomainVisits, 'apiClient');
|
||||
bottle.serviceFactory('cancelGetDomainVisits', prop('cancelGetVisits'), 'domainVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('getOrphanVisits', getOrphanVisits, 'apiClient');
|
||||
bottle.serviceFactory('cancelGetOrphanVisits', prop('cancelGetVisits'), 'orphanVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('getNonOrphanVisits', getNonOrphanVisits, 'apiClient');
|
||||
bottle.serviceFactory('cancelGetNonOrphanVisits', prop('cancelGetVisits'), 'nonOrphanVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('createNewVisits', () => createNewVisits);
|
||||
bottle.serviceFactory('loadVisitsOverview', loadVisitsOverview, 'apiClient');
|
||||
|
||||
// Reducers
|
||||
bottle.serviceFactory('visitsOverviewReducerCreator', visitsOverviewReducerCreator, 'loadVisitsOverview');
|
||||
bottle.serviceFactory('visitsOverviewReducer', prop('reducer'), 'visitsOverviewReducerCreator');
|
||||
|
||||
bottle.serviceFactory('domainVisitsReducerCreator', domainVisitsReducerCreator, 'getDomainVisits');
|
||||
bottle.serviceFactory('domainVisitsReducer', prop('reducer'), 'domainVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('nonOrphanVisitsReducerCreator', nonOrphanVisitsReducerCreator, 'getNonOrphanVisits');
|
||||
bottle.serviceFactory('nonOrphanVisitsReducer', prop('reducer'), 'nonOrphanVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('orphanVisitsReducerCreator', orphanVisitsReducerCreator, 'getOrphanVisits');
|
||||
bottle.serviceFactory('orphanVisitsReducer', prop('reducer'), 'orphanVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('shortUrlVisitsReducerCreator', shortUrlVisitsReducerCreator, 'getShortUrlVisits');
|
||||
bottle.serviceFactory('shortUrlVisitsReducer', prop('reducer'), 'shortUrlVisitsReducerCreator');
|
||||
|
||||
bottle.serviceFactory('tagVisitsReducerCreator', tagVisitsReducerCreator, 'getTagVisits');
|
||||
bottle.serviceFactory('tagVisitsReducer', prop('reducer'), 'tagVisitsReducerCreator');
|
||||
};
|
||||
Reference in New Issue
Block a user