import fetch from 'cross-fetch' import { END_POINT_PROTECTED, END_POINT_PUBLIC } from './constants' import { AUTH_TYPE } from './types' import { getErrorMessage, useAuth, validateBaseUrl } from './util' export class FetchClient { baseUrl: string headers: Record constructor( baseUrl: URL, public onError: (message: string) => Promise = ( message: string, ) => { // eslint-disable-next-line no-console console.error(message) return Promise.resolve() }, headers: Record = {}, public authHeader: Record = {}, public protectedRoutes = END_POINT_PROTECTED, public publicRoutes = END_POINT_PUBLIC, public authType: AUTH_TYPE | undefined = undefined, ) { this.baseUrl = validateBaseUrl(baseUrl.toString()) + '/' this.headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', ...headers, } } /** will also throw if onError is defined */ private async fetch( url: string, method: 'get' | 'post' | 'delete' | 'options', headers?: HeadersInit, body?: BodyInit, ): Promise<{ data: T; headers: Headers }> { try { headers = { ...this.headers, ...headers } if ( useAuth( url, method, this.authType, this.protectedRoutes, this.publicRoutes, ) ) headers = { ...this.authHeader, ...headers } const response = await fetch(this.baseUrl + url, { body, headers, method, }) if (response.status >= 400) throw response return { data: await response.json(), headers: response.headers, } } catch (error) { const message = await getErrorMessage(error) await this.onError(message) throw new Error(message) } } async get(url: string, headers?: Record): Promise { return (await this.fetch(url, 'get', headers)).data } async getAll( url: string, headers?: Record, ): Promise { const response = await this.fetch(url, 'get', headers) const result = response.data ?? [] const loadMore = result.length && !url.includes('?page=') && !url.includes('&page=') && !url.includes('?offset=') && !url.includes('&offset=') if (loadMore) { const totalPages = Number(response.headers.get('X-WP-TotalPages')) if (totalPages > 1) { const pages = ( await Promise.all( Array(totalPages - 1) .fill(null) .map((_null, i) => { return this.fetch( `${url}&page=${String(i + 2)}`, 'get', headers, ) }), ) ).map(page => page.data ?? []) const entries: T[] = [] pages.forEach(page => entries.push(...page)) result.push(...entries) } } return result } async getTotal(url: string): Promise { const response = await this.fetch(url, 'options') return Number(response.headers.get('X-WP-TotalPages')) } async post( url: string, headers?: Record, body?: BodyInit, ): Promise { return (await this.fetch(url, 'post', headers, body)).data } async delete(url: string, headers?: Record): Promise { return (await this.fetch(url, 'delete', headers)).data } }