api-debug.component.ts 3.44 KiB
import { HttpRequest, HttpResponseBase } from '@angular/common/http';
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  EventEmitter,
  Output,
} from '@angular/core';
// TODO injecter
import { Configuration } from '../configuration';
import { DebugStateService } from '../debug-state.service';
import { ApiDebugInterceptor, DebugEvent } from './api-debug.interceptor';
interface RequestInfo {
  id: string;
  url: string;
  status: 'pending' | 'success' | 'error' | 'cancelled';
  method: string;
  response?: ResponseInfo;
interface ResponseInfo {
  status: number;
  statusText: string;
  token?: string;
  tokenLink?: string;
@Component({
  selector: 'dbg-api-debug',
  templateUrl: './api-debug.component.html',
  styleUrls: ['./api-debug.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
export class ApiDebugComponent {
  private readonly requests = new Map<string, RequestInfo>();
  private readonly unseen = new Set<string>();
  private readonly ids: string[] = [];
  private readonly apiPrefix: string;
  @Output() public countChanged = new EventEmitter<number>();
  constructor(
    interceptor: ApiDebugInterceptor,
    public readonly debug: DebugStateService,
    private readonly cd: ChangeDetectorRef,
    conf: Configuration
  ) {
    this.apiPrefix = conf.apiBaseURL;
    interceptor.events$.subscribe(ev => {
      this.handle(ev);
    });
  public get unseenCount(): number {
    return this.unseen.size;
  public get totalCount(): number {
    return this.ids.length;
  public get last(): RequestInfo | null {
    return this.ids.length ? this.requests.get(this.ids[0]) : null;
  public get previous(): RequestInfo[] {
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
return this.ids.slice(1).map(id => this.requests.get(id)); } public get all(): RequestInfo[] { return this.ids.map(id => this.requests.get(id)); } public isUnseen({ id }: RequestInfo): boolean { return this.unseen.has(id); } public seen({ id }: RequestInfo): void { if (this.unseen.has(id)) { this.unseen.delete(id); this.cd.markForCheck(); } } private handle(ev: DebugEvent): void { if (ev.type === 'request') { this.addRequest(ev.id, ev.request); this.countChanged.emit(this.ids.length); this.cd.markForCheck(); return; } const request = this.requests.get(ev.id); if (!request || request.status !== 'pending') { return; } if (ev.type === 'response') { this.setResponse(request, ev.response); } else { this.cancelRequest(request); } this.cd.markForCheck(); } private addRequest( id: string, { urlWithParams, method }: HttpRequest<any> ): void { if (urlWithParams.startsWith(this.apiPrefix)) { urlWithParams = new URL(urlWithParams).pathname; } const req: RequestInfo = { id, method, url: urlWithParams, status: 'pending', }; this.requests.set(id, req); this.unseen.add(id); this.ids.unshift(id); } private setResponse( request: RequestInfo, { status, statusText, headers }: HttpResponseBase ): void { request.response = { status, statusText, token: headers.get('X-Debug-Token'), tokenLink: headers.get('X-Debug-Token-Link'), }; if (status < 100 || status >= 400) { request.status = 'error'; } else { request.status = 'success';
141142143144145146147148
} } private cancelRequest(request: RequestInfo): void { request.status = 'cancelled'; } }