github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/experiment/tracer/static/script.js (about)

     1  'use strict';
     2  
     3  const PARAMS = 5;
     4  const HOST = 'github.com';
     5  
     6  var logger = {
     7    data: [],
     8    message: '',
     9    options: {
    10      request: {
    11        endpoint: 'https://' + window.location.hostname + '/trace?',
    12        params: '',
    13        config: {}
    14      },
    15      messages: {
    16        invalidInput: 'Invalid input. Please pass a link from a PR or PR comment and profit',
    17        invalidHost: '{value} is an invalid host. Host needs to be github.com',
    18        invalidParams: 'The following {value} provided as params. Need either "pr", "repo", and "org", or  "issuecomment" to be specified',
    19        invalidUrl: '{value} is an invalid link. Failed to construct \'URL\'',
    20        requestError: 'Fetch error: Status is: {value}',
    21        requestLoading: 'Loading..',
    22        emptyLogs: 'No logs to display'
    23      }
    24    },
    25  
    26    // the actual fetch
    27    request: function(endpoint) {
    28      generateView.toggleElement(generateView.loader, true);
    29      fetch(endpoint, {credentials: 'same-origin'}).then(
    30          function(response) {
    31  
    32            if (response.status !== 200) {
    33              return Promise.reject(response.status);
    34            } else {
    35              generateView.toggleElement(generateView.messageHolder, false);
    36            }
    37  
    38            response.json().then(function(data) {
    39              controller.setData(data);
    40            });
    41          }
    42      ).catch(function(err) {
    43        controller.setMessage(logger.options.messages.requestError, err);
    44      }).finally(function(){
    45        generateView.toggleElement(generateView.loader, false);
    46      });
    47  
    48    }
    49  };
    50  
    51  var controller = {
    52  
    53    init: function() {
    54      generateView.init();
    55    },
    56  
    57    // Getter & Setters
    58  
    59    setParams: function(params) {
    60      logger.options.request.params = params;
    61    },
    62  
    63    setData: function(data) {
    64      logger.data = data;
    65      // render this view (update the DOM elements with the updated values)
    66      generateView.render();
    67    },
    68  
    69    getParams: function() {
    70      return logger.options.request.endpoint + encodeURI(logger.options.request.params);
    71    },
    72  
    73    setMessage: function(msg, value) {
    74      generateView.toggleElement(generateView.messageHolder, true);
    75      logger.message = msg.replace('{value}', value);
    76      generateView.updateMsg();
    77    },
    78  
    79    getMessage: function() {
    80      return logger.message;
    81    },
    82  
    83    // fetch data
    84    requestLogs: function() {
    85      //  params needed
    86      return logger.request(this.getParams());
    87    },
    88  
    89    buildParams: function(userValue) {
    90  
    91      if (userValue.length <= 1) {
    92        this.setMessage(logger.options.messages.invalidInput);
    93        return;
    94      }
    95  
    96      try {
    97        var url = new URL(userValue);
    98      } catch(err) {
    99        this.setMessage(logger.options.messages.invalidUrl, userValue);
   100        return;
   101      }
   102  
   103      var host = url.host,
   104          urlParser = url.pathname.split('/');
   105  
   106      if (host !== HOST) {
   107        this.setMessage(logger.options.messages.invalidHost, userValue);
   108        return;
   109      }
   110  
   111      /* 
   112        A valid PR link has 4 params. The params are: org, repo, pr number and pull 
   113        Although the length of urlParser is 5 because of split('/'). 
   114        It splits the url into an array of params with the separator "/" so it returns 5.
   115      */
   116      if (urlParser.length !== PARAMS) {
   117        this.setMessage(logger.options.messages.invalidParams, urlParser);
   118        return;
   119      }
   120  
   121      var org = urlParser[1],
   122          repo = urlParser[2],
   123          pr = urlParser[4];
   124  
   125      var params = 'org=' + org + '&repo=' + repo + '&pr=' + pr;
   126  
   127      if (url.hash.length > 1) {
   128        params += '&issuecomment=' + url.hash.substr(1).replace('-', '=');
   129      }
   130  
   131      this.setParams(params);
   132      this.requestLogs();
   133  
   134    }
   135  
   136  };
   137  
   138  var generateView = {
   139    init: function() {
   140      // store pointers to our DOM elements for easy access later
   141  
   142      this._columnHeaders_ = ["time", "level", "msg", "from", "to", "job", "event-type", "", "type"];
   143      this._table_ = document.createElement('table');
   144      this._tr_ = document.createElement('tr');
   145      this._th_ = document.createElement('th');
   146      this._td_ = document.createElement('td');
   147      this._ul_ = document.createElement('ul');
   148      this._li_ = document.createElement('li');
   149      this.response = document.getElementById('response');
   150      this.messageHolder = document.getElementById('error-message');
   151      this.userInput = document.getElementById('user-input');
   152      this.searchSubmit = document.getElementById('search-submit');
   153      this.loader = document.getElementById('loading');
   154      this.searchWrapper = document.getElementById('search-wrapper');
   155  
   156  
   157      // on click, get the user input and build the param
   158      this.searchSubmit.addEventListener('click', function() {
   159        var userValue = generateView.userInput.value;
   160  
   161        controller.buildParams(userValue);
   162      });
   163  
   164      // event listener for Enter
   165      this.userInput.addEventListener("keyup", function(event) {
   166        event.preventDefault();
   167        if (event.keyCode === 13) {
   168          generateView.searchSubmit.click();
   169        }
   170      });
   171    },
   172  
   173    render: function() {
   174      var logs = logger.data;
   175      //clear response each time
   176      this.response.innerHTML = "";
   177      // check if there are logs to display
   178      if ( logs.length > 0) {
   179        this.searchWrapper.classList.add("top");
   180        this.response.appendChild(this.buildHtmlTable(logs));
   181      } else {
   182        this.response.innerText = logger.options.messages.emptyLogs;
   183      }
   184    },
   185  
   186    // Builds the HTML Table out of json data.
   187  
   188    buildHtmlTable: function(arr) {
   189  
   190      var table = this._table_.cloneNode(false),
   191          columns = this.addColumnHeaders(arr, table),
   192          extra = this.addExtra(arr, table);
   193  
   194      for (var i = 0, maxi = arr.length; i < maxi; ++i) {
   195  
   196        var tr = this._tr_.cloneNode(false),
   197            ul = this._ul_.cloneNode(false);
   198  
   199        // append the basic columns
   200        for (var j = 0, maxj = columns.length; j < maxj; ++j) {
   201          var td = this._td_.cloneNode(false);
   202  
   203          var tableData  = arr[i][columns[j]] || '';
   204  
   205          if (columns[j] === "time") {
   206            tableData  = new Date(arr[i][columns[j]]) ;
   207          }
   208  
   209          td.appendChild(document.createTextNode(tableData));
   210          tr.appendChild(td);
   211        }
   212  
   213  
   214        // append the rest log columns
   215        for (var c = 0, maxc = extra.length; c < maxc; ++c) {
   216          var li = this._li_.cloneNode(false);
   217  
   218          if (extra[c] && arr[i][extra[c]]) {
   219            var extraLogInfo = extra[c] + ": " + arr[i][extra[c]];
   220            li.appendChild(document.createTextNode(extraLogInfo));
   221            ul.appendChild(li);
   222            tr.appendChild(ul);
   223          }
   224  
   225        }
   226  
   227        table.appendChild(tr);
   228      }
   229  
   230      return table;
   231    },
   232  
   233    addColumnHeaders: function(arr, table) {
   234      // set default column headers for the table
   235      var columnSet =  this._columnHeaders_,
   236          tr = this._tr_.cloneNode(false);
   237  
   238      //build table header
   239      for (var counter= 0, columnLength = columnSet.length; counter < columnLength; counter++ ) {
   240  
   241        var th = this._th_.cloneNode(false);
   242        th.appendChild(document.createTextNode(columnSet[counter]));
   243        tr.appendChild(th);
   244  
   245      }
   246  
   247      table.appendChild(tr);
   248      return columnSet;
   249  
   250    },
   251  
   252    addExtra: function(arr) {
   253  
   254      var extraSet = [],
   255          columnSet =  this._columnHeaders_;
   256  
   257      for (var i = 0, l = arr.length; i < l; i++) {
   258        for (var key in arr[i]) {
   259          if (arr[i].hasOwnProperty(key) && columnSet.indexOf(key) === -1 && extraSet.indexOf(key) === -1) {
   260            extraSet.push(key);
   261          }
   262        }
   263      }
   264  
   265      return extraSet;
   266  
   267    },
   268  
   269    updateMsg: function() {
   270      this.messageHolder.innerText = controller.getMessage();
   271    },
   272  
   273    toggleElement: function(elem, show) {
   274      show? elem.classList.remove("hide") : elem.classList.add("hide");
   275    }
   276  };
   277  
   278  document.addEventListener('DOMContentLoaded', function() {
   279    controller.init();
   280  });
   281