mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #7871 from overleaf/ii-fetch-json-ts
Convert fetch-json to TS GitOrigin-RevId: 78b0835aca4ba82c38004f37bb5c38ec7fb1b32c
This commit is contained in:
parent
2d8e832be7
commit
30b07f3d34
3 changed files with 58 additions and 97 deletions
|
@ -206,7 +206,7 @@ function useUserEmails() {
|
||||||
const { data, isLoading, isError, isSuccess, runAsync } = useAsync()
|
const { data, isLoading, isError, isSuccess, runAsync } = useAsync()
|
||||||
|
|
||||||
const getEmails = useCallback(() => {
|
const getEmails = useCallback(() => {
|
||||||
runAsync<UserEmailData[]>(getJSON('/user/emails?ensureAffiliation=true'))
|
runAsync(getJSON<UserEmailData[]>('/user/emails?ensureAffiliation=true'))
|
||||||
.then(data => {
|
.then(data => {
|
||||||
dispatch(ActionCreators.setData(data))
|
dispatch(ActionCreators.setData(data))
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,126 +5,86 @@
|
||||||
// - parse JSON response body, unless response is empty
|
// - parse JSON response body, unless response is empty
|
||||||
import OError from '@overleaf/o-error'
|
import OError from '@overleaf/o-error'
|
||||||
|
|
||||||
/**
|
type FetchPath = string
|
||||||
* @typedef {Object} FetchOptions
|
// Custom config types are merged with `fetch`s RequestInit type
|
||||||
* @extends RequestInit
|
type FetchConfig = {
|
||||||
* @property {Object} [body]
|
swallowAbortError?: boolean
|
||||||
* @property {Boolean} [swallowAbortError] Set to false for throwing AbortErrors.
|
body?: Record<string, unknown>
|
||||||
* @property {AbortSignal} [signal] Allows aborting a request via AbortController
|
} & Omit<RequestInit, 'body'>
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
export function getJSON<T = any>(path: FetchPath, options?: FetchConfig) {
|
||||||
* @param {string} path
|
return fetchJSON<T>(path, { ...options, method: 'GET' })
|
||||||
* @param {FetchOptions} [options]
|
|
||||||
*/
|
|
||||||
export function getJSON(path, options) {
|
|
||||||
return fetchJSON(path, { ...options, method: 'GET' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function postJSON<T = any>(path: FetchPath, options?: FetchConfig) {
|
||||||
* @param {string} path
|
return fetchJSON<T>(path, { ...options, method: 'POST' })
|
||||||
* @param {FetchOptions} [options]
|
|
||||||
*/
|
|
||||||
export function postJSON(path, options) {
|
|
||||||
return fetchJSON(path, { ...options, method: 'POST' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function putJSON<T = any>(path: FetchPath, options?: FetchConfig) {
|
||||||
* @param {string} path
|
return fetchJSON<T>(path, { ...options, method: 'PUT' })
|
||||||
* @param {FetchOptions} [options]
|
|
||||||
*/
|
|
||||||
export function putJSON(path, options) {
|
|
||||||
return fetchJSON(path, { ...options, method: 'PUT' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function deleteJSON<T = any>(path: FetchPath, options?: FetchConfig) {
|
||||||
* @param {string} path
|
return fetchJSON<T>(path, { ...options, method: 'DELETE' })
|
||||||
* @param {FetchOptions} [options]
|
|
||||||
*/
|
|
||||||
export function deleteJSON(path, options) {
|
|
||||||
return fetchJSON(path, { ...options, method: 'DELETE' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function getErrorMessageForStatusCode(statusCode?: number) {
|
||||||
* @param {number} statusCode
|
if (!statusCode) {
|
||||||
* @returns {string}
|
return 'Unknown Error'
|
||||||
*/
|
|
||||||
function getErrorMessageForStatusCode(statusCode) {
|
|
||||||
switch (statusCode) {
|
|
||||||
case 400:
|
|
||||||
return 'Bad Request'
|
|
||||||
case 401:
|
|
||||||
return 'Unauthorized'
|
|
||||||
case 403:
|
|
||||||
return 'Forbidden'
|
|
||||||
case 404:
|
|
||||||
return 'Not Found'
|
|
||||||
case 429:
|
|
||||||
return 'Too Many Requests'
|
|
||||||
case 500:
|
|
||||||
return 'Internal Server Error'
|
|
||||||
case 502:
|
|
||||||
return 'Bad Gateway'
|
|
||||||
case 503:
|
|
||||||
return 'Service Unavailable'
|
|
||||||
default:
|
|
||||||
return `Unexpected Error: ${statusCode}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const statusCodes: { readonly [K: number]: string } = {
|
||||||
|
400: 'Bad Request',
|
||||||
|
401: 'Unauthorized',
|
||||||
|
403: 'Forbidden',
|
||||||
|
404: 'Not Found',
|
||||||
|
429: 'Too Many Requests',
|
||||||
|
500: 'Internal Server Error',
|
||||||
|
502: 'Bad Gateway',
|
||||||
|
503: 'Service Unavailable',
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusCodes[statusCode] ?? `Unexpected Error: ${statusCode}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FetchError extends OError {
|
export class FetchError extends OError {
|
||||||
/**
|
constructor(
|
||||||
* @param {string} message
|
message: string,
|
||||||
* @param {string} url
|
public url: string,
|
||||||
* @param {FetchOptions} [options]
|
public options?: RequestInit,
|
||||||
* @param {Response} [response]
|
public response?: Response,
|
||||||
* @param {Object} [data]
|
public data?: any
|
||||||
*/
|
) {
|
||||||
constructor(message, url, options, response, data) {
|
|
||||||
// On HTTP2, the `statusText` property is not set,
|
// On HTTP2, the `statusText` property is not set,
|
||||||
// so this `message` will be undefined. We need to
|
// so this `message` will be undefined. We need to
|
||||||
// set a message based on the response `status`, so
|
// set a message based on the response `status`, so
|
||||||
// our error UI rendering will work
|
// our error UI rendering will work
|
||||||
if (!message) {
|
if (!message) {
|
||||||
message = getErrorMessageForStatusCode(response.status)
|
message = getErrorMessageForStatusCode(response?.status)
|
||||||
}
|
}
|
||||||
super(message, { statusCode: response ? response.status : undefined })
|
super(message, { statusCode: response ? response.status : undefined })
|
||||||
this.url = url
|
|
||||||
this.options = options
|
|
||||||
this.response = response
|
|
||||||
this.data = data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
getUserFacingMessage() {
|
getUserFacingMessage() {
|
||||||
const statusCode = this.response?.status
|
const statusCode = this.response?.status
|
||||||
const defaultMessage = getErrorMessageForStatusCode(statusCode)
|
const defaultMessage = getErrorMessageForStatusCode(statusCode)
|
||||||
const message = this.data?.message?.text || this.data?.message
|
const message = this.data?.message?.text || this.data?.message
|
||||||
if (message && message !== defaultMessage) return message
|
if (message && message !== defaultMessage) return message
|
||||||
|
|
||||||
switch (statusCode) {
|
const statusCodes: { readonly [K: number]: string } = {
|
||||||
case 400:
|
400: 'Invalid Request. Please correct the data and try again.',
|
||||||
return 'Invalid Request. Please correct the data and try again.'
|
403: 'Session error. Please check you have cookies enabled. If the problem persists, try clearing your cache and cookies.',
|
||||||
case 403:
|
429: 'Too many attempts. Please wait for a while and try again.',
|
||||||
return 'Session error. Please check you have cookies enabled. If the problem persists, try clearing your cache and cookies.'
|
|
||||||
case 429:
|
|
||||||
return 'Too many attempts. Please wait for a while and try again.'
|
|
||||||
default:
|
|
||||||
return 'Something went wrong. Please try again.'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return statusCode && statusCodes[statusCode]
|
||||||
|
? statusCodes[statusCode]
|
||||||
|
: 'Something went wrong. Please try again.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function fetchJSON<T>(
|
||||||
* @param {string} path
|
path: FetchPath,
|
||||||
* @param {FetchOptions} [options]
|
|
||||||
*
|
|
||||||
* @return Promise<Object>
|
|
||||||
*/
|
|
||||||
function fetchJSON(
|
|
||||||
path,
|
|
||||||
{
|
{
|
||||||
body = {},
|
body = {},
|
||||||
headers = {},
|
headers = {},
|
||||||
|
@ -132,9 +92,9 @@ function fetchJSON(
|
||||||
credentials = 'same-origin',
|
credentials = 'same-origin',
|
||||||
swallowAbortError = true,
|
swallowAbortError = true,
|
||||||
...otherOptions
|
...otherOptions
|
||||||
}
|
}: FetchConfig
|
||||||
) {
|
) {
|
||||||
const options = {
|
const options: RequestInit = {
|
||||||
...otherOptions,
|
...otherOptions,
|
||||||
headers: {
|
headers: {
|
||||||
...headers,
|
...headers,
|
||||||
|
@ -155,7 +115,7 @@ function fetchJSON(
|
||||||
// after a component has unmounted.
|
// after a component has unmounted.
|
||||||
// `resolve` will be called when the request succeeds, `reject` will be called when the request fails,
|
// `resolve` will be called when the request succeeds, `reject` will be called when the request fails,
|
||||||
// but nothing will be called if the request is cancelled via an AbortController.
|
// but nothing will be called if the request is cancelled via an AbortController.
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<T>((resolve, reject) => {
|
||||||
fetch(path, options).then(
|
fetch(path, options).then(
|
||||||
response => {
|
response => {
|
||||||
return parseResponseBody(response).then(
|
return parseResponseBody(response).then(
|
||||||
|
@ -206,13 +166,13 @@ function fetchJSON(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async function parseResponseBody(response: Response) {
|
||||||
* @param {Response} response
|
|
||||||
* @returns {Promise<Object>}
|
|
||||||
*/
|
|
||||||
async function parseResponseBody(response) {
|
|
||||||
const contentType = response.headers.get('Content-Type')
|
const contentType = response.headers.get('Content-Type')
|
||||||
|
|
||||||
|
if (!contentType) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
if (/application\/json/.test(contentType)) {
|
if (/application\/json/.test(contentType)) {
|
||||||
return response.json()
|
return response.json()
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import { OAuthProviders } from './oauth-providers'
|
||||||
declare global {
|
declare global {
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
interface Window {
|
interface Window {
|
||||||
|
csrfToken: string
|
||||||
sl_debugging: boolean
|
sl_debugging: boolean
|
||||||
user: {
|
user: {
|
||||||
id: string
|
id: string
|
||||||
|
|
Loading…
Reference in a new issue