All files networkMessage.ts

90.19% Statements 46/51
55.55% Branches 10/18
87.5% Functions 7/8
91.48% Lines 43/47

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                        4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x   4x 4x 4x 4x 4x 4x 4x     4x 4x 4x       1x 1x       1x   1x               1x   1x 1x           1x                           2x 2x 2x 1x   2x     2x 2x 2x 1x   2x       9x 6x   3x        
import {
  RequestResponseData,
  INetworkMessage,
  httpMethod,
  RequestState,
} from './types'
/**
 * I know we're not using most of the information from this class
 * but it can be useful in the future if we will decide to display more stuff in our ui
 * */
 
export default class NetworkMessage {
  id = ''
  name?: string = ''
  method: httpMethod = ''
  url = ''
  status = 0
  statusText?: string = ''
  cancelState?: 0 | 1 | 2 | 3 = 0
  readyState?: RequestState = 0
  header: { [key: string]: string } = {}
  responseType: XMLHttpRequest['responseType'] = ''
  requestType: 'xhr' | 'fetch' | 'ping' | 'custom' | 'beacon' | 'graphql' = 'xhr'
  requestHeader: HeadersInit = {}
  response: string
  responseSize = 0 // bytes
  responseSizeText = ''
  startTime = 0
  endTime = 0
  duration = 0
  getData: { [key: string]: string } = {}
  requestData: string | null = null
 
  constructor(
    private readonly ignoredHeaders: boolean | string[] = [],
    private readonly setSessionTokenHeader: (cb: (name: string, value: string) => void) => void,
    private readonly sanitize: (data: RequestResponseData) => RequestResponseData | null,
  ) {}
 
  getMessage(): INetworkMessage | null {
    const { reqHs, resHs } = this.writeHeaders()
    const request = {
      headers: reqHs,
      body: this.method === 'GET' ? JSON.stringify(this.getData) : this.requestData,
    }
    const response = { headers: resHs, body: this.response }
 
    const messageInfo = this.sanitize({
      url: this.url,
      method: this.method,
      status: this.status,
      request,
      response,
    })
 
    Iif (!messageInfo) return null;
 
    const isGraphql = messageInfo.url.includes("/graphql");
    Iif (isGraphql && messageInfo.response.body && typeof messageInfo.response.body === 'string') {
      const isError = messageInfo.response.body.includes("errors");
      messageInfo.status = isError ? 400 : 200;
      this.requestType = 'graphql';
    }
 
    return {
      requestType: this.requestType,
      method: messageInfo.method as httpMethod,
      url: messageInfo.url,
      request: JSON.stringify(messageInfo.request),
      response: JSON.stringify(messageInfo.response),
      status: messageInfo.status,
      startTime: this.startTime,
      duration: this.duration,
      responseSize: this.responseSize,
    }
  }
 
  writeHeaders() {
    const reqHs: Record<string, string> = {}
    Object.entries(this.requestHeader).forEach(([key, value]) => {
      if (this.isHeaderIgnored(key)) return
      reqHs[key] = value
    })
    this.setSessionTokenHeader((name, value) => {
      reqHs[name] = value
    })
    const resHs: Record<string, string> = {}
    Object.entries(this.header).forEach(([key, value]) => {
      if (this.isHeaderIgnored(key)) return
      resHs[key] = value
    })
    return { reqHs, resHs }
  }
 
  isHeaderIgnored(key: string) {
    if (Array.isArray(this.ignoredHeaders)) {
      return this.ignoredHeaders.map((k) => k.toLowerCase()).includes(key.toLowerCase())
    } else {
      return this.ignoredHeaders
    }
  }
}