From a200619d14bf1d90c21503ec358a30ca84d5337f Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Mon, 19 Mar 2018 19:06:09 +0000 Subject: Show Ajax requests in performance bar But first, rewrite the performance bar in Vue: 1. Remove the peek-host gem and replace it with existing code. This also allows us to include the host in the JSON response, rather than in the page HTML. 2. Leave the line profiler parts as here-be-dragons: nicer would be a separate endpoint for these, so we could use them on Ajax requests too. 3. The performance bar is too fiddly to rewrite right now, so apply the same logic to that. Then, add features! All requests made through Axios are able to be tracked. To keep a lid on memory usage, only the first two requests for a given URL are tracked, though. Each request that's tracked has the same data as the initial page load, with the exception of the performance bar and the line profiler, as explained above. --- app/assets/javascripts/dispatcher.js | 12 +- app/assets/javascripts/performance_bar.js | 57 ------ .../performance_bar/components/detailed_metric.vue | 78 +++++++++ .../components/performance_bar_app.vue | 191 +++++++++++++++++++++ .../components/request_selector.vue | 52 ++++++ .../performance_bar/components/simple_metric.vue | 30 ++++ .../components/upstream_performance_bar.vue | 18 ++ app/assets/javascripts/performance_bar/index.js | 37 ++++ .../services/performance_bar_service.js | 24 +++ .../stores/performance_bar_store.js | 39 +++++ 10 files changed, 477 insertions(+), 61 deletions(-) delete mode 100644 app/assets/javascripts/performance_bar.js create mode 100644 app/assets/javascripts/performance_bar/components/detailed_metric.vue create mode 100644 app/assets/javascripts/performance_bar/components/performance_bar_app.vue create mode 100644 app/assets/javascripts/performance_bar/components/request_selector.vue create mode 100644 app/assets/javascripts/performance_bar/components/simple_metric.vue create mode 100644 app/assets/javascripts/performance_bar/components/upstream_performance_bar.vue create mode 100644 app/assets/javascripts/performance_bar/index.js create mode 100644 app/assets/javascripts/performance_bar/services/performance_bar_service.js create mode 100644 app/assets/javascripts/performance_bar/stores/performance_bar_store.js (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 42ecc415173..72f21f13860 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -53,8 +53,12 @@ function initPageShortcuts(page) { function initGFMInput() { $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => { - const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources); - const enableGFM = convertPermissionToBoolean(el.dataset.supportsAutocomplete); + const gfm = new GfmAutoComplete( + gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources, + ); + const enableGFM = convertPermissionToBoolean( + el.dataset.supportsAutocomplete, + ); gfm.setup($(el), { emojis: true, members: enableGFM, @@ -67,9 +71,9 @@ function initGFMInput() { } function initPerformanceBar() { - if (document.querySelector('#peek')) { + if (document.querySelector('#js-peek')) { import('./performance_bar') - .then(m => new m.default({ container: '#peek' })) // eslint-disable-line new-cap + .then(m => new m.default({ container: '#js-peek' })) // eslint-disable-line new-cap .catch(() => Flash('Error loading performance bar module')); } } diff --git a/app/assets/javascripts/performance_bar.js b/app/assets/javascripts/performance_bar.js deleted file mode 100644 index c22598ee665..00000000000 --- a/app/assets/javascripts/performance_bar.js +++ /dev/null @@ -1,57 +0,0 @@ -import $ from 'jquery'; -import 'vendor/peek'; -import 'vendor/peek.performance_bar'; -import { getParameterValues } from './lib/utils/url_utility'; - -export default class PerformanceBar { - constructor(opts) { - if (!PerformanceBar.singleton) { - this.init(opts); - PerformanceBar.singleton = this; - } - return PerformanceBar.singleton; - } - - init(opts) { - const $container = $(opts.container); - this.$lineProfileLink = $container.find('.js-toggle-modal-peek-line-profile'); - this.$lineProfileModal = $('#modal-peek-line-profile'); - this.initEventListeners(); - this.showModalOnLoad(); - } - - initEventListeners() { - this.$lineProfileLink.on('click', e => this.handleLineProfileLink(e)); - $(document).on('click', '.js-lineprof-file', PerformanceBar.toggleLineProfileFile); - } - - showModalOnLoad() { - // When a lineprofiler query-string param is present, we show the line - // profiler modal upon page load - if (/lineprofiler/.test(window.location.search)) { - PerformanceBar.toggleModal(this.$lineProfileModal); - } - } - - handleLineProfileLink(e) { - const lineProfilerParameter = getParameterValues('lineprofiler'); - const lineProfilerParameterRegex = new RegExp(`lineprofiler=${lineProfilerParameter[0]}`); - const shouldToggleModal = lineProfilerParameter.length > 0 && - lineProfilerParameterRegex.test(e.currentTarget.href); - - if (shouldToggleModal) { - e.preventDefault(); - PerformanceBar.toggleModal(this.$lineProfileModal); - } - } - - static toggleModal($modal) { - if ($modal.length) { - $modal.modal('toggle'); - } - } - - static toggleLineProfileFile(e) { - $(e.currentTarget).parents('.peek-rblineprof-file').find('.data').toggle(); - } -} diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue new file mode 100644 index 00000000000..145465f4ee9 --- /dev/null +++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue @@ -0,0 +1,78 @@ + + diff --git a/app/assets/javascripts/performance_bar/components/performance_bar_app.vue b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue new file mode 100644 index 00000000000..88345cf2ad9 --- /dev/null +++ b/app/assets/javascripts/performance_bar/components/performance_bar_app.vue @@ -0,0 +1,191 @@ + + diff --git a/app/assets/javascripts/performance_bar/components/request_selector.vue b/app/assets/javascripts/performance_bar/components/request_selector.vue new file mode 100644 index 00000000000..2f360ea6f6c --- /dev/null +++ b/app/assets/javascripts/performance_bar/components/request_selector.vue @@ -0,0 +1,52 @@ + + diff --git a/app/assets/javascripts/performance_bar/components/simple_metric.vue b/app/assets/javascripts/performance_bar/components/simple_metric.vue new file mode 100644 index 00000000000..b654bc66249 --- /dev/null +++ b/app/assets/javascripts/performance_bar/components/simple_metric.vue @@ -0,0 +1,30 @@ + + diff --git a/app/assets/javascripts/performance_bar/components/upstream_performance_bar.vue b/app/assets/javascripts/performance_bar/components/upstream_performance_bar.vue new file mode 100644 index 00000000000..d438b1ec27b --- /dev/null +++ b/app/assets/javascripts/performance_bar/components/upstream_performance_bar.vue @@ -0,0 +1,18 @@ + + diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js new file mode 100644 index 00000000000..fca488120f6 --- /dev/null +++ b/app/assets/javascripts/performance_bar/index.js @@ -0,0 +1,37 @@ +import 'vendor/peek.performance_bar'; + +import Vue from 'vue'; +import performanceBarApp from './components/performance_bar_app.vue'; +import PerformanceBarStore from './stores/performance_bar_store'; + +export default () => + new Vue({ + el: '#js-peek', + components: { + performanceBarApp, + }, + data() { + const performanceBarData = document.querySelector(this.$options.el) + .dataset; + const store = new PerformanceBarStore(); + + return { + store, + env: performanceBarData.env, + requestId: performanceBarData.requestId, + peekUrl: performanceBarData.peekUrl, + profileUrl: performanceBarData.profileUrl, + }; + }, + render(createElement) { + return createElement('performance-bar-app', { + props: { + store: this.store, + env: this.env, + requestId: this.requestId, + peekUrl: this.peekUrl, + profileUrl: this.profileUrl, + }, + }); + }, + }); diff --git a/app/assets/javascripts/performance_bar/services/performance_bar_service.js b/app/assets/javascripts/performance_bar/services/performance_bar_service.js new file mode 100644 index 00000000000..d8e792446c3 --- /dev/null +++ b/app/assets/javascripts/performance_bar/services/performance_bar_service.js @@ -0,0 +1,24 @@ +import axios from '../../lib/utils/axios_utils'; + +export default class PerformanceBarService { + static fetchRequestDetails(peekUrl, requestId) { + return axios.get(peekUrl, { params: { request_id: requestId } }); + } + + static registerInterceptor(peekUrl, callback) { + return axios.interceptors.response.use(response => { + const requestId = response.headers['x-request-id']; + const requestUrl = response.config.url; + + if (requestUrl !== peekUrl && requestId) { + callback(requestId, requestUrl); + } + + return response; + }); + } + + static removeInterceptor(interceptor) { + axios.interceptors.response.eject(interceptor); + } +} diff --git a/app/assets/javascripts/performance_bar/stores/performance_bar_store.js b/app/assets/javascripts/performance_bar/stores/performance_bar_store.js new file mode 100644 index 00000000000..c6b2f55243c --- /dev/null +++ b/app/assets/javascripts/performance_bar/stores/performance_bar_store.js @@ -0,0 +1,39 @@ +export default class PerformanceBarStore { + constructor() { + this.requests = []; + } + + addRequest(requestId, requestUrl, requestDetails) { + if (!this.findRequest(requestId)) { + this.requests.push({ + id: requestId, + url: requestUrl, + details: requestDetails, + }); + } + + return this.requests; + } + + findRequest(requestId) { + return this.requests.find(request => request.id === requestId); + } + + addRequestDetails(requestId, requestDetails) { + const request = this.findRequest(requestId); + + request.details = requestDetails; + + return request; + } + + requestsWithDetails() { + return this.requests.filter(request => request.details); + } + + canTrackRequest(requestUrl) { + return ( + this.requests.filter(request => request.url === requestUrl).length < 2 + ); + } +} -- cgit v1.2.1