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

60% Statements 30/50
49.09% Branches 27/55
50% Functions 9/18
60% Lines 24/40

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 104 105 106 107 108 109 110130x     130x 130x   130x             1x                   313x         313x 313x   313x 313x           313x 313x   313x       1x 1x 1x       66x           314x       316x 316x 316x 314x             316x                                                                 66x          
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);
  }
 
  initialize() {
    this.stompConfig.brokerURL = this.brokerURL;
    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() {
    const api = this.config.api || '.';
    var proto = this.getWsProtocol(api);
    if (api === '.' || api === '/' || api === './') return proto + location.host;
    Eif (api.startsWith('//')) return proto + api.substring('//'.length);
    if (api.startsWith('https://')) return proto + api.substring('https://'.length);
    if (api.startsWith('http://')) return proto + api.substring('http://'.length);
    return proto + 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))),
    );
  }
}