@amanooo/fetch
Version:
custom fetch
164 lines (135 loc) • 4.69 kB
text/typescript
import { commonHeaders, obj2urlParams, timeout, delay, filter200Status, cost, commonRetryOpts } from './util'
import * as fetchImport from 'isomorphic-unfetch'
const fetch = (fetchImport.default || fetchImport) as typeof fetchImport.default
enum Color {
// blue = 'color:blue',
// red = 'color:red'
blue = '',
red = ''
}
type Request = RequestInit
interface RetryOptions {
retries?: number
timeout?: number,
delay?: number
}
export default class Fetch {
static headers = commonHeaders()
static credentials: RequestCredentials = "same-origin"
static debug: boolean = false
static retryOpts: RetryOptions = commonRetryOpts()
public static async get(url: string, params: Object = {}, options: RequestInit = {}, retryOpts: RetryOptions = {}): Promise<any> {
const completeUrl = url + obj2urlParams(params)
const _options = {
headers: { ...this.headers, 'content-type': 'text/plain' } as HeadersInit,
credentials: this.credentials,
...options
}
const result = await this.fetch(completeUrl, _options, retryOpts)
return result
}
public static async post(url: string, body: any = {}, options: RequestInit = {}, retryOpts: RetryOptions = {}): Promise<any> {
this.debug && console.debug('__post__:', url)
const _options = {
method: 'POST',
body: JSON.stringify(body),
headers: this.headers,
credentials: this.credentials,
...options
}
const result = await this.fetch(url, _options, retryOpts)
return result
}
public static async put(url: string, body: Object = {}, options: RequestInit = {}, retryOpts: RetryOptions = {}): Promise<any> {
const _options = {
method: 'PUT',
body: JSON.stringify(body),
headers: this.headers,
credentials: this.credentials,
...options
}
const result = await this.fetch(url, _options, retryOpts)
return result
}
public static async delete(url: string, body: Object = {}, options: RequestInit = {}, retryOpts: RetryOptions = {}): Promise<any> {
const _options = {
method: 'DELETE',
body: JSON.stringify(body),
headers: this.headers,
credentials: this.credentials,
...options
}
const result = await this.fetch(url, _options, retryOpts)
return result
}
public static async fetch(url: string, options: RequestInit, retryOpts: RetryOptions = {}) {
const start = new Date()
const mark = `__${(options.method || 'GET').toLowerCase()}__`
this.debug && console.debug('%s: %s, options', mark, url, options)
const _retryOpts = {
...commonRetryOpts(),
...this.retryOpts,
...retryOpts
}
const action = (_: Function) => {
return fetch(url, options)
.then(filter200Status)
.catch((e: Error) => {
console.error('%s %s:%s Error Cost %f ms', Color.red, mark, url, cost(start), e)
throw e
})
}
const result = await retry(action, _retryOpts)
this.debug && console.debug('%s %s: %s Success Cost %f ms res: %o', Color.blue, mark, url, cost(start), result)
return result
}
}
function bail(err: Error) {
throw (err || new Error('Aborted'))
}
type Action = (bail?: Function) => Promise<any>
async function retry(action: Action, options) {
const { retries, timeout: _timeout, delay: _dealy } = options
function doAction() {
return Promise.race([timeout(_timeout), action()])
}
let times = 1
let response: any
while (times <= retries) {
try {
response = await doAction()
break
} catch (error) {
if (times >= retries) {
throw new Error(error)
}
await delay(_dealy)
} finally {
times++
}
}
return response
}
interface SucessResult<T> {
error: null
payload: T
}
interface FailResult<T> {
error: string
payload: null
}
export type IResultUtil<T = any> = SucessResult<T> | FailResult<T>
export class ResultUtil {
static send(error: string, payload?): IResultUtil<any> {
return {
error,
payload
}
}
static success(payload: any): IResultUtil {
return ResultUtil.send(null, payload)
}
static fail(error = 'DEFAULT ERROR') {
return ResultUtil.send(error)
}
}