From: Kohei Yoshino Date: Mon, 20 May 2019 18:59:40 +0000 (-0400) Subject: Bug 1523536 - New bug's "Choose from your most-used components" list is slow to show up X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e7d809bc95d91537994d0e54b758e7b04b8ab719;p=thirdparty%2Fbugzilla.git Bug 1523536 - New bug's "Choose from your most-used components" list is slow to show up --- diff --git a/extensions/BMO/template/en/default/global/choose-product.html.tmpl b/extensions/BMO/template/en/default/global/choose-product.html.tmpl index 5615a0355..5233ac403 100644 --- a/extensions/BMO/template/en/default/global/choose-product.html.tmpl +++ b/extensions/BMO/template/en/default/global/choose-product.html.tmpl @@ -37,7 +37,6 @@ "extensions/ProdCompSearch/web/styles/prod_comp_search.css", ]; javascript_urls = [ - "extensions/BMO/web/js/new-bug-frequent-comp.js", "extensions/ProdCompSearch/web/js/prod_comp_search.js", ]; diff --git a/extensions/BMO/web/js/new-bug-frequent-comp.js b/extensions/BMO/web/js/new-bug-frequent-comp.js deleted file mode 100644 index 060fac219..000000000 --- a/extensions/BMO/web/js/new-bug-frequent-comp.js +++ /dev/null @@ -1,123 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. - * - * This Source Code Form is "Incompatible With Secondary Licenses", as - * defined by the Mozilla Public License, v. 2.0. */ - -/** - * Reference or define the Bugzilla app namespace. - * @namespace - */ -var Bugzilla = Bugzilla || {}; // eslint-disable-line no-var - -/** - * Show the current user's most-used components on the New Bug page. - */ -Bugzilla.NewBugFrequentComp = class NewBugFrequentComp { - /** - * Initialize a new NewBugFrequentComp instance. - */ - constructor() { - this.$container = document.querySelector('#frequent-components'); - - if (this.$container && BUGZILLA.user.login) { - this.init(); - } - } - - /** - * Initialize the UI. - */ - async init() { - this.$results = this.$container.querySelector('.results'); - this.$message = this.$results.appendChild(document.createElement('p')); - this.$message.textContent = 'Loading...'; - this.$results.setAttribute('aria-busy', 'true'); - this.$container.hidden = false; - - // Get the current params that may contain `cloned_bug_id` and `format` - const current_params = new URLSearchParams(location.search); - - try { - const links = (await this.fetch()).map(({ product, component }) => { - const params = new URLSearchParams(current_params); - - params.append('product', product); - params.append('component', component); - - return { - href: `${BUGZILLA.config.basepath}enter_bug.cgi?${params.toString()}`, - text: `${product} :: ${component}`, - }; - }); - - this.$message.remove(); - this.$results.insertAdjacentHTML('beforeend', - `` - ); - } catch (error) { - this.$message.textContent = error.message || 'Your frequent components could not be retrieved.'; - } - - this.$results.removeAttribute('aria-busy'); - } - - /** - * Retrieve frequently used components. - * @param {Number} [max=10] Maximum number of results. - * @returns {Promise} Results or error. - */ - async fetch(max = 10) { - const params = new URLSearchParams({ - email1: BUGZILLA.user.login, - emailreporter1: '1', - emailtype1: 'exact', - chfield: '[Bug creation]', - chfieldfrom: '-1y', - chfieldto: 'Now', - include_fields: 'product,component', - }); - - return new Promise((resolve, reject) => { - bugzilla_ajax({ - url: `${BUGZILLA.config.basepath}rest/bug?${params.toString()}` - }, response => { - if (!response.bugs) { - reject(new Error('Your frequent components could not be retrieved.')); - - return; - } - - if (!response.bugs.length) { - reject(new Error(('Your frequent components could not be found.'))); - - return; - } - - const results = []; - - for (const { product, component } of response.bugs) { - const index = results.findIndex(result => product === result.product && component === result.component); - - if (index > -1) { - results[index].count++; - } else { - results.push({ product, component, count: 1 }); - } - } - - // Sort in descending order - results.sort((a, b) => (a.count < b.count ? 1 : a.count > b.count ? -1 : 0)); - - resolve(results.slice(0, max)); - }, () => { - reject(new Error('Your frequent components could not be retrieved.')); - }); - }); - } -}; - -window.addEventListener('DOMContentLoaded', () => new Bugzilla.NewBugFrequentComp(), { once: true }); diff --git a/extensions/ProdCompSearch/lib/WebService.pm b/extensions/ProdCompSearch/lib/WebService.pm index 2242d083d..602ec466c 100644 --- a/extensions/ProdCompSearch/lib/WebService.pm +++ b/extensions/ProdCompSearch/lib/WebService.pm @@ -26,7 +26,7 @@ use constant PUBLIC_METHODS => qw( sub rest_resources { return [ - qr{^/prod_comp_search/(.*)$}, + qr{^/prod_comp_search/find/(.*)$}, { GET => { method => 'prod_comp_search', @@ -34,6 +34,12 @@ sub rest_resources { return {search => $_[0]}; } } + }, + qr{^/prod_comp_search/frequent}, + { + GET => { + method => 'list_frequent_components', + } } ]; } @@ -148,6 +154,40 @@ sub prod_comp_search { return {products => $products}; } +# Get a list of components the user has frequently reported in the past 2 years +sub list_frequent_components { + my ($self) = @_; + my $user = Bugzilla->user; + + # Nothing to show if the user is signed out + return {results => []} unless $user->id; + + # Select the date of 2 years ago today + my ($day, $month, $year) = (localtime(time))[3, 4, 5]; + my $date = sprintf('%4d-%02d-%02d', $year + 1900 - 2, $month + 1, $day); + + my $dbh = Bugzilla->switch_to_shadow_db(); + my $sth = $dbh->prepare(' + SELECT products.name, components.name FROM bugs + INNER JOIN products ON bugs.product_id = products.id + INNER JOIN components ON bugs.component_id = components.id + WHERE bugs.reporter = ? AND bugs.creation_ts > ? + AND products.isactive = 1 AND components.isactive = 1 + GROUP BY components.id ORDER BY count(bugs.bug_id) DESC LIMIT 10; + '); + $sth->execute($user->id, $date); + + my $results = []; + while (my ($product, $component) = $sth->fetchrow_array) { + push @$results, { + product => $self->type('string', $product), + component => $self->type('string', $component), + }; + }; + + return {results => $results}; +} + ################### # Private Methods # ################### diff --git a/extensions/ProdCompSearch/web/js/prod_comp_search.js b/extensions/ProdCompSearch/web/js/prod_comp_search.js index 4953473ac..42731912a 100644 --- a/extensions/ProdCompSearch/web/js/prod_comp_search.js +++ b/extensions/ProdCompSearch/web/js/prod_comp_search.js @@ -69,7 +69,7 @@ $(function() { appendTo: $('#main-inner'), forceFixPosition: true, serviceUrl: function(query) { - return `${BUGZILLA.config.basepath}rest/prod_comp_search/${encodeURIComponent(query)}`; + return `${BUGZILLA.config.basepath}rest/prod_comp_search/find/${encodeURIComponent(query)}`; }, params: params, deferRequestBy: 250, @@ -149,3 +149,86 @@ $(function() { }) .data('counter', 0); }); + +/** + * Reference or define the Bugzilla app namespace. + * @namespace + */ +var Bugzilla = Bugzilla || {}; // eslint-disable-line no-var + +/** + * Show the current user's most-used components on the New Bug page. + */ +Bugzilla.FrequentComponents = class FrequentComponents { + /** + * Initialize a new FrequentComponents instance. + */ + constructor() { + this.$container = document.querySelector('#frequent-components'); + + if (this.$container && BUGZILLA.user.login) { + this.init(); + } + } + + /** + * Initialize the UI. + */ + async init() { + this.$results = this.$container.querySelector('.results'); + this.$message = this.$results.appendChild(document.createElement('p')); + this.$message.textContent = 'Loading...'; + this.$results.setAttribute('aria-busy', 'true'); + this.$container.hidden = false; + + // Get the current params that may contain `cloned_bug_id` and `format` + const current_params = new URLSearchParams(location.search); + + try { + const links = (await this.fetch()).map(({ product, component }) => { + const params = new URLSearchParams(current_params); + + params.append('product', product); + params.append('component', component); + + return { + href: `${BUGZILLA.config.basepath}enter_bug.cgi?${params.toString()}`, + text: `${product} :: ${component}`, + }; + }); + + this.$message.remove(); + this.$results.insertAdjacentHTML('beforeend', + `` + ); + } catch (error) { + this.$message.textContent = error.message || 'Your frequent components could not be retrieved.'; + } + + this.$results.removeAttribute('aria-busy'); + } + + /** + * Retrieve frequently used components. + * @returns {Promise} Results or error. + */ + async fetch() { + return new Promise((resolve, reject) => bugzilla_ajax({ + url: `${BUGZILLA.config.basepath}rest/prod_comp_search/frequent` + }, ({ results }) => { + if (!results) { + reject(new Error('Your frequent components could not be retrieved.')); + } else if (!results.length) { + reject(new Error(('Your frequent components could not be found.'))); + } else { + resolve(results); + } + }, () => { + reject(new Error('Your frequent components could not be retrieved.')); + })); + } +}; + +window.addEventListener('DOMContentLoaded', () => new Bugzilla.FrequentComponents(), { once: true });