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