github.com/argoproj/argo-cd/v3@v3.2.1/ui/src/app/shared/services/requests.ts (about) 1 import * as path from 'path'; 2 import * as agent from 'superagent'; 3 4 import {BehaviorSubject, Observable, Observer} from 'rxjs'; 5 import {filter} from 'rxjs/operators'; 6 7 type Callback = (data: any) => void; 8 9 declare class EventSource { 10 public onopen: Callback; 11 public onmessage: Callback; 12 public onerror: Callback; 13 public readyState: number; 14 constructor(url: string); 15 public close(): void; 16 } 17 18 enum ReadyState { 19 CONNECTING = 0, 20 OPEN = 1, 21 CLOSED = 2, 22 DONE = 4 23 } 24 25 let baseHRef = '/'; 26 27 const onError = new BehaviorSubject<agent.ResponseError>(null); 28 29 function toAbsURL(val: string): string { 30 return path.join(baseHRef, val); 31 } 32 33 function apiRoot(): string { 34 return toAbsURL('/api/v1'); 35 } 36 37 function initHandlers(req: agent.Request) { 38 req.on('error', err => onError.next(err)); 39 return req; 40 } 41 42 export default { 43 setBaseHRef(val: string) { 44 baseHRef = val; 45 }, 46 agent, 47 toAbsURL, 48 onError: onError.asObservable().pipe(filter(err => err != null)), 49 get(url: string) { 50 return initHandlers(agent.get(`${apiRoot()}${url}`)); 51 }, 52 53 post(url: string) { 54 return initHandlers(agent.post(`${apiRoot()}${url}`)).set('Content-Type', 'application/json'); 55 }, 56 57 put(url: string) { 58 return initHandlers(agent.put(`${apiRoot()}${url}`)).set('Content-Type', 'application/json'); 59 }, 60 61 patch(url: string) { 62 return initHandlers(agent.patch(`${apiRoot()}${url}`)).set('Content-Type', 'application/json'); 63 }, 64 65 delete(url: string) { 66 return initHandlers(agent.del(`${apiRoot()}${url}`)).set('Content-Type', 'application/json'); 67 }, 68 69 loadEventSource(url: string): Observable<string> { 70 return Observable.create((observer: Observer<any>) => { 71 const fullUrl = `${apiRoot()}${url}`; 72 73 const abortController = new AbortController(); 74 75 // If there is an error, show it beforehand 76 fetch(fullUrl, {signal: abortController.signal}) 77 .then(response => { 78 if (!response.ok) { 79 return response.text().then(text => { 80 observer.error({status: response.status, statusText: response.statusText, body: text}); 81 }); 82 } 83 }) 84 .catch(err => { 85 if (err.name === 'AbortError') { 86 return; 87 } 88 observer.error(err); 89 }); 90 91 let eventSource = new EventSource(fullUrl); 92 eventSource.onmessage = msg => observer.next(msg.data); 93 eventSource.onerror = e => () => { 94 observer.error(e); 95 onError.next(e); 96 }; 97 98 // EventSource does not provide easy way to get notification when connection closed. 99 // check readyState periodically instead. 100 const interval = setInterval(() => { 101 if (eventSource && eventSource.readyState === ReadyState.CLOSED) { 102 observer.error('connection got closed unexpectedly'); 103 } 104 }, 500); 105 return () => { 106 clearInterval(interval); 107 eventSource.close(); 108 abortController.abort(); 109 eventSource = null; 110 }; 111 }); 112 } 113 };