All files / app/service/api stomp.service.ts

53.06% Statements 26/49
40% Branches 22/55
29.41% Functions 5/17
51.35% Lines 19/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 10475x     75x 75x   75x             28x                   75x         28x 28x   28x 28x           28x 28x   28x 28x                   28x       30x 30x 29x             30x                                                                            
import { Injectable, isDevMode } from '@angular/core';
import { RxStomp, RxStompConfig } from '@stomp/rx-stomp';
import { map, Observable } from 'rxjs';
import { Ext, mapExt } from '../../model/ext';
import { mapRef, RefUpdates } from '../../model/ref';
import { Store } from '../../store/store';
import { isSubOrigin, localTag, tagOrigin } from '../../util/tag';
import { ConfigService } from '../config.service';
 
 
function isTestEnvironment(): boolean {
  // Check for Vitest/Jest test environment
  // @ts-ignore
  Eif (typeof globalThis !== 'undefined' && (globalThis.__vitest_worker__ || globalThis.jest)) return true;
  // Check for Zone.js test zone (Angular TestBed)
  // @ts-ignore
  if (typeof Zone !== 'undefined' && Zone.current?.name === 'ProxyZone') return true;
  return false;
}
 
@Injectable({
  providedIn: 'root'
})
export class StompService extends RxStomp {
 
  private stompConfig: RxStompConfig;
 
  constructor(
    private config: ConfigService,
    private store: Store,
  ) {
    super();
    this.stompConfig = {
      brokerURL: this.brokerURL,
      heartbeatIncoming: 20000,
      heartbeatOutgoing: 0,
      reconnectDelay: 2000,
    };
    Eif (isDevMode()) {
      this.stompConfig.debug = (msg: string) => console.debug('📶️  '+ msg);
    }
    this.configure(this.stompConfig);
    Iif (this.config.websockets && !isTestEnvironment()) this.activate();
  }
 
  get headers() {
    return {
      jwt: this.config.token,
    };
  }
 
  get brokerURL() {
    return `${this.hostUrl}/api/stomp/websocket`
  }
 
  get hostUrl() {
    var proto = this.getWsProtocol(this.config.api);
    if (this.config.api === '.' || this.config.api === '/' || this.config.api === './') return proto + location.host;
    Eif (this.config.api.startsWith('//')) return proto + this.config.api.substring('//'.length);
    if (this.config.api.startsWith('https://')) return proto + this.config.api.substring('https://'.length);
    if (this.config.api.startsWith('http://')) return proto + this.config.api.substring('http://'.length);
    return proto + this.config.api;
  }
 
  getWsProtocol(url = '') {
    return url.startsWith('https:') ? 'wss://' :
           url.startsWith('http:') ? 'ws://' :
           location.protocol === 'https:' ? 'wss://' :
           'ws://';
  }
 
  watchOrigin(origin: string): Observable<string> {
    return this.watch('/topic/cursor/' + (origin || 'default'), this.headers).pipe(
      map(m => m.body),
    );
  }
 
  watchRef(url: string, origin?: string): Observable<RefUpdates> {
    origin = (origin === undefined ? this.store.account.origin : origin) || 'default';
    return this.watch('/topic/ref/' + origin + '/' + encodeURIComponent(url), this.headers).pipe(
      map(m => mapRef(JSON.parse(m.body))),
    );
  }
 
  watchTag(tag: string): Observable<string> {
    const origin = isSubOrigin(this.store.account.origin, tagOrigin(tag)) ? tagOrigin(tag) : this.store.account.origin;
    return this.watch('/topic/tag/' + (origin || 'default') + '/' + encodeURIComponent(localTag(tag)), this.headers).pipe(
      map(m => m.body as string),
    );
  }
 
  watchResponse(url: string): Observable<string> {
    return this.watch('/topic/response/' + (this.store.account.origin || 'default') + '/' + encodeURIComponent(url), this.headers).pipe(
      map(m => m.body as string),
    );
  }
 
  watchExt(tag: string): Observable<Ext> {
    return this.watch('/topic/ext/' + (tagOrigin(tag) || this.store.account.origin || 'default') + '/' + encodeURIComponent(localTag(tag)), this.headers).pipe(
      map(m => mapExt(JSON.parse(m.body))),
    );
  }
}