soapbox/app/soapbox/api/index.ts

98 wiersze
3.1 KiB
TypeScript
Czysty Zwykły widok Historia

/**
* API: HTTP client and utilities.
* @see {@link https://github.com/axios/axios}
* @module soapbox/api
*/
2020-03-27 20:59:38 +00:00
'use strict';
2022-03-14 23:06:42 +00:00
import axios, { AxiosInstance, AxiosResponse } from 'axios';
2020-03-27 20:59:38 +00:00
import LinkHeader from 'http-link-header';
2021-08-22 01:41:29 +00:00
import { createSelector } from 'reselect';
2022-11-15 20:48:54 +00:00
import * as BuildConfig from 'soapbox/build-config';
2022-03-14 23:06:42 +00:00
import { RootState } from 'soapbox/store';
import { getAccessToken, getAppToken, isURL, parseBaseURL } from 'soapbox/utils/auth';
2020-03-27 20:59:38 +00:00
2022-06-21 18:14:45 +00:00
import type MockAdapter from 'axios-mock-adapter';
/**
2022-11-15 13:43:26 +00:00
Parse Link headers, mostly for pagination.
@see {@link https://www.npmjs.com/package/http-link-header}
@param {object} response - Axios response object
@returns {object} Link object
*/
2022-03-14 23:06:42 +00:00
export const getLinks = (response: AxiosResponse): LinkHeader => {
return new LinkHeader(response.headers?.link);
2020-03-27 20:59:38 +00:00
};
2022-04-24 03:31:49 +00:00
export const getNextLink = (response: AxiosResponse): string | undefined => {
return getLinks(response).refs.find(link => link.rel === 'next')?.uri;
};
2022-03-14 23:06:42 +00:00
const getToken = (state: RootState, authType: string) => {
2021-03-31 19:47:54 +00:00
return authType === 'app' ? getAppToken(state) : getAccessToken(state);
};
2020-04-29 19:06:26 +00:00
2022-03-14 23:06:42 +00:00
const maybeParseJSON = (data: string) => {
2021-03-31 19:47:54 +00:00
try {
return JSON.parse(data);
} catch (Exception) {
2021-03-31 19:47:54 +00:00
return data;
}
};
2020-04-29 19:06:26 +00:00
2021-08-22 01:41:29 +00:00
const getAuthBaseURL = createSelector([
2022-03-14 23:06:42 +00:00
(state: RootState, me: string | false | null) => state.accounts.getIn([me, 'url']),
(state: RootState, _me: string | false | null) => state.auth.me,
2021-08-22 01:41:29 +00:00
], (accountUrl, authUserUrl) => {
const baseURL = parseBaseURL(accountUrl) || parseBaseURL(authUserUrl);
return baseURL !== window.location.origin ? baseURL : '';
});
/**
2022-11-15 13:43:26 +00:00
* Base client for HTTP requests.
* @param {string} accessToken
* @param {string} baseURL
* @returns {object} Axios instance
*/
export const baseClient = (accessToken?: string | null, baseURL: string = ''): AxiosInstance => {
2020-04-04 20:28:57 +00:00
return axios.create({
// When BACKEND_URL is set, always use it.
2022-03-14 23:06:42 +00:00
baseURL: isURL(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : baseURL,
2020-06-09 19:45:48 +00:00
headers: Object.assign(accessToken ? {
2020-04-29 19:06:26 +00:00
'Authorization': `Bearer ${accessToken}`,
2020-04-04 20:28:57 +00:00
} : {}),
2021-03-31 19:47:54 +00:00
transformResponse: [maybeParseJSON],
2020-04-04 20:28:57 +00:00
});
};
2021-03-31 19:47:54 +00:00
/**
2022-11-15 13:43:26 +00:00
* Dumb client for grabbing static files.
* It uses FE_SUBDIRECTORY and parses JSON if possible.
* No authorization is needed.
*/
export const staticClient = axios.create({
2022-03-14 23:06:42 +00:00
baseURL: BuildConfig.FE_SUBDIRECTORY,
transformResponse: [maybeParseJSON],
});
/**
2022-11-15 13:43:26 +00:00
* Stateful API client.
* Uses credentials from the Redux store if available.
* @param {function} getState - Must return the Redux state
* @param {string} authType - Either 'user' or 'app'
* @returns {object} Axios instance
*/
2022-03-14 23:06:42 +00:00
export default (getState: () => RootState, authType: string = 'user'): AxiosInstance => {
2021-08-22 01:41:29 +00:00
const state = getState();
const accessToken = getToken(state, authType);
2022-03-14 23:06:42 +00:00
const me = state.me;
2022-04-04 19:24:42 +00:00
const baseURL = me ? getAuthBaseURL(state, me) : '';
2021-08-22 01:41:29 +00:00
return baseClient(accessToken, baseURL);
2021-03-31 19:47:54 +00:00
};
2022-06-21 18:14:45 +00:00
// The Jest mock exports these, so they're needed for TypeScript.
export const __stub = (_func: (mock: MockAdapter) => void) => 0;
export const __clear = (): Function[] => [];