From 566322a8c531cf50a1f53e46d0786c3d17f2ee08 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 18 Dec 2018 14:54:54 +0100 Subject: [PATCH] Extracted tag related services to its own service provider --- src/container/index.js | 62 +++++++------------ src/tags/reducers/tagDelete.js | 6 +- src/tags/reducers/tagEdit.js | 8 +-- src/tags/services/provideServices.js | 37 +++++++++++ .../provideServices.js | 2 +- test/tags/reducers/tagDelete.test.js | 6 +- test/tags/reducers/tagEdit.test.js | 6 +- 7 files changed, 68 insertions(+), 59 deletions(-) create mode 100644 src/tags/services/provideServices.js rename src/visits/{container => services}/provideServices.js (92%) diff --git a/src/container/index.js b/src/container/index.js index 4aba91ea..70ba82dc 100644 --- a/src/container/index.js +++ b/src/container/index.js @@ -1,6 +1,6 @@ import Bottle from 'bottlejs'; import { withRouter } from 'react-router-dom'; -import { connect } from 'react-redux'; +import { connect as reduxConnect } from 'react-redux'; import { assoc, pick } from 'ramda'; import csvjson from 'csvjson'; import axios from 'axios'; @@ -13,8 +13,6 @@ import MenuLayout from '../common/MenuLayout'; import { createServer, createServers, deleteServer, listServers } from '../servers/reducers/server'; import CreateServer from '../servers/CreateServer'; import ServersDropdown from '../servers/ServersDropdown'; -import TagsList from '../tags/TagsList'; -import { filterTags, forceListTags, listTags } from '../tags/reducers/tagsList'; import ShortUrls from '../short-urls/ShortUrls'; import SearchBar from '../short-urls/SearchBar'; import { listShortUrls } from '../short-urls/reducers/shortUrlsList'; @@ -34,18 +32,13 @@ import { ServersExporter } from '../servers/services/ServersExporter'; import { ServersService } from '../servers/services/ServersService'; import CreateShortUrl from '../short-urls/CreateShortUrl'; import { createShortUrl, resetCreateShortUrl } from '../short-urls/reducers/shortUrlCreation'; -import TagsSelector from '../tags/helpers/TagsSelector'; 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 buildShlinkApiClient from '../api/ShlinkApiClientBuilder'; -import TagCard from '../tags/TagCard'; -import DeleteTagConfirmModal from '../tags/helpers/DeleteTagConfirmModal'; -import { deleteTag, tagDeleted } from '../tags/reducers/tagDelete'; -import EditTagModal from '../tags/helpers/EditTagModal'; -import { editTag, tagEdited } from '../tags/reducers/tagEdit'; -import provideVisitsServices from '../visits/container/provideServices'; +import provideVisitsServices from '../visits/services/provideServices'; +import provideTagsServices from '../tags/services/provideServices'; const bottle = new Bottle(); const { container } = bottle; @@ -56,8 +49,8 @@ const mapActionService = (map, actionName) => ({ // Wrap actual action service in a function so that it is lazily created the first time it is called [actionName]: (...args) => container[actionName](...args), }); -const connectDecorator = (propsFromState, actionServiceNames) => - connect( +const connect = (propsFromState, actionServiceNames) => + reduxConnect( pick(propsFromState), Array.isArray(actionServiceNames) ? actionServiceNames.reduce(mapActionService, {}) : actionServiceNames ); @@ -69,7 +62,7 @@ bottle.serviceFactory('MainHeader', MainHeader, 'ServersDropdown'); bottle.decorator('MainHeader', withRouter); bottle.serviceFactory('Home', () => Home); -bottle.decorator('Home', connectDecorator([ 'servers' ], { resetSelectedServer })); +bottle.decorator('Home', connect([ 'servers' ], { resetSelectedServer })); bottle.serviceFactory( 'MenuLayout', @@ -80,30 +73,27 @@ bottle.serviceFactory( 'CreateShortUrl', 'ShortUrlVisits' ); -bottle.decorator('MenuLayout', connectDecorator([ 'selectedServer', 'shortUrlsListParams' ], { selectServer })); +bottle.decorator('MenuLayout', connect([ 'selectedServer', 'shortUrlsListParams' ], { selectServer })); bottle.decorator('MenuLayout', withRouter); bottle.serviceFactory('CreateServer', CreateServer, 'ImportServersBtn'); -bottle.decorator('CreateServer', connectDecorator([ 'selectedServer' ], { createServer, resetSelectedServer })); +bottle.decorator('CreateServer', connect([ 'selectedServer' ], { createServer, resetSelectedServer })); bottle.serviceFactory('App', App, 'MainHeader', 'Home', 'MenuLayout', 'CreateServer'); bottle.serviceFactory('ServersDropdown', ServersDropdown, 'ServersExporter'); -bottle.decorator('ServersDropdown', connectDecorator([ 'servers', 'selectedServer' ], { listServers, selectServer })); - -bottle.serviceFactory('TagsList', TagsList, 'TagCard'); -bottle.decorator('TagsList', connectDecorator([ 'tagsList' ], { forceListTags, filterTags })); +bottle.decorator('ServersDropdown', connect([ 'servers', 'selectedServer' ], { listServers, selectServer })); bottle.serviceFactory('ShortUrls', ShortUrls, 'SearchBar', 'ShortUrlsList'); -bottle.decorator('ShortUrls', connect( +bottle.decorator('ShortUrls', reduxConnect( (state) => assoc('shortUrlsList', state.shortUrlsList.shortUrls, state.shortUrlsList) )); bottle.serviceFactory('SearchBar', SearchBar, 'ColorGenerator'); -bottle.decorator('SearchBar', connectDecorator([ 'shortUrlsListParams' ], { listShortUrls })); +bottle.decorator('SearchBar', connect([ 'shortUrlsListParams' ], { listShortUrls })); bottle.serviceFactory('ShortUrlsList', ShortUrlsList, 'ShortUrlsRow'); -bottle.decorator('ShortUrlsList', connectDecorator( +bottle.decorator('ShortUrlsList', connect( [ 'selectedServer', 'shortUrlsListParams' ], { listShortUrls, resetShortUrlParams } )); @@ -112,6 +102,8 @@ bottle.constant('localStorage', global.localStorage); bottle.service('Storage', Storage, 'localStorage'); bottle.service('ColorGenerator', ColorGenerator, 'Storage'); +bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'axios'); + bottle.serviceFactory('ShortUrlsRow', ShortUrlsRow, 'ShortUrlsRowMenu', 'ColorGenerator'); bottle.serviceFactory('ShortUrlsRowMenu', ShortUrlsRowMenu, 'DeleteShortUrlModal', 'EditTagsModal'); @@ -121,13 +113,13 @@ bottle.service('ShlinkApiClient', ShlinkApiClient, 'axios'); bottle.serviceFactory('DeleteServerModal', () => DeleteServerModal); bottle.decorator('DeleteServerModal', withRouter); -bottle.decorator('DeleteServerModal', connect(null, { deleteServer })); +bottle.decorator('DeleteServerModal', reduxConnect(null, { deleteServer })); bottle.serviceFactory('DeleteServerButton', DeleteServerButton, 'DeleteServerModal'); bottle.serviceFactory('AsideMenu', AsideMenu, 'DeleteServerButton'); bottle.serviceFactory('ImportServersBtn', ImportServersBtn, 'ServersImporter'); -bottle.decorator('ImportServersBtn', connect(null, { createServers })); +bottle.decorator('ImportServersBtn', reduxConnect(null, { createServers })); bottle.constant('csvjson', csvjson); bottle.constant('window', global.window); @@ -136,22 +128,19 @@ bottle.service('ServersService', ServersService, 'Storage'); bottle.service('ServersExporter', ServersExporter, 'ServersService', 'window', 'csvjson'); bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'TagsSelector'); -bottle.decorator('CreateShortUrl', connectDecorator([ 'shortUrlCreationResult' ], { +bottle.decorator('CreateShortUrl', connect([ 'shortUrlCreationResult' ], { createShortUrl, resetCreateShortUrl, })); -bottle.serviceFactory('TagsSelector', TagsSelector, 'ColorGenerator'); -bottle.decorator('TagsSelector', connectDecorator([ 'tagsList' ], { listTags })); - bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal); -bottle.decorator('DeleteShortUrlModal', connectDecorator( +bottle.decorator('DeleteShortUrlModal', connect( [ 'shortUrlDeletion' ], { deleteShortUrl, resetDeleteShortUrl, shortUrlDeleted } )); bottle.serviceFactory('EditTagsModal', EditTagsModal, 'TagsSelector'); -bottle.decorator('EditTagsModal', connectDecorator( +bottle.decorator('EditTagsModal', connect( [ 'shortUrlTags' ], [ 'editShortUrlTags', 'resetShortUrlsTags', 'shortUrlTagsEdited' ] )); @@ -160,16 +149,7 @@ bottle.serviceFactory('editShortUrlTags', editShortUrlTags, 'buildShlinkApiClien bottle.serviceFactory('resetShortUrlsTags', () => resetShortUrlsTags); bottle.serviceFactory('shortUrlTagsEdited', () => shortUrlTagsEdited); -bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'axios'); - -bottle.serviceFactory('TagCard', TagCard, 'DeleteTagConfirmModal', 'EditTagModal', 'ColorGenerator'); - -bottle.serviceFactory('DeleteTagConfirmModal', () => DeleteTagConfirmModal); -bottle.decorator('DeleteTagConfirmModal', connectDecorator([ 'tagDelete' ], { deleteTag, tagDeleted })); - -bottle.serviceFactory('EditTagModal', EditTagModal, 'ColorGenerator'); -bottle.decorator('EditTagModal', connectDecorator([ 'tagEdit' ], { editTag, tagEdited })); - -provideVisitsServices(bottle, connectDecorator); +provideTagsServices(bottle, connect); +provideVisitsServices(bottle, connect); export default container; diff --git a/src/tags/reducers/tagDelete.js b/src/tags/reducers/tagDelete.js index e4ea9410..03c42947 100644 --- a/src/tags/reducers/tagDelete.js +++ b/src/tags/reducers/tagDelete.js @@ -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 DELETE_TAG_START = 'shlink/deleteTag/DELETE_TAG_START'; @@ -41,7 +39,7 @@ export default function reducer(state = defaultState, action) { } } -export const _deleteTag = (buildShlinkApiClient, tag) => async (dispatch, getState) => { +export const deleteTag = (buildShlinkApiClient) => (tag) => async (dispatch, getState) => { dispatch({ type: DELETE_TAG_START }); const { selectedServer } = getState(); @@ -57,6 +55,4 @@ export const _deleteTag = (buildShlinkApiClient, tag) => async (dispatch, getSta } }; -export const deleteTag = curry(_deleteTag)(buildShlinkApiClient); - export const tagDeleted = (tag) => ({ type: TAG_DELETED, tag }); diff --git a/src/tags/reducers/tagEdit.js b/src/tags/reducers/tagEdit.js index 53bb25c4..950e95db 100644 --- a/src/tags/reducers/tagEdit.js +++ b/src/tags/reducers/tagEdit.js @@ -1,6 +1,4 @@ -import { curry, pick } from 'ramda'; -import { buildShlinkApiClientWithAxios as buildShlinkApiClient } from '../../api/ShlinkApiClientBuilder'; -import colorGenerator from '../../utils/ColorGenerator'; +import { pick } from 'ramda'; /* eslint-disable padding-line-between-statements, newline-after-var */ export const EDIT_TAG_START = 'shlink/editTag/EDIT_TAG_START'; @@ -42,7 +40,7 @@ export default function reducer(state = defaultState, action) { } } -export const _editTag = (buildShlinkApiClient, colorGenerator, oldName, newName, color) => async ( +export const editTag = (buildShlinkApiClient, colorGenerator) => (oldName, newName, color) => async ( dispatch, getState ) => { @@ -62,8 +60,6 @@ export const _editTag = (buildShlinkApiClient, colorGenerator, oldName, newName, } }; -export const editTag = curry(_editTag)(buildShlinkApiClient, colorGenerator); - export const tagEdited = (oldName, newName, color) => ({ type: TAG_EDITED, oldName, diff --git a/src/tags/services/provideServices.js b/src/tags/services/provideServices.js new file mode 100644 index 00000000..ba7c7be9 --- /dev/null +++ b/src/tags/services/provideServices.js @@ -0,0 +1,37 @@ +import TagsSelector from '../helpers/TagsSelector'; +import TagCard from '../TagCard'; +import DeleteTagConfirmModal from '../helpers/DeleteTagConfirmModal'; +import EditTagModal from '../helpers/EditTagModal'; +import TagsList from '../TagsList'; +import { filterTags, forceListTags, listTags } from '../reducers/tagsList'; +import { deleteTag, tagDeleted } from '../reducers/tagDelete'; +import { editTag, tagEdited } from '../reducers/tagEdit'; + +const provideServices = (bottle, connect) => { + // Components + bottle.serviceFactory('TagsSelector', TagsSelector, 'ColorGenerator'); + bottle.decorator('TagsSelector', connect([ 'tagsList' ], [ 'listTags' ])); + + bottle.serviceFactory('TagCard', TagCard, 'DeleteTagConfirmModal', 'EditTagModal', 'ColorGenerator'); + + bottle.serviceFactory('DeleteTagConfirmModal', () => DeleteTagConfirmModal); + bottle.decorator('DeleteTagConfirmModal', connect([ 'tagDelete' ], [ 'deleteTag', 'tagDeleted' ])); + + bottle.serviceFactory('EditTagModal', EditTagModal, 'ColorGenerator'); + bottle.decorator('EditTagModal', connect([ 'tagEdit' ], [ 'editTag', 'tagEdited' ])); + + bottle.serviceFactory('TagsList', TagsList, 'TagCard'); + bottle.decorator('TagsList', connect([ 'tagsList' ], [ 'forceListTags', 'filterTags' ])); + + // Actions + bottle.serviceFactory('filterTags', () => filterTags); + bottle.serviceFactory('forceListTags', () => forceListTags); + bottle.serviceFactory('listTags', () => listTags); + bottle.serviceFactory('tagDeleted', () => tagDeleted); + bottle.serviceFactory('tagEdited', () => tagEdited); + + bottle.serviceFactory('deleteTag', deleteTag, 'buildShlinkApiClient'); + bottle.serviceFactory('editTag', editTag, 'buildShlinkApiClient', 'ColorGenerator'); +}; + +export default provideServices; diff --git a/src/visits/container/provideServices.js b/src/visits/services/provideServices.js similarity index 92% rename from src/visits/container/provideServices.js rename to src/visits/services/provideServices.js index a7aec43f..0b754868 100644 --- a/src/visits/container/provideServices.js +++ b/src/visits/services/provideServices.js @@ -1,7 +1,7 @@ import ShortUrlVisits from '../ShortUrlVisits'; import { getShortUrlVisits } from '../reducers/shortUrlVisits'; import { getShortUrlDetail } from '../reducers/shortUrlDetail'; -import * as visitsParser from '../services/VisitsParser'; +import * as visitsParser from './VisitsParser'; const provideServices = (bottle, connect) => { // Components diff --git a/test/tags/reducers/tagDelete.test.js b/test/tags/reducers/tagDelete.test.js index 26450c9e..b3cc8c8e 100644 --- a/test/tags/reducers/tagDelete.test.js +++ b/test/tags/reducers/tagDelete.test.js @@ -5,7 +5,7 @@ import reducer, { DELETE_TAG, TAG_DELETED, tagDeleted, - _deleteTag, + deleteTag, } from '../../../src/tags/reducers/tagDelete'; describe('tagDeleteReducer', () => { @@ -56,7 +56,7 @@ describe('tagDeleteReducer', () => { const expectedDispatchCalls = 2; const tag = 'foo'; const apiClientMock = createApiClientMock(Promise.resolve()); - const dispatchable = _deleteTag(() => apiClientMock, tag); + const dispatchable = deleteTag(() => apiClientMock)(tag); await dispatchable(dispatch, getState); @@ -73,7 +73,7 @@ describe('tagDeleteReducer', () => { const error = 'Error'; const tag = 'foo'; const apiClientMock = createApiClientMock(Promise.reject(error)); - const dispatchable = _deleteTag(() => apiClientMock, tag); + const dispatchable = deleteTag(() => apiClientMock)(tag); try { await dispatchable(dispatch, getState); diff --git a/test/tags/reducers/tagEdit.test.js b/test/tags/reducers/tagEdit.test.js index ed97cf67..91642c06 100644 --- a/test/tags/reducers/tagEdit.test.js +++ b/test/tags/reducers/tagEdit.test.js @@ -5,7 +5,7 @@ import reducer, { EDIT_TAG, TAG_EDITED, tagEdited, - _editTag, + editTag, } from '../../../src/tags/reducers/tagEdit'; describe('tagEditReducer', () => { @@ -68,7 +68,7 @@ describe('tagEditReducer', () => { const newName = 'bar'; const color = '#ff0000'; const apiClientMock = createApiClientMock(Promise.resolve()); - const dispatchable = _editTag(() => apiClientMock, colorGenerator, oldName, newName, color); + const dispatchable = editTag(() => apiClientMock, colorGenerator)(oldName, newName, color); await dispatchable(dispatch, getState); @@ -90,7 +90,7 @@ describe('tagEditReducer', () => { const newName = 'bar'; const color = '#ff0000'; const apiClientMock = createApiClientMock(Promise.reject(error)); - const dispatchable = _editTag(() => apiClientMock, colorGenerator, oldName, newName, color); + const dispatchable = editTag(() => apiClientMock, colorGenerator)(oldName, newName, color); try { await dispatchable(dispatch, getState);