mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Migrated domainRedirects reducer to redux/toolkit
This commit is contained in:
parent
b6d08e2203
commit
34f4411aa1
5 changed files with 61 additions and 68 deletions
|
@ -1,33 +1,23 @@
|
||||||
import { Action, Dispatch } from 'redux';
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
|
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
|
||||||
import { ShlinkDomainRedirects } from '../../api/types';
|
import { ShlinkDomainRedirects } from '../../api/types';
|
||||||
import { GetState } from '../../container/types';
|
import { ShlinkState } from '../../container/types';
|
||||||
import { ApiErrorAction } from '../../api/types/actions';
|
|
||||||
import { parseApiError } from '../../api/utils';
|
|
||||||
|
|
||||||
export const EDIT_DOMAIN_REDIRECTS_START = 'shlink/domainRedirects/EDIT_DOMAIN_REDIRECTS_START';
|
const EDIT_DOMAIN_REDIRECTS = 'shlink/domainRedirects/EDIT_DOMAIN_REDIRECTS';
|
||||||
export const EDIT_DOMAIN_REDIRECTS_ERROR = 'shlink/domainRedirects/EDIT_DOMAIN_REDIRECTS_ERROR';
|
|
||||||
export const EDIT_DOMAIN_REDIRECTS = 'shlink/domainRedirects/EDIT_DOMAIN_REDIRECTS';
|
|
||||||
|
|
||||||
export interface EditDomainRedirects {
|
export interface EditDomainRedirects {
|
||||||
domain: string;
|
domain: string;
|
||||||
redirects: ShlinkDomainRedirects;
|
redirects: ShlinkDomainRedirects;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EditDomainRedirectsAction extends Action<string>, EditDomainRedirects {}
|
export const editDomainRedirects = (
|
||||||
|
buildShlinkApiClient: ShlinkApiClientBuilder,
|
||||||
export const editDomainRedirects = (buildShlinkApiClient: ShlinkApiClientBuilder) => ({
|
) => createAsyncThunk<EditDomainRedirects, EditDomainRedirects, { state: ShlinkState }>(
|
||||||
domain,
|
EDIT_DOMAIN_REDIRECTS,
|
||||||
redirects: domainRedirects,
|
async ({ domain, redirects: domainRedirects }, { getState }) => {
|
||||||
}: EditDomainRedirects) => async (dispatch: Dispatch, getState: GetState) => {
|
const { editDomainRedirects: shlinkEditDomainRedirects } = buildShlinkApiClient(getState);
|
||||||
dispatch({ type: EDIT_DOMAIN_REDIRECTS_START });
|
|
||||||
const { editDomainRedirects: shlinkEditDomainRedirects } = buildShlinkApiClient(getState);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const redirects = await shlinkEditDomainRedirects({ domain, ...domainRedirects });
|
const redirects = await shlinkEditDomainRedirects({ domain, ...domainRedirects });
|
||||||
|
|
||||||
dispatch<EditDomainRedirectsAction>({ type: EDIT_DOMAIN_REDIRECTS, domain, redirects });
|
return { domain, redirects };
|
||||||
} catch (e: any) {
|
},
|
||||||
dispatch<ApiErrorAction>({ type: EDIT_DOMAIN_REDIRECTS_ERROR, errorData: parseApiError(e) });
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { createSlice, createAsyncThunk, createAction, SliceCaseReducers } from '@reduxjs/toolkit';
|
import { createSlice, createAsyncThunk, createAction, SliceCaseReducers, AsyncThunk } from '@reduxjs/toolkit';
|
||||||
import { ShlinkDomainRedirects } from '../../api/types';
|
import { ShlinkDomainRedirects } from '../../api/types';
|
||||||
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
|
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
|
||||||
import { ShlinkState } from '../../container/types';
|
import { ShlinkState } from '../../container/types';
|
||||||
import { Domain, DomainStatus } from '../data';
|
import { Domain, DomainStatus } from '../data';
|
||||||
import { hasServerData } from '../../servers/data';
|
import { hasServerData } from '../../servers/data';
|
||||||
import { replaceAuthorityFromUri } from '../../utils/helpers/uri';
|
import { replaceAuthorityFromUri } from '../../utils/helpers/uri';
|
||||||
import { EDIT_DOMAIN_REDIRECTS } from './domainRedirects';
|
|
||||||
import { ProblemDetailsError } from '../../api/types/errors';
|
import { ProblemDetailsError } from '../../api/types/errors';
|
||||||
import { parseApiError } from '../../api/utils';
|
import { parseApiError } from '../../api/utils';
|
||||||
|
import { EditDomainRedirects } from './domainRedirects';
|
||||||
|
|
||||||
export const LIST_DOMAINS = 'shlink/domainsList/LIST_DOMAINS';
|
const LIST_DOMAINS = 'shlink/domainsList/LIST_DOMAINS';
|
||||||
export const FILTER_DOMAINS = 'shlink/domainsList/FILTER_DOMAINS';
|
const FILTER_DOMAINS = 'shlink/domainsList/FILTER_DOMAINS';
|
||||||
export const VALIDATE_DOMAIN = 'shlink/domainsList/VALIDATE_DOMAIN';
|
const VALIDATE_DOMAIN = 'shlink/domainsList/VALIDATE_DOMAIN';
|
||||||
|
|
||||||
export interface DomainsList {
|
export interface DomainsList {
|
||||||
domains: Domain[];
|
domains: Domain[];
|
||||||
|
@ -39,13 +39,16 @@ const initialState: DomainsList = {
|
||||||
error: false,
|
error: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const replaceRedirectsOnDomain = (domain: string, redirects: ShlinkDomainRedirects) =>
|
export const replaceRedirectsOnDomain = ({ domain, redirects }: EditDomainRedirects) =>
|
||||||
(d: Domain): Domain => (d.domain !== domain ? d : { ...d, redirects });
|
(d: Domain): Domain => (d.domain !== domain ? d : { ...d, redirects });
|
||||||
|
|
||||||
export const replaceStatusOnDomain = (domain: string, status: DomainStatus) =>
|
export const replaceStatusOnDomain = (domain: string, status: DomainStatus) =>
|
||||||
(d: Domain): Domain => (d.domain !== domain ? d : { ...d, status });
|
(d: Domain): Domain => (d.domain !== domain ? d : { ...d, status });
|
||||||
|
|
||||||
export const domainsListReducerCreator = (buildShlinkApiClient: ShlinkApiClientBuilder) => {
|
export const domainsListReducerCreator = (
|
||||||
|
buildShlinkApiClient: ShlinkApiClientBuilder,
|
||||||
|
editDomainRedirects: AsyncThunk<EditDomainRedirects, any, any>,
|
||||||
|
) => {
|
||||||
const listDomains = createAsyncThunk<ListDomains, void, { state: ShlinkState }>(
|
const listDomains = createAsyncThunk<ListDomains, void, { state: ShlinkState }>(
|
||||||
LIST_DOMAINS,
|
LIST_DOMAINS,
|
||||||
async (_, { getState }) => {
|
async (_, { getState }) => {
|
||||||
|
@ -110,10 +113,10 @@ export const domainsListReducerCreator = (buildShlinkApiClient: ShlinkApiClientB
|
||||||
filteredDomains: state.domains.filter(({ domain }) => domain.toLowerCase().match(payload.toLowerCase())),
|
filteredDomains: state.domains.filter(({ domain }) => domain.toLowerCase().match(payload.toLowerCase())),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
builder.addCase(EDIT_DOMAIN_REDIRECTS, (state, { domain, redirects }: any) => ({ // TODO Fix this "any"
|
builder.addCase(editDomainRedirects.fulfilled, (state, { payload }) => ({
|
||||||
...state,
|
...state,
|
||||||
domains: state.domains.map(replaceRedirectsOnDomain(domain, redirects)),
|
domains: state.domains.map(replaceRedirectsOnDomain(payload)),
|
||||||
filteredDomains: state.filteredDomains.map(replaceRedirectsOnDomain(domain, redirects)),
|
filteredDomains: state.filteredDomains.map(replaceRedirectsOnDomain(payload)),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,14 +18,19 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
));
|
));
|
||||||
|
|
||||||
// Reducer
|
// Reducer
|
||||||
bottle.serviceFactory('domainsListReducerCreator', domainsListReducerCreator, 'buildShlinkApiClient');
|
bottle.serviceFactory(
|
||||||
bottle.serviceFactory('domainsListReducer', prop('reducer'), 'domainsListReducerCreator'); // TODO Improve type checks on the prop that gets picked here
|
'domainsListReducerCreator',
|
||||||
|
domainsListReducerCreator,
|
||||||
|
'buildShlinkApiClient',
|
||||||
|
'editDomainRedirects',
|
||||||
|
);
|
||||||
|
bottle.serviceFactory('domainsListReducer', prop('reducer'), 'domainsListReducerCreator');
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
bottle.serviceFactory('listDomains', prop('listDomains'), 'domainsListReducerCreator'); // TODO Improve type checks on the prop that gets picked here
|
bottle.serviceFactory('listDomains', prop('listDomains'), 'domainsListReducerCreator');
|
||||||
bottle.serviceFactory('filterDomains', prop('filterDomains'), 'domainsListReducerCreator'); // TODO Improve type checks on the prop that gets picked here
|
bottle.serviceFactory('filterDomains', prop('filterDomains'), 'domainsListReducerCreator');
|
||||||
bottle.serviceFactory('editDomainRedirects', editDomainRedirects, 'buildShlinkApiClient');
|
bottle.serviceFactory('editDomainRedirects', editDomainRedirects, 'buildShlinkApiClient');
|
||||||
bottle.serviceFactory('checkDomainHealth', prop('checkDomainHealth'), 'domainsListReducerCreator'); // TODO Improve type checks on the prop that gets picked here
|
bottle.serviceFactory('checkDomainHealth', prop('checkDomainHealth'), 'domainsListReducerCreator');
|
||||||
};
|
};
|
||||||
|
|
||||||
export default provideServices;
|
export default provideServices;
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
|
import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
|
||||||
import {
|
import { EditDomainRedirects, editDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
|
||||||
EDIT_DOMAIN_REDIRECTS,
|
|
||||||
EDIT_DOMAIN_REDIRECTS_ERROR,
|
|
||||||
EDIT_DOMAIN_REDIRECTS_START, EditDomainRedirects,
|
|
||||||
editDomainRedirects as editDomainRedirectsAction,
|
|
||||||
} from '../../../src/domains/reducers/domainRedirects';
|
|
||||||
import { ShlinkDomainRedirects } from '../../../src/api/types';
|
import { ShlinkDomainRedirects } from '../../../src/api/types';
|
||||||
|
|
||||||
describe('domainRedirectsReducer', () => {
|
describe('domainRedirectsReducer', () => {
|
||||||
|
@ -16,35 +11,33 @@ describe('domainRedirectsReducer', () => {
|
||||||
const redirects = Mock.all<ShlinkDomainRedirects>();
|
const redirects = Mock.all<ShlinkDomainRedirects>();
|
||||||
const dispatch = jest.fn();
|
const dispatch = jest.fn();
|
||||||
const getState = jest.fn();
|
const getState = jest.fn();
|
||||||
const editDomainRedirects = jest.fn();
|
const editDomainRedirectsCall = jest.fn();
|
||||||
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ editDomainRedirects });
|
const buildShlinkApiClient = () => Mock.of<ShlinkApiClient>({ editDomainRedirects: editDomainRedirectsCall });
|
||||||
|
const editDomainRedirectsAction = editDomainRedirects(buildShlinkApiClient);
|
||||||
|
|
||||||
it('dispatches error when loading domains fails', async () => {
|
it('dispatches error when loading domains fails', async () => {
|
||||||
editDomainRedirects.mockRejectedValue(new Error('error'));
|
editDomainRedirectsCall.mockRejectedValue(new Error('error'));
|
||||||
|
|
||||||
await editDomainRedirectsAction(buildShlinkApiClient)(Mock.of<EditDomainRedirects>({ domain }))(
|
await editDomainRedirectsAction(Mock.of<EditDomainRedirects>({ domain }))(dispatch, getState, {});
|
||||||
dispatch,
|
|
||||||
getState,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_DOMAIN_REDIRECTS_START });
|
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_DOMAIN_REDIRECTS_ERROR });
|
type: editDomainRedirectsAction.rejected.toString(),
|
||||||
expect(editDomainRedirects).toHaveBeenCalledTimes(1);
|
}));
|
||||||
|
expect(editDomainRedirectsCall).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches domain and redirects once loaded', async () => {
|
it('dispatches domain and redirects once loaded', async () => {
|
||||||
editDomainRedirects.mockResolvedValue(redirects);
|
editDomainRedirectsCall.mockResolvedValue(redirects);
|
||||||
|
|
||||||
await editDomainRedirectsAction(buildShlinkApiClient)(Mock.of<EditDomainRedirects>({ domain }))(
|
await editDomainRedirectsAction(Mock.of<EditDomainRedirects>({ domain }))(dispatch, getState, {});
|
||||||
dispatch,
|
|
||||||
getState,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_DOMAIN_REDIRECTS_START });
|
expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(2, { type: EDIT_DOMAIN_REDIRECTS, domain, redirects });
|
type: editDomainRedirectsAction.fulfilled.toString(),
|
||||||
expect(editDomainRedirects).toHaveBeenCalledTimes(1);
|
payload: { domain, redirects },
|
||||||
|
}));
|
||||||
|
expect(editDomainRedirectsCall).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
replaceStatusOnDomain,
|
replaceStatusOnDomain,
|
||||||
domainsListReducerCreator,
|
domainsListReducerCreator,
|
||||||
} from '../../../src/domains/reducers/domainsList';
|
} from '../../../src/domains/reducers/domainsList';
|
||||||
import { EDIT_DOMAIN_REDIRECTS } from '../../../src/domains/reducers/domainRedirects';
|
import { editDomainRedirects } from '../../../src/domains/reducers/domainRedirects';
|
||||||
import { ShlinkDomainRedirects } from '../../../src/api/types';
|
import { ShlinkDomainRedirects } from '../../../src/api/types';
|
||||||
import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
|
import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient';
|
||||||
import { Domain } from '../../../src/domains/data';
|
import { Domain } from '../../../src/domains/data';
|
||||||
|
@ -30,8 +30,10 @@ describe('domainsListReducer', () => {
|
||||||
data: { type: 'NOT_FOUND', status: 404 },
|
data: { type: 'NOT_FOUND', status: 404 },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const editDomainRedirectsThunk = editDomainRedirects(buildShlinkApiClient);
|
||||||
const { reducer, listDomains: listDomainsAction, checkDomainHealth, filterDomains } = domainsListReducerCreator(
|
const { reducer, listDomains: listDomainsAction, checkDomainHealth, filterDomains } = domainsListReducerCreator(
|
||||||
buildShlinkApiClient,
|
buildShlinkApiClient,
|
||||||
|
editDomainRedirectsThunk,
|
||||||
);
|
);
|
||||||
|
|
||||||
beforeEach(jest.clearAllMocks);
|
beforeEach(jest.clearAllMocks);
|
||||||
|
@ -72,12 +74,12 @@ describe('domainsListReducer', () => {
|
||||||
invalidShortUrlRedirect: null,
|
invalidShortUrlRedirect: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(reducer(
|
expect(reducer(Mock.of<DomainsList>({ domains, filteredDomains }), {
|
||||||
Mock.of<DomainsList>({ domains, filteredDomains }),
|
type: editDomainRedirectsThunk.fulfilled.toString(),
|
||||||
{ type: EDIT_DOMAIN_REDIRECTS, domain, redirects },
|
payload: { domain, redirects },
|
||||||
)).toEqual({
|
})).toEqual({
|
||||||
domains: domains.map(replaceRedirectsOnDomain(domain, redirects)),
|
domains: domains.map(replaceRedirectsOnDomain({ domain, redirects })),
|
||||||
filteredDomains: filteredDomains.map(replaceRedirectsOnDomain(domain, redirects)),
|
filteredDomains: filteredDomains.map(replaceRedirectsOnDomain({ domain, redirects })),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue