import {catchError, mergeMap, Observable, retryWhen, timer} from "rxjs"; import {HttpClient, HttpErrorResponse} from "@angular/common/http"; import {NotifyColor, OpenNotifyService} from "@service/open-notify.service"; import {environment} from "@/config/environment"; import {Router} from "@angular/router"; import {Injectable} from "@angular/core"; export function retryWithInterval(): (source: Observable) => Observable { return (source: Observable) => source.pipe( retryWhen((errors: Observable) => errors.pipe( mergeMap((error, index) => { if (index < (environment.maxRetry < 0 ? Infinity : environment.maxRetry - 1) && !error.status.toString().startsWith('4') && !error.status.toString().startsWith('5')) { console.log(`Retrying after ${environment.retryDelay}ms...`); return timer(environment.retryDelay); } else { if (error.status.toString().startsWith('4')) console.error(`Server returned a client code error`); else console.error(`Exceeded maximum retries (${environment.maxRetry})`); throw error; } }) ) ) ); } /* @Injectable({ providedIn: 'root' }) */ export enum AvailableVersion { v1 } @Injectable() export default abstract class ApiService { constructor(private http: HttpClient, private notify: OpenNotifyService, private router: Router) { } private urlApi = environment.apiUrl; protected abstract basePath: string; protected abstract version: AvailableVersion; private static addQuery(endpoint: string, queryParams?: Record | null> | null): string { const url = new URL(endpoint); if (queryParams) { Object.keys(queryParams).forEach(key => { const value = queryParams[key]; if (value !== null && value !== undefined) { if (typeof(value) === typeof(Array)) { (value as Array).forEach(x => url.searchParams.append(key, x.toString())); } else url.searchParams.append(key, value.toString()); } }); } return url.href; } private static combineUrls(...parts: string[]): string { let test = parts.map(part => part.replace(/(^\/+|\/+$)/g, '')).join('/'); console.log(test); return test; } public get(endpoint: string = '', queryParams: Record | null> | null = null): Observable { return this.http.get(ApiService.addQuery(ApiService.combineUrls(this.urlApi, AvailableVersion[this.version], this.basePath, endpoint), queryParams), {withCredentials: true}).pipe( retryWithInterval(), catchError(error => { this.handleError(error); throw error; }) ); } public post(endpoint: string, data: any, queryParams: Record | null> | null = null): Observable { return this.http.post(ApiService.addQuery(ApiService.combineUrls(this.urlApi, AvailableVersion[this.version], this.basePath, endpoint), queryParams), data, {withCredentials: true}).pipe( retryWithInterval(), catchError(error => { this.handleError(error); throw error; }) ); } public put(endpoint: string, data: any, queryParams: Record | null> | null = null): Observable { return this.http.put(ApiService.addQuery(ApiService.combineUrls(this.urlApi, AvailableVersion[this.version], this.basePath, endpoint), queryParams), data, {withCredentials: true}).pipe( retryWithInterval(), catchError(error => { this.handleError(error); throw error; }) ); } public delete(endpoint: string): Observable { return this.http.delete(ApiService.combineUrls(this.urlApi, AvailableVersion[this.version], this.basePath, endpoint), {withCredentials: true}).pipe( retryWithInterval(), catchError(error => { this.handleError(error); throw error; }) ); } private handleError(error: HttpErrorResponse): void { // todo: change to Retry-After condition if (error.error.toString().includes("setup")) { this.router.navigate(['/setup/']); return; } let message: string; if (error.error instanceof ErrorEvent) { message = `Произошла ошибка: ${error.error.message}`; } else { switch (error.status) { case 0: message = 'Неизвестная ошибка. Пожалуйста, попробуйте позже.'; break; case 400: message = 'Ошибка запроса. Пожалуйста, проверьте отправленные данные.'; break; case 401: message = 'Ошибка авторизации. Пожалуйста, выполните вход с правильными учетными данными.'; break; case 403: message = 'Отказано в доступе. У вас нет разрешения на выполнение этого действия.'; break; case 404: message = 'Запрашиваемый ресурс не найден.'; break; case 500: message = 'Внутренняя ошибка сервера. Пожалуйста, попробуйте позже.'; break; case 503: message = 'Сервер на обслуживании. Пожалуйста, попробуйте позже.'; break; default: message = `Сервер вернул код ошибки: ${error.status}`; break; } if (error.error?.Error) { message += ` ${error.error.Error}`; } } this.notify.open(message, NotifyColor.Danger); } }