Extract shlink-web-component outside of src folder

This commit is contained in:
Alejandro Celaya
2023-07-24 20:14:59 +02:00
parent 768fb1992f
commit 3a0cea1268
230 changed files with 485 additions and 524 deletions

View 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[];
}

View 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');
};