import ng from 'core/services/ng'; import {appConfig} from '../../appConfig'; interface IHttpRequestOptions { method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'; url: string; // absolute url payload?: {}; headers?: {[key: string]: any}; urlParams?: {[key: string]: any}; abortSignal?: AbortSignal; } interface IHttpRequestOptionsLocal extends Omit { path: string; // relative to application server } interface IHttpRequestJsonOptionsLocal extends IHttpRequestOptionsLocal { // JSON not available with DELETE method method: 'GET' | 'POST' | 'PATCH' | 'PUT'; } interface IHttpLocalApiErrorResponse { _error: { code: number; message: string; }; _issues: {[key: string]: any}; _status: 'ERR' | string; } export function isHttpApiError(x): x is IHttpLocalApiErrorResponse { return typeof x === 'object' && x['_status'] === 'ERR'; } function httpRequestBase(options: IHttpRequestOptions): Promise { const {method, url, payload, headers, abortSignal} = options; const _url = new URL(url); if (options.urlParams != null) { Object.keys(options.urlParams).forEach((key) => { _url.searchParams.append(key, options.urlParams[key]); }); } return fetch(_url.toString(), { method, headers: headers || {}, mode: 'cors', body: JSON.stringify(payload), // works when `payload` is `undefined` signal: abortSignal, }); } export function httpRequestVoidLocal(options: IHttpRequestOptionsLocal): Promise { return ng.getService('session') .then((session) => { return httpRequestBase({ ...options, url: appConfig.server.url + options.path, urlParams: options.urlParams, headers: { ...(options.headers || {}), 'Authorization': session.token, }, }).then((res) => { if (res.ok) { return Promise.resolve(); } else { // Attempt to convert error response to JSON, // otherwise return body as text as returned from the server return res.text() .then((bodyText) => { try { return Promise.reject(JSON.parse(bodyText)); } catch (_err) { return Promise.reject(bodyText); } }); } }); }); } export function httpRequestJsonLocal(options: IHttpRequestJsonOptionsLocal): Promise { return ng.getService('session') .then((session) => { return httpRequestBase({ ...options, url: appConfig.server.url + options.path, headers: { ...(options.headers || {}), 'Content-Type': 'application/json', 'Authorization': session.token, }, }).then((res) => res.json().then((json) => { if (res.ok) { return json; } else { return Promise.reject(json); } })); }); } export function httpRequestRawLocal(options: IHttpRequestOptionsLocal): Promise { return ng.getService('session') .then((session) => { return httpRequestBase({ ...options, url: appConfig.server.url + options.path, headers: { ...(options.headers || {}), 'Authorization': session.token, }, }).then((res) => { if (res.ok) { return res; } else { return Promise.reject(res); } }); }); } export function uploadFileWithProgress( endpoint: string, data: FormData, onProgress: (event: ProgressEvent) => void, method?: 'POST' | 'PATCH', etag?: string, ): Promise { return ng.getService('session') .then((session) => { return new Promise((resolve, reject) => { // Using `XMLHttpRequest` over `fetch` so we can get `onprogress` reporting const request = new XMLHttpRequest(); const url = appConfig.server.url + endpoint; request.open(method ?? 'POST', url); request.setRequestHeader('Authorization', session.token); if (method === 'PATCH' && etag != null) { request.setRequestHeader('If-Match', etag); } request.upload.onprogress = onProgress; request.onload = function() { if (this.status >= 200 && this.status < 300) { resolve(JSON.parse(this.responseText)); } else { reject(JSON.parse(this.responseText)); } }; request.onerror = function(e: ProgressEvent) { reject(e); }; request.send(data); }); }); }