mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Extracted helper fetch function and migrated remoteServers redux action from axios to fetch
This commit is contained in:
parent
e5afe4f767
commit
d800062159
9 changed files with 67 additions and 78 deletions
|
@ -119,21 +119,11 @@ export class ShlinkApiClient {
|
||||||
const normalizedQuery = stringifyQuery(rejectNilProps(query));
|
const normalizedQuery = stringifyQuery(rejectNilProps(query));
|
||||||
const stringifiedQuery = isEmpty(normalizedQuery) ? '' : `?${normalizedQuery}`;
|
const stringifiedQuery = isEmpty(normalizedQuery) ? '' : `?${normalizedQuery}`;
|
||||||
|
|
||||||
return this.fetch(`${buildShlinkBaseUrl(this.baseUrl, this.apiVersion)}${url}${stringifiedQuery}`, {
|
return this.fetch<T>(`${buildShlinkBaseUrl(this.baseUrl, this.apiVersion)}${url}${stringifiedQuery}`, {
|
||||||
method,
|
method,
|
||||||
body: body && JSON.stringify(body),
|
body: body && JSON.stringify(body),
|
||||||
headers: { 'X-Api-Key': this.apiKey },
|
headers: { 'X-Api-Key': this.apiKey },
|
||||||
})
|
}).catch((e: unknown) => {
|
||||||
.then(async (resp) => {
|
|
||||||
const parsed = await resp.json();
|
|
||||||
|
|
||||||
if (!resp.ok) {
|
|
||||||
throw parsed; // eslint-disable-line @typescript-eslint/no-throw-literal
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsed as T; // TODO Improve type inference here without explicit casting
|
|
||||||
})
|
|
||||||
.catch((e: unknown) => {
|
|
||||||
if (!isRegularNotFound(parseApiError(e))) {
|
if (!isRegularNotFound(parseApiError(e))) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Bottle from 'bottlejs';
|
||||||
import { buildShlinkApiClient } from './ShlinkApiClientBuilder';
|
import { buildShlinkApiClient } from './ShlinkApiClientBuilder';
|
||||||
|
|
||||||
const provideServices = (bottle: Bottle) => {
|
const provideServices = (bottle: Bottle) => {
|
||||||
bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'fetch');
|
bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'jsonFetch');
|
||||||
};
|
};
|
||||||
|
|
||||||
export default provideServices;
|
export default provideServices;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServ
|
||||||
import { sidebarNotPresent, sidebarPresent } from '../reducers/sidebar';
|
import { sidebarNotPresent, sidebarPresent } from '../reducers/sidebar';
|
||||||
import { ImageDownloader } from './ImageDownloader';
|
import { ImageDownloader } from './ImageDownloader';
|
||||||
import { ReportExporter } from './ReportExporter';
|
import { ReportExporter } from './ReportExporter';
|
||||||
|
import { jsonFetch } from '../../utils/helpers/fetch';
|
||||||
|
|
||||||
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
// Services
|
// Services
|
||||||
|
@ -19,6 +20,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
bottle.constant('console', global.console);
|
bottle.constant('console', global.console);
|
||||||
bottle.constant('axios', axios);
|
bottle.constant('axios', axios);
|
||||||
bottle.constant('fetch', (global as any).fetch.bind((global as any)));
|
bottle.constant('fetch', (global as any).fetch.bind((global as any)));
|
||||||
|
bottle.serviceFactory('jsonFetch', jsonFetch, 'fetch');
|
||||||
|
|
||||||
bottle.service('ImageDownloader', ImageDownloader, 'axios', 'window');
|
bottle.service('ImageDownloader', ImageDownloader, 'axios', 'window');
|
||||||
bottle.service('ReportExporter', ReportExporter, 'window', 'jsonToCsv');
|
bottle.service('ReportExporter', ReportExporter, 'window', 'jsonToCsv');
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
import { pipe, prop } from 'ramda';
|
|
||||||
import { AxiosInstance } from 'axios';
|
|
||||||
import pack from '../../../package.json';
|
import pack from '../../../package.json';
|
||||||
import { hasServerData, ServerData } from '../data';
|
import { hasServerData, ServerData } from '../data';
|
||||||
import { createServers } from './servers';
|
import { createServers } from './servers';
|
||||||
import { createAsyncThunk } from '../../utils/helpers/redux';
|
import { createAsyncThunk } from '../../utils/helpers/redux';
|
||||||
|
import { Fetch } from '../../utils/types';
|
||||||
|
|
||||||
const responseToServersList = pipe(
|
const responseToServersList = (data: any): ServerData[] => (Array.isArray(data) ? data.filter(hasServerData) : []);
|
||||||
prop<any, any>('data'),
|
|
||||||
(data: any): ServerData[] => (Array.isArray(data) ? data.filter(hasServerData) : []),
|
|
||||||
);
|
|
||||||
|
|
||||||
export const fetchServers = ({ get }: AxiosInstance) => createAsyncThunk(
|
export const fetchServers = (fetch: Fetch) => createAsyncThunk(
|
||||||
'shlink/remoteServers/fetchServers',
|
'shlink/remoteServers/fetchServers',
|
||||||
async (_: void, { dispatch }): Promise<void> => {
|
async (_: void, { dispatch }): Promise<void> => {
|
||||||
const resp = await get(`${pack.homepage}/servers.json`);
|
const resp = await fetch<any>(`${pack.homepage}/servers.json`);
|
||||||
const result = responseToServersList(resp);
|
const result = responseToServersList(resp);
|
||||||
|
|
||||||
dispatch(createServers(result));
|
dispatch(createServers(result));
|
||||||
|
|
|
@ -80,7 +80,7 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
bottle.serviceFactory('deleteServer', () => deleteServer);
|
bottle.serviceFactory('deleteServer', () => deleteServer);
|
||||||
bottle.serviceFactory('editServer', () => editServer);
|
bottle.serviceFactory('editServer', () => editServer);
|
||||||
bottle.serviceFactory('setAutoConnect', () => setAutoConnect);
|
bottle.serviceFactory('setAutoConnect', () => setAutoConnect);
|
||||||
bottle.serviceFactory('fetchServers', fetchServers, 'axios');
|
bottle.serviceFactory('fetchServers', fetchServers, 'jsonFetch');
|
||||||
|
|
||||||
bottle.serviceFactory('resetSelectedServer', () => resetSelectedServer);
|
bottle.serviceFactory('resetSelectedServer', () => resetSelectedServer);
|
||||||
|
|
||||||
|
|
10
src/utils/helpers/fetch.ts
Normal file
10
src/utils/helpers/fetch.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export const jsonFetch = (fetch: typeof window.fetch) => <T>(url: string, options?: RequestInit) => fetch(url, options)
|
||||||
|
.then(async (resp) => {
|
||||||
|
const parsed = await resp.json();
|
||||||
|
|
||||||
|
if (!resp.ok) {
|
||||||
|
throw parsed; // eslint-disable-line @typescript-eslint/no-throw-literal
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed as T;
|
||||||
|
});
|
|
@ -1,3 +1,3 @@
|
||||||
export type MediaMatcher = (query: string) => MediaQueryList;
|
export type MediaMatcher = (query: string) => MediaQueryList;
|
||||||
|
|
||||||
export type Fetch = typeof window.fetch;
|
export type Fetch = <T>(url: string, options?: RequestInit) => Promise<T>;
|
||||||
|
|
|
@ -6,10 +6,8 @@ import { ShortUrl, ShortUrlsOrder } from '../../../src/short-urls/data';
|
||||||
import { Fetch } from '../../../src/utils/types';
|
import { Fetch } from '../../../src/utils/types';
|
||||||
|
|
||||||
describe('ShlinkApiClient', () => {
|
describe('ShlinkApiClient', () => {
|
||||||
const buildFetch = (data: any) => jest.fn().mockResolvedValue({ json: () => Promise.resolve(data), ok: true });
|
const buildFetch = (data: any) => jest.fn().mockResolvedValue(data);
|
||||||
const buildRejectedFetch = (error: any) => jest.fn().mockResolvedValueOnce(
|
const buildRejectedFetch = (error: any) => jest.fn().mockRejectedValueOnce(error);
|
||||||
{ json: () => Promise.resolve(error), ok: false },
|
|
||||||
);
|
|
||||||
const buildApiClient = (fetch: Fetch) => new ShlinkApiClient(fetch, '', '');
|
const buildApiClient = (fetch: Fetch) => new ShlinkApiClient(fetch, '', '');
|
||||||
const shortCodesWithDomainCombinations: [string, OptionalString][] = [
|
const shortCodesWithDomainCombinations: [string, OptionalString][] = [
|
||||||
['abc123', null],
|
['abc123', null],
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { Mock } from 'ts-mockery';
|
|
||||||
import { AxiosInstance } from 'axios';
|
|
||||||
import { fetchServers } from '../../../src/servers/reducers/remoteServers';
|
import { fetchServers } from '../../../src/servers/reducers/remoteServers';
|
||||||
import { createServers } from '../../../src/servers/reducers/servers';
|
import { createServers } from '../../../src/servers/reducers/servers';
|
||||||
|
|
||||||
|
@ -8,13 +6,11 @@ describe('remoteServersReducer', () => {
|
||||||
|
|
||||||
describe('fetchServers', () => {
|
describe('fetchServers', () => {
|
||||||
const dispatch = jest.fn();
|
const dispatch = jest.fn();
|
||||||
const get = jest.fn();
|
const fetch = jest.fn();
|
||||||
const axios = Mock.of<AxiosInstance>({ get });
|
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[
|
[
|
||||||
{
|
[
|
||||||
data: [
|
|
||||||
{
|
{
|
||||||
id: '111',
|
id: '111',
|
||||||
name: 'acel.me from servers.json',
|
name: 'acel.me from servers.json',
|
||||||
|
@ -28,7 +24,6 @@ describe('remoteServersReducer', () => {
|
||||||
apiKey: '7a531c75-134e-4d5c-86e0-a71b7167b57a',
|
apiKey: '7a531c75-134e-4d5c-86e0-a71b7167b57a',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
|
||||||
{
|
{
|
||||||
111: {
|
111: {
|
||||||
id: '111',
|
id: '111',
|
||||||
|
@ -45,8 +40,7 @@ describe('remoteServersReducer', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
[
|
||||||
data: [
|
|
||||||
{
|
{
|
||||||
id: '111',
|
id: '111',
|
||||||
name: 'acel.me from servers.json',
|
name: 'acel.me from servers.json',
|
||||||
|
@ -64,7 +58,6 @@ describe('remoteServersReducer', () => {
|
||||||
apiKey: '7a531c75-134e-4d5c-86e0-a71b7167b57a',
|
apiKey: '7a531c75-134e-4d5c-86e0-a71b7167b57a',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
|
||||||
{
|
{
|
||||||
111: {
|
111: {
|
||||||
id: '111',
|
id: '111',
|
||||||
|
@ -83,8 +76,8 @@ describe('remoteServersReducer', () => {
|
||||||
['<html></html>', {}],
|
['<html></html>', {}],
|
||||||
[{}, {}],
|
[{}, {}],
|
||||||
])('tries to fetch servers from remote', async (mockedValue, expectedNewServers) => {
|
])('tries to fetch servers from remote', async (mockedValue, expectedNewServers) => {
|
||||||
get.mockResolvedValue(mockedValue);
|
fetch.mockResolvedValue(mockedValue);
|
||||||
const doFetchServers = fetchServers(axios);
|
const doFetchServers = fetchServers(fetch);
|
||||||
|
|
||||||
await doFetchServers()(dispatch, jest.fn(), {});
|
await doFetchServers()(dispatch, jest.fn(), {});
|
||||||
|
|
||||||
|
@ -95,7 +88,7 @@ describe('remoteServersReducer', () => {
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(3, expect.objectContaining({
|
expect(dispatch).toHaveBeenNthCalledWith(3, expect.objectContaining({
|
||||||
type: doFetchServers.fulfilled.toString(),
|
type: doFetchServers.fulfilled.toString(),
|
||||||
}));
|
}));
|
||||||
expect(get).toHaveBeenCalledTimes(1);
|
expect(fetch).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue