mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 17:40:23 +03:00
Improved short URL detail redux action so that it avoids API calls when the URL is found in local state
This commit is contained in:
parent
56b3523c5b
commit
13d3a95a06
2 changed files with 47 additions and 9 deletions
|
@ -4,6 +4,7 @@ import { buildReducer } from '../../utils/helpers/redux';
|
||||||
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
|
import { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder';
|
||||||
import { OptionalString } from '../../utils/utils';
|
import { OptionalString } from '../../utils/utils';
|
||||||
import { GetState } from '../../container/types';
|
import { GetState } from '../../container/types';
|
||||||
|
import { shortUrlMatches } from '../helpers';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const GET_SHORT_URL_DETAIL_START = 'shlink/shortUrlDetail/GET_SHORT_URL_DETAIL_START';
|
export const GET_SHORT_URL_DETAIL_START = 'shlink/shortUrlDetail/GET_SHORT_URL_DETAIL_START';
|
||||||
|
@ -37,10 +38,12 @@ export const getShortUrlDetail = (buildShlinkApiClient: ShlinkApiClientBuilder)
|
||||||
domain: OptionalString,
|
domain: OptionalString,
|
||||||
) => async (dispatch: Dispatch, getState: GetState) => {
|
) => async (dispatch: Dispatch, getState: GetState) => {
|
||||||
dispatch({ type: GET_SHORT_URL_DETAIL_START });
|
dispatch({ type: GET_SHORT_URL_DETAIL_START });
|
||||||
const { getShortUrl } = buildShlinkApiClient(getState);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const shortUrl = await getShortUrl(shortCode, domain);
|
const { shortUrlsList } = getState();
|
||||||
|
const shortUrl = shortUrlsList?.shortUrls?.data.find(
|
||||||
|
(shortUrl) => shortUrlMatches(shortUrl, shortCode, domain),
|
||||||
|
) ?? await buildShlinkApiClient(getState).getShortUrl(shortCode, domain);
|
||||||
|
|
||||||
dispatch<ShortUrlDetailAction>({ shortUrl, type: GET_SHORT_URL_DETAIL });
|
dispatch<ShortUrlDetailAction>({ shortUrl, type: GET_SHORT_URL_DETAIL });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -9,8 +9,11 @@ import reducer, {
|
||||||
import { ShortUrl } from '../../../src/short-urls/data';
|
import { ShortUrl } from '../../../src/short-urls/data';
|
||||||
import ShlinkApiClient from '../../../src/api/services/ShlinkApiClient';
|
import ShlinkApiClient from '../../../src/api/services/ShlinkApiClient';
|
||||||
import { ShlinkState } from '../../../src/container/types';
|
import { ShlinkState } from '../../../src/container/types';
|
||||||
|
import { ShortUrlsList } from '../../../src/short-urls/reducers/shortUrlsList';
|
||||||
|
|
||||||
describe('shortUrlDetailReducer', () => {
|
describe('shortUrlDetailReducer', () => {
|
||||||
|
beforeEach(jest.clearAllMocks);
|
||||||
|
|
||||||
describe('reducer', () => {
|
describe('reducer', () => {
|
||||||
const action = (type: string) => Mock.of<ShortUrlDetailAction>({ type });
|
const action = (type: string) => Mock.of<ShortUrlDetailAction>({ type });
|
||||||
|
|
||||||
|
@ -45,14 +48,12 @@ describe('shortUrlDetailReducer', () => {
|
||||||
getShortUrl: jest.fn(async () => returned),
|
getShortUrl: jest.fn(async () => returned),
|
||||||
});
|
});
|
||||||
const dispatchMock = jest.fn();
|
const dispatchMock = jest.fn();
|
||||||
const getState = () => Mock.of<ShlinkState>();
|
const buildGetState = (shortUrlsList?: ShortUrlsList) => () => Mock.of<ShlinkState>({ shortUrlsList });
|
||||||
|
|
||||||
beforeEach(() => dispatchMock.mockReset());
|
|
||||||
|
|
||||||
it('dispatches start and error when promise is rejected', async () => {
|
it('dispatches start and error when promise is rejected', async () => {
|
||||||
const ShlinkApiClient = buildApiClientMock(Promise.reject());
|
const ShlinkApiClient = buildApiClientMock(Promise.reject());
|
||||||
|
|
||||||
await getShortUrlDetail(() => ShlinkApiClient)('abc123', '')(dispatchMock, getState);
|
await getShortUrlDetail(() => ShlinkApiClient)('abc123', '')(dispatchMock, buildGetState());
|
||||||
|
|
||||||
expect(dispatchMock).toHaveBeenCalledTimes(2);
|
expect(dispatchMock).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_DETAIL_START });
|
expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_DETAIL_START });
|
||||||
|
@ -60,16 +61,50 @@ describe('shortUrlDetailReducer', () => {
|
||||||
expect(ShlinkApiClient.getShortUrl).toHaveBeenCalledTimes(1);
|
expect(ShlinkApiClient.getShortUrl).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches start and success when promise is resolved', async () => {
|
it.each([
|
||||||
const resolvedShortUrl = Mock.of<ShortUrl>({ longUrl: 'foo', shortCode: 'bar' });
|
[ undefined ],
|
||||||
|
[ Mock.all<ShortUrlsList>() ],
|
||||||
|
[
|
||||||
|
Mock.of<ShortUrlsList>({
|
||||||
|
shortUrls: { data: [] },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
Mock.of<ShortUrlsList>({
|
||||||
|
shortUrls: {
|
||||||
|
data: [ Mock.of<ShortUrl>({ shortCode: 'this_will_not_match' }) ],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
])('performs API call when short URL is not found in local state', async (shortUrlsList?: ShortUrlsList) => {
|
||||||
|
const resolvedShortUrl = Mock.of<ShortUrl>({ longUrl: 'foo', shortCode: 'abc123' });
|
||||||
const ShlinkApiClient = buildApiClientMock(Promise.resolve(resolvedShortUrl));
|
const ShlinkApiClient = buildApiClientMock(Promise.resolve(resolvedShortUrl));
|
||||||
|
|
||||||
await getShortUrlDetail(() => ShlinkApiClient)('abc123', '')(dispatchMock, getState);
|
await getShortUrlDetail(() => ShlinkApiClient)('abc123', '')(dispatchMock, buildGetState(shortUrlsList));
|
||||||
|
|
||||||
expect(dispatchMock).toHaveBeenCalledTimes(2);
|
expect(dispatchMock).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_DETAIL_START });
|
expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_DETAIL_START });
|
||||||
expect(dispatchMock).toHaveBeenNthCalledWith(2, { type: GET_SHORT_URL_DETAIL, shortUrl: resolvedShortUrl });
|
expect(dispatchMock).toHaveBeenNthCalledWith(2, { type: GET_SHORT_URL_DETAIL, shortUrl: resolvedShortUrl });
|
||||||
expect(ShlinkApiClient.getShortUrl).toHaveBeenCalledTimes(1);
|
expect(ShlinkApiClient.getShortUrl).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('avoids API calls when short URL is found in local state', async () => {
|
||||||
|
const foundShortUrl = Mock.of<ShortUrl>({ longUrl: 'foo', shortCode: 'abc123' });
|
||||||
|
const ShlinkApiClient = buildApiClientMock(Promise.resolve(Mock.all<ShortUrl>()));
|
||||||
|
|
||||||
|
await getShortUrlDetail(() => ShlinkApiClient)(foundShortUrl.shortCode, foundShortUrl.domain)(
|
||||||
|
dispatchMock,
|
||||||
|
buildGetState(Mock.of<ShortUrlsList>({
|
||||||
|
shortUrls: {
|
||||||
|
data: [ foundShortUrl ],
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(dispatchMock).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatchMock).toHaveBeenNthCalledWith(1, { type: GET_SHORT_URL_DETAIL_START });
|
||||||
|
expect(dispatchMock).toHaveBeenNthCalledWith(2, { type: GET_SHORT_URL_DETAIL, shortUrl: foundShortUrl });
|
||||||
|
expect(ShlinkApiClient.getShortUrl).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue