From 54290d4c9acc0facc84fbd6a333298b46a72f3b1 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 27 Aug 2020 22:09:16 +0200 Subject: [PATCH] Migrated ShlinkApiClientBuilder to TS --- src/mercure/reducers/mercureInfo.ts | 3 +- src/servers/data/index.ts | 5 ++- src/servers/reducers/selectedServer.ts | 3 +- src/short-urls/reducers/shortUrlCreation.ts | 2 +- src/short-urls/reducers/shortUrlDeletion.ts | 3 +- src/short-urls/reducers/shortUrlEdition.ts | 2 +- src/short-urls/reducers/shortUrlMeta.ts | 2 +- src/short-urls/reducers/shortUrlTags.ts | 2 +- src/short-urls/reducers/shortUrlsList.ts | 2 +- src/tags/reducers/tagDelete.ts | 2 +- src/tags/reducers/tagEdit.ts | 2 +- src/tags/reducers/tagsList.ts | 3 +- src/utils/services/ShlinkApiClientBuilder.js | 24 ------------- src/utils/services/ShlinkApiClientBuilder.ts | 36 +++++++++++++++++++ src/utils/services/types.ts | 7 ---- ...test.js => ShlinkApiClientBuilder.test.ts} | 23 +++++++----- 16 files changed, 70 insertions(+), 51 deletions(-) delete mode 100644 src/utils/services/ShlinkApiClientBuilder.js create mode 100644 src/utils/services/ShlinkApiClientBuilder.ts rename test/utils/services/{ShlinkApiClientBuilder.test.js => ShlinkApiClientBuilder.test.ts} (60%) diff --git a/src/mercure/reducers/mercureInfo.ts b/src/mercure/reducers/mercureInfo.ts index 396506c7..582f76b7 100644 --- a/src/mercure/reducers/mercureInfo.ts +++ b/src/mercure/reducers/mercureInfo.ts @@ -1,8 +1,9 @@ import PropTypes from 'prop-types'; import { Action, Dispatch } from 'redux'; -import { ShlinkApiClientBuilder, ShlinkMercureInfo } from '../../utils/services/types'; +import { ShlinkMercureInfo } from '../../utils/services/types'; import { GetState } from '../../container/types'; import { buildReducer } from '../../utils/helpers/redux'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const GET_MERCURE_INFO_START = 'shlink/mercure/GET_MERCURE_INFO_START'; diff --git a/src/servers/data/index.ts b/src/servers/data/index.ts index 222b418e..57dbf52a 100644 --- a/src/servers/data/index.ts +++ b/src/servers/data/index.ts @@ -4,7 +4,7 @@ export interface ServerData { apiKey: string; } -export interface ServerWithId { +export interface ServerWithId extends ServerData { id: string; } @@ -24,3 +24,6 @@ export interface NotFoundServer { export type RegularServer = ReachableServer | NonReachableServer; export type SelectedServer = RegularServer | NotFoundServer | null; + +export const hasServerData = (server: ServerData | NotFoundServer | null): server is ServerData => + !!(server as ServerData)?.url && !!(server as ServerData)?.apiKey; diff --git a/src/servers/reducers/selectedServer.ts b/src/servers/reducers/selectedServer.ts index cf0583fd..3579a00e 100644 --- a/src/servers/reducers/selectedServer.ts +++ b/src/servers/reducers/selectedServer.ts @@ -4,8 +4,9 @@ import { resetShortUrlParams } from '../../short-urls/reducers/shortUrlsListPara import { versionToPrintable, versionToSemVer as toSemVer } from '../../utils/helpers/version'; import { SelectedServer } from '../data'; import { GetState } from '../../container/types'; -import { ShlinkApiClientBuilder, ShlinkHealth } from '../../utils/services/types'; +import { ShlinkHealth } from '../../utils/services/types'; import { buildActionCreator, buildReducer } from '../../utils/helpers/redux'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const SELECT_SERVER = 'shlink/selectedServer/SELECT_SERVER'; diff --git a/src/short-urls/reducers/shortUrlCreation.ts b/src/short-urls/reducers/shortUrlCreation.ts index 40753d4b..f062b694 100644 --- a/src/short-urls/reducers/shortUrlCreation.ts +++ b/src/short-urls/reducers/shortUrlCreation.ts @@ -1,9 +1,9 @@ import PropTypes from 'prop-types'; import { Action, Dispatch } from 'redux'; -import { ShlinkApiClientBuilder } from '../../utils/services/types'; import { GetState } from '../../container/types'; import { ShortUrl, ShortUrlData } from '../data'; import { buildReducer, buildActionCreator } from '../../utils/helpers/redux'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const CREATE_SHORT_URL_START = 'shlink/createShortUrl/CREATE_SHORT_URL_START'; diff --git a/src/short-urls/reducers/shortUrlDeletion.ts b/src/short-urls/reducers/shortUrlDeletion.ts index 316bd4b8..21347852 100644 --- a/src/short-urls/reducers/shortUrlDeletion.ts +++ b/src/short-urls/reducers/shortUrlDeletion.ts @@ -1,7 +1,8 @@ import { Action, Dispatch } from 'redux'; import { buildActionCreator, buildReducer } from '../../utils/helpers/redux'; -import { ProblemDetailsError, ShlinkApiClientBuilder } from '../../utils/services/types'; +import { ProblemDetailsError} from '../../utils/services/types'; import { GetState } from '../../container/types'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const DELETE_SHORT_URL_START = 'shlink/deleteShortUrl/DELETE_SHORT_URL_START'; diff --git a/src/short-urls/reducers/shortUrlEdition.ts b/src/short-urls/reducers/shortUrlEdition.ts index 6179f98c..44feac7b 100644 --- a/src/short-urls/reducers/shortUrlEdition.ts +++ b/src/short-urls/reducers/shortUrlEdition.ts @@ -1,9 +1,9 @@ import { Action, Dispatch } from 'redux'; import { buildReducer } from '../../utils/helpers/redux'; -import { ShlinkApiClientBuilder } from '../../utils/services/types'; import { GetState } from '../../container/types'; import { OptionalString } from '../../utils/utils'; import { ShortUrlIdentifier } from '../data'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const EDIT_SHORT_URL_START = 'shlink/shortUrlEdition/EDIT_SHORT_URL_START'; diff --git a/src/short-urls/reducers/shortUrlMeta.ts b/src/short-urls/reducers/shortUrlMeta.ts index ec0a2bca..71736ea0 100644 --- a/src/short-urls/reducers/shortUrlMeta.ts +++ b/src/short-urls/reducers/shortUrlMeta.ts @@ -1,10 +1,10 @@ import PropTypes from 'prop-types'; import { Dispatch, Action } from 'redux'; import { ShortUrlIdentifier, ShortUrlMeta } from '../data'; -import { ShlinkApiClientBuilder } from '../../utils/services/types'; import { GetState } from '../../container/types'; import { buildActionCreator, buildReducer } from '../../utils/helpers/redux'; import { OptionalString } from '../../utils/utils'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const EDIT_SHORT_URL_META_START = 'shlink/shortUrlMeta/EDIT_SHORT_URL_META_START'; diff --git a/src/short-urls/reducers/shortUrlTags.ts b/src/short-urls/reducers/shortUrlTags.ts index 823499aa..cdaf9269 100644 --- a/src/short-urls/reducers/shortUrlTags.ts +++ b/src/short-urls/reducers/shortUrlTags.ts @@ -1,10 +1,10 @@ import PropTypes from 'prop-types'; import { Action, Dispatch } from 'redux'; import { buildActionCreator, buildReducer } from '../../utils/helpers/redux'; -import { ShlinkApiClientBuilder } from '../../utils/services/types'; import { GetState } from '../../container/types'; import { OptionalString } from '../../utils/utils'; import { ShortUrlIdentifier } from '../data'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const EDIT_SHORT_URL_TAGS_START = 'shlink/shortUrlTags/EDIT_SHORT_URL_TAGS_START'; diff --git a/src/short-urls/reducers/shortUrlsList.ts b/src/short-urls/reducers/shortUrlsList.ts index 3e54ea6e..6294dd17 100644 --- a/src/short-urls/reducers/shortUrlsList.ts +++ b/src/short-urls/reducers/shortUrlsList.ts @@ -6,12 +6,12 @@ import { CREATE_VISIT, CreateVisitAction } from '../../visits/reducers/visitCrea import { ShortUrl, ShortUrlIdentifier } from '../data'; import { buildReducer } from '../../utils/helpers/redux'; import { GetState } from '../../container/types'; -import { ShlinkApiClientBuilder } from '../../utils/services/types'; import { EditShortUrlTagsAction, SHORT_URL_TAGS_EDITED } from './shortUrlTags'; import { SHORT_URL_DELETED } from './shortUrlDeletion'; import { SHORT_URL_META_EDITED, ShortUrlMetaEditedAction, shortUrlMetaType } from './shortUrlMeta'; import { SHORT_URL_EDITED, ShortUrlEditedAction } from './shortUrlEdition'; import { ShortUrlsListParams } from './shortUrlsListParams'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const LIST_SHORT_URLS_START = 'shlink/shortUrlsList/LIST_SHORT_URLS_START'; diff --git a/src/tags/reducers/tagDelete.ts b/src/tags/reducers/tagDelete.ts index 8fa85414..e662a179 100644 --- a/src/tags/reducers/tagDelete.ts +++ b/src/tags/reducers/tagDelete.ts @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import { Action, Dispatch } from 'redux'; import { buildReducer } from '../../utils/helpers/redux'; import { GetState } from '../../container/types'; -import { ShlinkApiClientBuilder } from '../../utils/services/types'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const DELETE_TAG_START = 'shlink/deleteTag/DELETE_TAG_START'; diff --git a/src/tags/reducers/tagEdit.ts b/src/tags/reducers/tagEdit.ts index b3e631c8..44825200 100644 --- a/src/tags/reducers/tagEdit.ts +++ b/src/tags/reducers/tagEdit.ts @@ -2,8 +2,8 @@ import { pick } from 'ramda'; import { Action, Dispatch } from 'redux'; import { buildReducer } from '../../utils/helpers/redux'; import { GetState } from '../../container/types'; -import { ShlinkApiClientBuilder } from '../../utils/services/types'; import ColorGenerator from '../../utils/services/ColorGenerator'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const EDIT_TAG_START = 'shlink/editTag/EDIT_TAG_START'; diff --git a/src/tags/reducers/tagsList.ts b/src/tags/reducers/tagsList.ts index 207e64a8..2e4cf1c0 100644 --- a/src/tags/reducers/tagsList.ts +++ b/src/tags/reducers/tagsList.ts @@ -3,10 +3,11 @@ import PropTypes from 'prop-types'; import { Action, Dispatch } from 'redux'; import { CREATE_VISIT, CreateVisitAction } from '../../visits/reducers/visitCreation'; import { buildReducer } from '../../utils/helpers/redux'; -import { ShlinkApiClientBuilder, ShlinkTags } from '../../utils/services/types'; +import { ShlinkTags } from '../../utils/services/types'; import { GetState } from '../../container/types'; import { DeleteTagAction, TAG_DELETED } from './tagDelete'; import { EditTagAction, TAG_EDITED } from './tagEdit'; +import { ShlinkApiClientBuilder } from '../../utils/services/ShlinkApiClientBuilder'; /* eslint-disable padding-line-between-statements */ export const LIST_TAGS_START = 'shlink/tagsList/LIST_TAGS_START'; diff --git a/src/utils/services/ShlinkApiClientBuilder.js b/src/utils/services/ShlinkApiClientBuilder.js deleted file mode 100644 index a436f698..00000000 --- a/src/utils/services/ShlinkApiClientBuilder.js +++ /dev/null @@ -1,24 +0,0 @@ -import ShlinkApiClient from './ShlinkApiClient'; - -const apiClients = {}; - -const getSelectedServerFromState = (getState) => { - const { selectedServer } = getState(); - - return selectedServer; -}; - -const buildShlinkApiClient = (axios) => (getStateOrSelectedServer) => { - const { url, apiKey } = typeof getStateOrSelectedServer === 'function' - ? getSelectedServerFromState(getStateOrSelectedServer) - : getStateOrSelectedServer; - const clientKey = `${url}_${apiKey}`; - - if (!apiClients[clientKey]) { - apiClients[clientKey] = new ShlinkApiClient(axios, url, apiKey); - } - - return apiClients[clientKey]; -}; - -export default buildShlinkApiClient; diff --git a/src/utils/services/ShlinkApiClientBuilder.ts b/src/utils/services/ShlinkApiClientBuilder.ts new file mode 100644 index 00000000..d2ba24cd --- /dev/null +++ b/src/utils/services/ShlinkApiClientBuilder.ts @@ -0,0 +1,36 @@ +import { AxiosInstance } from 'axios'; +import { prop } from 'ramda'; +import { hasServerData, SelectedServer, ServerWithId } from '../../servers/data'; +import { GetState } from '../../container/types'; +import ShlinkApiClient from './ShlinkApiClient'; + +const apiClients: Record = {}; + +const isGetState = (getStateOrSelectedServer: GetState | ServerWithId): getStateOrSelectedServer is GetState => + typeof getStateOrSelectedServer === 'function'; +const getSelectedServerFromState = (getState: GetState): SelectedServer => prop('selectedServer', getState()); + +export type ShlinkApiClientBuilder = (getStateOrSelectedServer: GetState | ServerWithId) => ShlinkApiClient; + +const buildShlinkApiClient = (axios: AxiosInstance): ShlinkApiClientBuilder => ( + getStateOrSelectedServer: GetState | ServerWithId, +) => { + const server = isGetState(getStateOrSelectedServer) + ? getSelectedServerFromState(getStateOrSelectedServer) + : getStateOrSelectedServer; + + if (!hasServerData(server)) { + throw new Error('There\'s no selected server or it is not found'); + } + + const { url, apiKey } = server; + const clientKey = `${url}_${apiKey}`; + + if (!apiClients[clientKey]) { + apiClients[clientKey] = new ShlinkApiClient(axios, url, apiKey); + } + + return apiClients[clientKey]; +}; + +export default buildShlinkApiClient; diff --git a/src/utils/services/types.ts b/src/utils/services/types.ts index a1c55880..5e446c55 100644 --- a/src/utils/services/types.ts +++ b/src/utils/services/types.ts @@ -1,10 +1,3 @@ -import { ServerWithId } from '../../servers/data'; -import { GetState } from '../../container/types'; -import ShlinkApiClient from './ShlinkApiClient'; - -// FIXME Move to ShlinkApiClientBuilder -export type ShlinkApiClientBuilder = (getStateOrSelectedServer: ServerWithId | GetState) => ShlinkApiClient; - export interface ShlinkMercureInfo { token: string; mercureHubUrl: string; diff --git a/test/utils/services/ShlinkApiClientBuilder.test.js b/test/utils/services/ShlinkApiClientBuilder.test.ts similarity index 60% rename from test/utils/services/ShlinkApiClientBuilder.test.js rename to test/utils/services/ShlinkApiClientBuilder.test.ts index 2490ca27..5fac09ae 100644 --- a/test/utils/services/ShlinkApiClientBuilder.test.js +++ b/test/utils/services/ShlinkApiClientBuilder.test.ts @@ -1,18 +1,25 @@ +import { Mock } from 'ts-mockery'; +import { AxiosInstance } from 'axios'; import buildShlinkApiClient from '../../../src/utils/services/ShlinkApiClientBuilder'; +import { ReachableServer, SelectedServer } from '../../../src/servers/data'; +import { ShlinkState } from '../../../src/container/types'; describe('ShlinkApiClientBuilder', () => { - const createBuilder = () => { - const builder = buildShlinkApiClient({}); + const axiosMock = Mock.all(); + const server = (data: Partial) => Mock.of(data); - return (selectedServer) => builder(() => ({ selectedServer })); + const createBuilder = () => { + const builder = buildShlinkApiClient(axiosMock); + + return (selectedServer: SelectedServer) => builder(() => Mock.of({ selectedServer })); }; it('creates new instances when provided params are different', async () => { const builder = createBuilder(); const [ firstApiClient, secondApiClient, thirdApiClient ] = await Promise.all([ - builder({ url: 'foo', apiKey: 'bar' }), - builder({ url: 'bar', apiKey: 'bar' }), - builder({ url: 'bar', apiKey: 'foo' }), + builder(server({ url: 'foo', apiKey: 'bar' })), + builder(server({ url: 'bar', apiKey: 'bar' })), + builder(server({ url: 'bar', apiKey: 'foo' })), ]); expect(firstApiClient).not.toBe(secondApiClient); @@ -22,7 +29,7 @@ describe('ShlinkApiClientBuilder', () => { it('returns existing instances when provided params are the same', async () => { const builder = createBuilder(); - const selectedServer = { url: 'foo', apiKey: 'bar' }; + const selectedServer = server({ url: 'foo', apiKey: 'bar' }); const [ firstApiClient, secondApiClient, thirdApiClient ] = await Promise.all([ builder(selectedServer), builder(selectedServer), @@ -37,7 +44,7 @@ describe('ShlinkApiClientBuilder', () => { it('does not fetch from state when provided param is already selected server', () => { const url = 'url'; const apiKey = 'apiKey'; - const apiClient = buildShlinkApiClient({})({ url, apiKey }); + const apiClient = buildShlinkApiClient(axiosMock)(server({ url, apiKey })); expect(apiClient._baseUrl).toEqual(url); expect(apiClient._apiKey).toEqual(apiKey);