From 89423737e82b30951042bca7bc67057baabbf6a6 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 8 Nov 2022 22:59:41 +0100 Subject: [PATCH] Removed hardcoded action references by improving dependency injection --- src/short-urls/reducers/shortUrlCreation.ts | 16 +++++++++------- src/short-urls/services/provideServices.ts | 6 +++--- src/tags/reducers/tagsList.ts | 10 ++++++---- src/tags/services/provideServices.ts | 2 +- .../short-urls/reducers/shortUrlCreation.test.ts | 4 +++- test/tags/reducers/tagsList.test.ts | 7 ++++--- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/short-urls/reducers/shortUrlCreation.ts b/src/short-urls/reducers/shortUrlCreation.ts index f30cdcd3..faa76a6e 100644 --- a/src/short-urls/reducers/shortUrlCreation.ts +++ b/src/short-urls/reducers/shortUrlCreation.ts @@ -35,12 +35,15 @@ const initialState: ShortUrlCreation = { error: false, }; -export const shortUrlCreationReducerCreator = (buildShlinkApiClient: ShlinkApiClientBuilder) => { - const createShortUrl = createAsyncThunk(CREATE_SHORT_URL, (data: ShortUrlData, { getState }): Promise => { +export const createShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk( + CREATE_SHORT_URL, + (data: ShortUrlData, { getState }): Promise => { const { createShortUrl: shlinkCreateShortUrl } = buildShlinkApiClient(getState); return shlinkCreateShortUrl(data); - }); + }, +); +export const shortUrlCreationReducerCreator = (createShortUrlThunk: ReturnType) => { const { reducer, actions } = createSlice({ name: 'shortUrlCreationReducer', initialState: initialState as ShortUrlCreation, // Without this casting it infers type ShortUrlCreationWaiting @@ -48,13 +51,13 @@ export const shortUrlCreationReducerCreator = (buildShlinkApiClient: ShlinkApiCl resetCreateShortUrl: () => initialState, }, extraReducers: (builder) => { - builder.addCase(createShortUrl.pending, () => ({ saving: true, saved: false, error: false })); + builder.addCase(createShortUrlThunk.pending, () => ({ saving: true, saved: false, error: false })); builder.addCase( - createShortUrl.rejected, + createShortUrlThunk.rejected, (_, { error }) => ({ saving: false, saved: false, error: true, errorData: parseApiError(error) }), ); builder.addCase( - createShortUrl.fulfilled, + createShortUrlThunk.fulfilled, (_, { payload: result }) => ({ result, saving: false, saved: true, error: false }), ); }, @@ -64,7 +67,6 @@ export const shortUrlCreationReducerCreator = (buildShlinkApiClient: ShlinkApiCl return { reducer, - createShortUrl, resetCreateShortUrl, }; }; diff --git a/src/short-urls/services/provideServices.ts b/src/short-urls/services/provideServices.ts index 8feb709b..13b9e2fe 100644 --- a/src/short-urls/services/provideServices.ts +++ b/src/short-urls/services/provideServices.ts @@ -8,7 +8,7 @@ import { CreateShortUrl } from '../CreateShortUrl'; import { DeleteShortUrlModal } from '../helpers/DeleteShortUrlModal'; import { CreateShortUrlResult } from '../helpers/CreateShortUrlResult'; import { listShortUrls } from '../reducers/shortUrlsList'; -import { shortUrlCreationReducerCreator } from '../reducers/shortUrlCreation'; +import { shortUrlCreationReducerCreator, createShortUrl } from '../reducers/shortUrlCreation'; import { shortUrlDeletionReducerCreator } from '../reducers/shortUrlDeletion'; import { shortUrlEditionReducerCreator } from '../reducers/shortUrlEdition'; import { shortUrlDetailReducerCreator } from '../reducers/shortUrlDetail'; @@ -57,7 +57,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.decorator('ExportShortUrlsBtn', connect(['selectedServer'])); // Reducers - bottle.serviceFactory('shortUrlCreationReducerCreator', shortUrlCreationReducerCreator, 'buildShlinkApiClient'); + bottle.serviceFactory('shortUrlCreationReducerCreator', shortUrlCreationReducerCreator, 'createShortUrl'); bottle.serviceFactory('shortUrlCreationReducer', prop('reducer'), 'shortUrlCreationReducerCreator'); bottle.serviceFactory('shortUrlEditionReducerCreator', shortUrlEditionReducerCreator, 'buildShlinkApiClient'); @@ -72,7 +72,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { // Actions bottle.serviceFactory('listShortUrls', listShortUrls, 'buildShlinkApiClient'); - bottle.serviceFactory('createShortUrl', prop('createShortUrl'), 'shortUrlCreationReducerCreator'); + bottle.serviceFactory('createShortUrl', createShortUrl, 'buildShlinkApiClient'); bottle.serviceFactory('resetCreateShortUrl', prop('resetCreateShortUrl'), 'shortUrlCreationReducerCreator'); bottle.serviceFactory('deleteShortUrl', prop('deleteShortUrl'), 'shortUrlDeletionReducerCreator'); diff --git a/src/tags/reducers/tagsList.ts b/src/tags/reducers/tagsList.ts index 59d1a2d6..a6e684b6 100644 --- a/src/tags/reducers/tagsList.ts +++ b/src/tags/reducers/tagsList.ts @@ -7,7 +7,7 @@ import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilde import { CreateVisit, Stats } from '../../visits/types'; import { parseApiError } from '../../api/utils'; import { TagStats } from '../data'; -import { CREATE_SHORT_URL } from '../../short-urls/reducers/shortUrlCreation'; +import { createShortUrl } from '../../short-urls/reducers/shortUrlCreation'; import { tagDeleted } from './tagDelete'; import { tagEdited } from './tagEdit'; import { ProblemDetailsError } from '../../api/types/errors'; @@ -88,7 +88,10 @@ export const listTags = (buildShlinkApiClient: ShlinkApiClientBuilder, force = t export const filterTags = createAction(FILTER_TAGS); -export const reducer = (listTagsThunk: ReturnType) => createSlice({ +export const reducer = ( + listTagsThunk: ReturnType, + createShortUrlThunk: ReturnType, +) => createSlice({ name: 'shlink/tagsList', initialState, reducers: {}, @@ -121,8 +124,7 @@ export const reducer = (listTagsThunk: ReturnType) => createSli stats: increaseVisitsForTags(calculateVisitsPerTag(payload.createdVisits), state.stats), })); - // TODO Do not hardcode action type here. Inject async thunk instead - builder.addCase(`${CREATE_SHORT_URL}/fulfilled`, ({ tags: stateTags, ...rest }, { payload }: any) => ({ + builder.addCase(createShortUrlThunk.fulfilled, ({ tags: stateTags, ...rest }, { payload }) => ({ ...rest, tags: stateTags.concat(payload.tags.filter((tag: string) => !stateTags.includes(tag))), // More performant than [ ...new Set(...) ] })); diff --git a/src/tags/services/provideServices.ts b/src/tags/services/provideServices.ts index 0661288c..ce1cc554 100644 --- a/src/tags/services/provideServices.ts +++ b/src/tags/services/provideServices.ts @@ -44,7 +44,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { bottle.serviceFactory('tagDeleteReducerCreator', tagDeleteReducerCreator, 'buildShlinkApiClient'); bottle.serviceFactory('tagDeleteReducer', prop('reducer'), 'tagDeleteReducerCreator'); - bottle.serviceFactory('tagsListReducer', reducer, 'listTags'); + bottle.serviceFactory('tagsListReducer', reducer, 'listTags', 'createShortUrl'); // Actions const listTagsActionFactory = (force: boolean) => diff --git a/test/short-urls/reducers/shortUrlCreation.test.ts b/test/short-urls/reducers/shortUrlCreation.test.ts index e99e0ea2..f7815b4a 100644 --- a/test/short-urls/reducers/shortUrlCreation.test.ts +++ b/test/short-urls/reducers/shortUrlCreation.test.ts @@ -2,6 +2,7 @@ import { Mock } from 'ts-mockery'; import { CreateShortUrlAction, shortUrlCreationReducerCreator, + createShortUrl as createShortUrlCreator, } from '../../../src/short-urls/reducers/shortUrlCreation'; import { ShortUrl } from '../../../src/short-urls/data'; import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; @@ -11,7 +12,8 @@ describe('shortUrlCreationReducer', () => { const shortUrl = Mock.of(); const createShortUrlCall = jest.fn(); const buildShlinkApiClient = () => Mock.of({ createShortUrl: createShortUrlCall }); - const { reducer, createShortUrl, resetCreateShortUrl } = shortUrlCreationReducerCreator(buildShlinkApiClient); + const createShortUrl = createShortUrlCreator(buildShlinkApiClient); + const { reducer, resetCreateShortUrl } = shortUrlCreationReducerCreator(createShortUrl); afterEach(jest.resetAllMocks); diff --git a/test/tags/reducers/tagsList.test.ts b/test/tags/reducers/tagsList.test.ts index 4cc6ebb9..06c99828 100644 --- a/test/tags/reducers/tagsList.test.ts +++ b/test/tags/reducers/tagsList.test.ts @@ -7,7 +7,7 @@ import { } from '../../../src/tags/reducers/tagsList'; import { ShlinkState } from '../../../src/container/types'; import { ShortUrl } from '../../../src/short-urls/data'; -import { CREATE_SHORT_URL } from '../../../src/short-urls/reducers/shortUrlCreation'; +import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation'; import { tagEdited } from '../../../src/tags/reducers/tagEdit'; import { tagDeleted } from '../../../src/tags/reducers/tagDelete'; @@ -15,7 +15,8 @@ describe('tagsListReducer', () => { const state = (props: Partial) => Mock.of(props); const buildShlinkApiClient = jest.fn(); const listTags = listTagsCreator(buildShlinkApiClient, true); - const reducer = reducerCreator(listTags); + const createShortUrl = createShortUrlCreator(buildShlinkApiClient); + const reducer = reducerCreator(listTags, createShortUrl); afterEach(jest.clearAllMocks); @@ -99,7 +100,7 @@ describe('tagsListReducer', () => { const tags = ['foo', 'bar', 'baz', 'foo2', 'fo']; const payload = Mock.of({ tags: shortUrlTags }); - expect(reducer(state({ tags }), { type: `${CREATE_SHORT_URL}/fulfilled`, payload })).toEqual({ + expect(reducer(state({ tags }), { type: createShortUrl.fulfilled.toString(), payload })).toEqual({ tags: expectedTags, }); });