Extracted short-url related services to its own service provider

This commit is contained in:
Alejandro Celaya 2018-12-18 19:59:50 +01:00
parent cf1239cf6e
commit 4b1f5e9f4c
16 changed files with 94 additions and 121 deletions

View file

@ -1,31 +1,18 @@
import Bottle from 'bottlejs';
import { withRouter } from 'react-router-dom';
import { connect as reduxConnect } from 'react-redux';
import { assoc, pick } from 'ramda';
import { pick } from 'ramda';
import axios from 'axios';
import App from '../App';
import ScrollToTop from '../common/ScrollToTop';
import MainHeader from '../common/MainHeader';
import { resetSelectedServer } from '../servers/reducers/selectedServer';
import Home from '../common/Home';
import MenuLayout from '../common/MenuLayout';
import ShortUrls from '../short-urls/ShortUrls';
import SearchBar from '../short-urls/SearchBar';
import { listShortUrls } from '../short-urls/reducers/shortUrlsList';
import ShortUrlsList from '../short-urls/ShortUrlsList';
import { resetShortUrlParams } from '../short-urls/reducers/shortUrlsListParams';
import { ColorGenerator } from '../utils/ColorGenerator';
import { Storage } from '../utils/Storage';
import ShortUrlsRow from '../short-urls/helpers/ShortUrlsRow';
import ShortUrlsRowMenu from '../short-urls/helpers/ShortUrlsRowMenu';
import AsideMenu from '../common/AsideMenu';
import CreateShortUrl from '../short-urls/CreateShortUrl';
import { createShortUrl, resetCreateShortUrl } from '../short-urls/reducers/shortUrlCreation';
import DeleteShortUrlModal from '../short-urls/helpers/DeleteShortUrlModal';
import { deleteShortUrl, resetDeleteShortUrl, shortUrlDeleted } from '../short-urls/reducers/shortUrlDeletion';
import EditTagsModal from '../short-urls/helpers/EditTagsModal';
import { editShortUrlTags, resetShortUrlsTags, shortUrlTagsEdited } from '../short-urls/reducers/shortUrlTags';
import ColorGenerator from '../utils/ColorGenerator';
import Storage from '../utils/Storage';
import buildShlinkApiClient from '../api/ShlinkApiClientBuilder';
import provideShortUrlsServices from '../short-urls/services/provideServices';
import provideServersServices from '../servers/services/provideServices';
import provideVisitsServices from '../visits/services/provideServices';
import provideTagsServices from '../tags/services/provideServices';
@ -54,7 +41,7 @@ bottle.serviceFactory('MainHeader', MainHeader, 'ServersDropdown');
bottle.decorator('MainHeader', withRouter);
bottle.serviceFactory('Home', () => Home);
bottle.decorator('Home', connect([ 'servers' ], { resetSelectedServer }));
bottle.decorator('Home', connect([ 'servers' ], [ 'resetSelectedServer' ]));
bottle.serviceFactory(
'MenuLayout',
@ -77,49 +64,7 @@ bottle.service('ColorGenerator', ColorGenerator, 'Storage');
bottle.constant('axios', axios);
bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'axios');
bottle.serviceFactory('ShortUrls', ShortUrls, 'SearchBar', 'ShortUrlsList');
bottle.decorator('ShortUrls', reduxConnect(
(state) => assoc('shortUrlsList', state.shortUrlsList.shortUrls, state.shortUrlsList)
));
bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator');
bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], { listShortUrls }));
bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsRow');
bottle.decorator('ShortUrlsList', connect(
[ 'selectedServer', 'shortUrlsListParams' ],
[ 'listShortUrls', 'resetShortUrlParams' ]
));
bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator');
bottle.serviceFactory('ShortUrlsRowMenu', ShortUrlsRowMenu, 'DeleteShortUrlModal', 'EditTagsModal');
bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'TagsSelector');
bottle.decorator('CreateShortUrl', connect([ 'shortUrlCreationResult' ], {
createShortUrl,
resetCreateShortUrl,
}));
bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal);
bottle.decorator('DeleteShortUrlModal', connect(
[ 'shortUrlDeletion' ],
{ deleteShortUrl, resetDeleteShortUrl, shortUrlDeleted }
));
bottle.serviceFactory('EditTagsModal', EditTagsModal, 'TagsSelector');
bottle.decorator('EditTagsModal', connect(
[ 'shortUrlTags' ],
[ 'editShortUrlTags', 'resetShortUrlsTags', 'shortUrlTagsEdited' ]
));
bottle.serviceFactory('editShortUrlTags', editShortUrlTags, 'buildShlinkApiClient');
bottle.serviceFactory('resetShortUrlsTags', () => resetShortUrlsTags);
bottle.serviceFactory('shortUrlTagsEdited', () => shortUrlTagsEdited);
bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient');
bottle.serviceFactory('resetShortUrlParams', () => resetShortUrlParams);
provideShortUrlsServices(bottle, connect);
provideServersServices(bottle, connect, withRouter);
provideTagsServices(bottle, connect);
provideVisitsServices(bottle, connect);

View file

@ -23,8 +23,6 @@ export const resetSelectedServer = () => ({ type: RESET_SELECTED_SERVER });
export const selectServer = (serversService) => (serverId) => (dispatch) => {
dispatch(resetShortUrlParams());
console.log('Setting server');
const selectedServer = serversService.findServerById(serverId);
dispatch({

View file

@ -1,6 +1,4 @@
import { dissoc, head, keys, values } from 'ramda';
import csvjson from 'csvjson';
import serversService from './ServersService';
const saveCsv = (window, csv) => {
const { navigator, document } = window;
@ -26,7 +24,7 @@ const saveCsv = (window, csv) => {
document.body.removeChild(link);
};
export class ServersExporter {
export default class ServersExporter {
constructor(serversService, window, csvjson) {
this.serversService = serversService;
this.window = window;
@ -49,7 +47,3 @@ export class ServersExporter {
}
};
}
const serverExporter = new ServersExporter(serversService, global.window, csvjson);
export default serverExporter;

View file

@ -1,11 +1,10 @@
import csvjson from 'csvjson';
import PropTypes from 'prop-types';
export const serversImporterType = PropTypes.shape({
importServersFromFile: PropTypes.func,
});
export class ServersImporter {
export default class ServersImporter {
constructor(csvjson) {
this.csvjson = csvjson;
}
@ -28,7 +27,3 @@ export class ServersImporter {
});
};
}
const serversImporter = new ServersImporter(csvjson);
export default serversImporter;

View file

@ -1,9 +1,8 @@
import { assoc, dissoc, reduce } from 'ramda';
import storage from '../../utils/Storage';
const SERVERS_STORAGE_KEY = 'servers';
export class ServersService {
export default class ServersService {
constructor(storage) {
this.storage = storage;
}
@ -30,7 +29,3 @@ export class ServersService {
dissoc(server.id, this.listServers())
);
}
const serversService = new ServersService(storage);
export default serversService;

View file

@ -6,9 +6,9 @@ import DeleteServerButton from '../DeleteServerButton';
import ImportServersBtn from '../helpers/ImportServersBtn';
import { resetSelectedServer, selectServer } from '../reducers/selectedServer';
import { createServer, createServers, deleteServer, listServers } from '../reducers/server';
import { ServersImporter } from './ServersImporter';
import { ServersService } from './ServersService';
import { ServersExporter } from './ServersExporter';
import ServersImporter from './ServersImporter';
import ServersService from './ServersService';
import ServersExporter from './ServersExporter';
const provideServices = (bottle, connect, withRouter) => {
// Components

View file

@ -1,6 +1,4 @@
import { curry } from 'ramda';
import PropTypes from 'prop-types';
import { buildShlinkApiClientWithAxios as buildShlinkApiClient } from '../../api/ShlinkApiClientBuilder';
/* eslint-disable padding-line-between-statements, newline-after-var */
export const CREATE_SHORT_URL_START = 'shlink/createShortUrl/CREATE_SHORT_URL_START';
@ -50,7 +48,7 @@ export default function reducer(state = defaultState, action) {
}
}
export const _createShortUrl = (buildShlinkApiClient, data) => async (dispatch, getState) => {
export const createShortUrl = (buildShlinkApiClient) => (data) => async (dispatch, getState) => {
dispatch({ type: CREATE_SHORT_URL_START });
const { selectedServer } = getState();
@ -65,6 +63,4 @@ export const _createShortUrl = (buildShlinkApiClient, data) => async (dispatch,
}
};
export const createShortUrl = curry(_createShortUrl)(buildShlinkApiClient);
export const resetCreateShortUrl = () => ({ type: RESET_CREATE_SHORT_URL });

View file

@ -1,6 +1,4 @@
import { curry } from 'ramda';
import PropTypes from 'prop-types';
import { buildShlinkApiClientWithAxios as buildShlinkApiClient } from '../../api/ShlinkApiClientBuilder';
/* eslint-disable padding-line-between-statements, newline-after-var */
const DELETE_SHORT_URL_START = 'shlink/deleteShortUrl/DELETE_SHORT_URL_START';
@ -56,7 +54,7 @@ export default function reducer(state = defaultState, action) {
}
}
export const _deleteShortUrl = (buildShlinkApiClient, shortCode) => async (dispatch, getState) => {
export const deleteShortUrl = (buildShlinkApiClient) => (shortCode) => async (dispatch, getState) => {
dispatch({ type: DELETE_SHORT_URL_START });
const { selectedServer } = getState();
@ -72,8 +70,6 @@ export const _deleteShortUrl = (buildShlinkApiClient, shortCode) => async (dispa
}
};
export const deleteShortUrl = curry(_deleteShortUrl)(buildShlinkApiClient);
export const resetDeleteShortUrl = () => ({ type: RESET_DELETE_SHORT_URL });
export const shortUrlDeleted = (shortCode) => ({ type: SHORT_URL_DELETED, shortCode });

View file

@ -0,0 +1,71 @@
import { connect as reduxConnect } from 'react-redux';
import { assoc } from 'ramda';
import ShortUrls from '../ShortUrls';
import SearchBar from '../SearchBar';
import ShortUrlsList from '../ShortUrlsList';
import ShortUrlsRow from '../helpers/ShortUrlsRow';
import ShortUrlsRowMenu from '../helpers/ShortUrlsRowMenu';
import CreateShortUrl from '../CreateShortUrl';
import DeleteShortUrlModal from '../helpers/DeleteShortUrlModal';
import EditTagsModal from '../helpers/EditTagsModal';
import { listShortUrls } from '../reducers/shortUrlsList';
import { createShortUrl, resetCreateShortUrl } from '../reducers/shortUrlCreation';
import { deleteShortUrl, resetDeleteShortUrl, shortUrlDeleted } from '../reducers/shortUrlDeletion';
import { editShortUrlTags, resetShortUrlsTags, shortUrlTagsEdited } from '../reducers/shortUrlTags';
import { resetShortUrlParams } from '../reducers/shortUrlsListParams';
const provideServices = (bottle, connect) => {
// Components
bottle.serviceFactory('ShortUrls', ShortUrls, 'SearchBar', 'ShortUrlsList');
bottle.decorator('ShortUrls', reduxConnect(
(state) => assoc('shortUrlsList', state.shortUrlsList.shortUrls, state.shortUrlsList)
));
bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator');
bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], [ 'listShortUrls' ]));
bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsRow');
bottle.decorator('ShortUrlsList', connect(
[ 'selectedServer', 'shortUrlsListParams' ],
[ 'listShortUrls', 'resetShortUrlParams' ]
));
bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator');
bottle.serviceFactory('ShortUrlsRowMenu', ShortUrlsRowMenu, 'DeleteShortUrlModal', 'EditTagsModal');
bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'TagsSelector');
bottle.decorator(
'CreateShortUrl',
connect([ 'shortUrlCreationResult' ], [ 'createShortUrl', 'resetCreateShortUrl' ])
);
bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal);
bottle.decorator('DeleteShortUrlModal', connect(
[ 'shortUrlDeletion' ],
[ 'deleteShortUrl', 'resetDeleteShortUrl', 'shortUrlDeleted' ]
));
bottle.serviceFactory('EditTagsModal', EditTagsModal, 'TagsSelector');
bottle.decorator('EditTagsModal', connect(
[ 'shortUrlTags' ],
[ 'editShortUrlTags', 'resetShortUrlsTags', 'shortUrlTagsEdited' ]
));
// Actions
bottle.serviceFactory('editShortUrlTags', editShortUrlTags, 'buildShlinkApiClient');
bottle.serviceFactory('resetShortUrlsTags', () => resetShortUrlsTags);
bottle.serviceFactory('shortUrlTagsEdited', () => shortUrlTagsEdited);
bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient');
bottle.serviceFactory('resetShortUrlParams', () => resetShortUrlParams);
bottle.serviceFactory('createShortUrl', createShortUrl, 'buildShlinkApiClient');
bottle.serviceFactory('resetCreateShortUrl', () => resetCreateShortUrl);
bottle.serviceFactory('deleteShortUrl', deleteShortUrl, 'buildShlinkApiClient');
bottle.serviceFactory('resetDeleteShortUrl', () => resetDeleteShortUrl);
bottle.serviceFactory('shortUrlDeleted', () => shortUrlDeleted);
};
export default provideServices;

View file

@ -1,6 +1,5 @@
import { range } from 'ramda';
import PropTypes from 'prop-types';
import storage from './Storage';
const HEX_COLOR_LENGTH = 6;
const { floor, random } = Math;
@ -13,7 +12,7 @@ const buildRandomColor = () =>
}`;
const normalizeKey = (key) => key.toLowerCase().trim();
export class ColorGenerator {
export default class ColorGenerator {
constructor(storage) {
this.storage = storage;
this.colors = this.storage.get('colors') || {};
@ -45,7 +44,3 @@ export const colorGeneratorType = PropTypes.shape({
getColorForKey: PropTypes.func,
setColorForKey: PropTypes.func,
});
const colorGenerator = new ColorGenerator(storage);
export default colorGenerator;

View file

@ -1,7 +1,7 @@
const PREFIX = 'shlink';
const buildPath = (path) => `${PREFIX}.${path}`;
export class Storage {
export default class Storage {
constructor(localStorage) {
this.localStorage = localStorage;
}
@ -14,15 +14,3 @@ export class Storage {
set = (key, value) => this.localStorage.setItem(buildPath(key), JSON.stringify(value));
}
const browserStorage = global.localStorage || {
getItem() {
return '';
},
setItem() {
return '';
},
};
const storage = new Storage(browserStorage);
export default storage;

View file

@ -1,5 +1,5 @@
import sinon from 'sinon';
import { ServersExporter } from '../../../src/servers/services/ServersExporter';
import ServersExporter from '../../../src/servers/services/ServersExporter';
describe('ServersExporter', () => {
const createLinkMock = () => ({

View file

@ -1,5 +1,5 @@
import sinon from 'sinon';
import { ServersImporter } from '../../../src/servers/services/ServersImporter';
import ServersImporter from '../../../src/servers/services/ServersImporter';
describe('ServersImporter', () => {
const servers = [{ name: 'foo' }, { name: 'bar' }];

View file

@ -1,6 +1,6 @@
import sinon from 'sinon';
import { last } from 'ramda';
import { ServersService } from '../../../src/servers/services/ServersService';
import ServersService from '../../../src/servers/services/ServersService';
describe('ServersService', () => {
const servers = {

View file

@ -4,7 +4,7 @@ import reducer, {
CREATE_SHORT_URL_ERROR,
CREATE_SHORT_URL,
RESET_CREATE_SHORT_URL,
_createShortUrl,
createShortUrl,
resetCreateShortUrl,
} from '../../../src/short-urls/reducers/shortUrlCreation';
@ -62,7 +62,7 @@ describe('shortUrlCreationReducer', () => {
const expectedDispatchCalls = 2;
const result = 'foo';
const apiClientMock = createApiClientMock(Promise.resolve(result));
const dispatchable = _createShortUrl(() => apiClientMock, {});
const dispatchable = createShortUrl(() => apiClientMock)({});
await dispatchable(dispatch, getState);
@ -77,7 +77,7 @@ describe('shortUrlCreationReducer', () => {
const expectedDispatchCalls = 2;
const error = 'Error';
const apiClientMock = createApiClientMock(Promise.reject(error));
const dispatchable = _createShortUrl(() => apiClientMock, {});
const dispatchable = createShortUrl(() => apiClientMock)({});
try {
await dispatchable(dispatch, getState);

View file

@ -1,5 +1,5 @@
import * as sinon from 'sinon';
import { ColorGenerator } from '../../src/utils/ColorGenerator';
import ColorGenerator from '../../src/utils/ColorGenerator';
describe('ColorGenerator', () => {
let colorGenerator;