Abstract PolyGerrit router
Set up the mechanism for generating URLs and triggering navigation in PG components in a way that is decoupled from page.js and gr-router. Upgrade some components to use this system. Feature: Issue 6446 Change-Id: Idc18cbd87b8e4e05d24ae6a5feb0a0a43f47fd7f
This commit is contained in:
@@ -19,6 +19,8 @@ limitations under the License.
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
|
||||
|
||||
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||
|
||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
|
||||
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
|
||||
|
||||
@@ -843,8 +843,7 @@
|
||||
action: 'Reload',
|
||||
callback: () => {
|
||||
// Load the current change without any patch range.
|
||||
location.href = `${this.getBaseUrl()}/c/${
|
||||
this.change._number}`;
|
||||
Gerrit.Nav.navigateToChange(this.change);
|
||||
},
|
||||
});
|
||||
cleanupFn();
|
||||
|
||||
@@ -14,11 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
||||
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
|
||||
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../plugins/gr-external-style/gr-external-style.html">
|
||||
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||
<link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
|
||||
<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
|
||||
<link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
|
||||
|
||||
@@ -57,9 +57,7 @@
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.BaseUrlBehavior,
|
||||
Gerrit.RESTClientBehavior,
|
||||
Gerrit.URLEncodingBehavior,
|
||||
],
|
||||
|
||||
observers: [
|
||||
@@ -266,27 +264,17 @@
|
||||
},
|
||||
|
||||
_computeProjectURL(project) {
|
||||
return this.getBaseUrl() + '/q/project:' +
|
||||
this.encodeURL(project, false);
|
||||
return Gerrit.Nav.getUrlForProject(project);
|
||||
},
|
||||
|
||||
_computeBranchURL(project, branch) {
|
||||
let status;
|
||||
if (this.change.status == this.ChangeStatus.NEW) {
|
||||
status = 'open';
|
||||
} else {
|
||||
status = this.change.status.toLowerCase();
|
||||
}
|
||||
return this.getBaseUrl() + '/q/project:' +
|
||||
this.encodeURL(project, false) +
|
||||
' branch:' + this.encodeURL(branch, false) +
|
||||
' status:' + this.encodeURL(status, false);
|
||||
return Gerrit.Nav.getUrlForBranch(branch, project,
|
||||
this.change.status == this.ChangeStatus.NEW ? 'open' :
|
||||
this.change.status.toLowerCase());
|
||||
},
|
||||
|
||||
_computeTopicURL(topic) {
|
||||
return this.getBaseUrl() + '/q/topic:' +
|
||||
this.encodeURL('"' + topic + '"', false) +
|
||||
'+(status:open OR status:merged)';
|
||||
return Gerrit.Nav.getUrlForTopic(topic);
|
||||
},
|
||||
|
||||
_handleTopicRemoved() {
|
||||
|
||||
@@ -300,12 +300,6 @@ limitations under the License.
|
||||
});
|
||||
});
|
||||
|
||||
test('topic href has quotes', () => {
|
||||
const hrefArr = element._computeTopicURL('test')
|
||||
.split('%2522'); // Double-escaped quote.
|
||||
assert.equal(hrefArr[1], 'test');
|
||||
});
|
||||
|
||||
test('topic removal', () => {
|
||||
sandbox.stub(element.$.restAPI, 'setChangeTopic').returns(
|
||||
Promise.resolve());
|
||||
|
||||
@@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
||||
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
|
||||
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
|
||||
<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||
<link rel="import" href="../../diff/gr-diff-preferences/gr-diff-preferences.html">
|
||||
<link rel="import" href="../../shared/gr-account-link/gr-account-link.html">
|
||||
<link rel="import" href="../../shared/gr-select/gr-select.html">
|
||||
@@ -316,7 +316,7 @@ limitations under the License.
|
||||
change="{{_change}}" hidden$="[[!_loggedIn]]"></gr-change-star>
|
||||
<a
|
||||
aria-label$="[[_computeChangePermalinkAriaLabel(_change._number)]]"
|
||||
href$="[[_computeChangePermalink(_change._number)]]">[[_change._number]]</a><!--
|
||||
href$="[[_computeChangeUrl(_change)]]">[[_change._number]]</a><!--
|
||||
--><template is="dom-if" if="[[_changeStatus]]"><!--
|
||||
--> (<!--
|
||||
--><span
|
||||
@@ -471,7 +471,7 @@ limitations under the License.
|
||||
commit-info="[[_commitInfo]]"></gr-commit-info>
|
||||
<span class="latestPatchContainer">
|
||||
/
|
||||
<a href$="[[getBaseUrl()]]/c/[[_change._number]]">Go to latest patch set</a>
|
||||
<a href$="[[_computeChangeUrl(_change)]]">Go to latest patch set</a>
|
||||
</span>
|
||||
<span class="downloadContainer desktop">
|
||||
/
|
||||
|
||||
@@ -183,7 +183,6 @@
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.BaseUrlBehavior,
|
||||
Gerrit.KeyboardShortcutBehavior,
|
||||
Gerrit.PatchSetBehavior,
|
||||
Gerrit.RESTClientBehavior,
|
||||
@@ -632,8 +631,8 @@
|
||||
page.show(this.changePath(this._changeNum) + '/' + patchExpr);
|
||||
},
|
||||
|
||||
_computeChangePermalink(changeNum) {
|
||||
return this.getBaseUrl() + '/' + changeNum;
|
||||
_computeChangeUrl(change) {
|
||||
return Gerrit.Nav.getUrlForChange(change);
|
||||
},
|
||||
|
||||
_computeChangeStatus(change, patchNum) {
|
||||
@@ -1288,8 +1287,7 @@
|
||||
action: 'Reload',
|
||||
callback: function() {
|
||||
// Load the current change without any patch range.
|
||||
location.href = this.getBaseUrl() + '/c/' +
|
||||
this._change._number;
|
||||
Gerrit.Nav.navigateToChange(this._change);
|
||||
}.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
152
polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
Normal file
152
polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
Normal file
@@ -0,0 +1,152 @@
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<script>
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
// Navigation parameters object format:
|
||||
//
|
||||
// Each object has a `view` property with a value from Gerrit.Nav.View. The
|
||||
// remaining properties depend on the value used for view.
|
||||
//
|
||||
// - Gerrit.Nav.View.CHANGE:
|
||||
// - `id`, required, String: the numeric ID of the change.
|
||||
//
|
||||
// - Gerrit.Nav.View.SEARCH:
|
||||
// - `owner`, optional, String: the owner name.
|
||||
// - `project`, optional, String: the project name.
|
||||
// - `branch`, optional, String: the branch name.
|
||||
// - `topic`, optional, String: the topic name.
|
||||
// - `statuses`, optional, Array<String>: the list of change statuses to
|
||||
// search for. If more than one is provided, the search will OR them
|
||||
// together.
|
||||
|
||||
window.Gerrit = window.Gerrit || {};
|
||||
|
||||
// Prevent redefinition.
|
||||
if (window.Gerrit.hasOwnProperty('Nav')) { return; }
|
||||
|
||||
const uninitialized = () => {
|
||||
console.warn('Use of uninitialized routing');
|
||||
};
|
||||
|
||||
window.Gerrit.Nav = {
|
||||
|
||||
View: {
|
||||
CHANGE: 'change',
|
||||
SEARCH: 'search',
|
||||
},
|
||||
|
||||
/** @type {Function} */
|
||||
_navigate: uninitialized,
|
||||
|
||||
/** @type {Function} */
|
||||
_generateUrl: uninitialized,
|
||||
|
||||
/**
|
||||
* Setup router implementation.
|
||||
* @param {Function} handleNavigate
|
||||
* @param {Function} generateUrl
|
||||
*/
|
||||
setup(navigate, generateUrl) {
|
||||
this._navigate = navigate;
|
||||
this._generateUrl = generateUrl;
|
||||
},
|
||||
|
||||
destroy() {
|
||||
this._navigate = uninitialized;
|
||||
this._generateUrl = uninitialized;
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a URL for the given route parameters.
|
||||
* @param {Object} params
|
||||
* @return {String}
|
||||
*/
|
||||
_getUrlFor(params) {
|
||||
return this._generateUrl(params);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} project The name of the project.
|
||||
* @return {String}
|
||||
*/
|
||||
getUrlForProject(project) {
|
||||
return this._getUrlFor({
|
||||
view: Gerrit.Nav.View.SEARCH,
|
||||
project,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} branch The name of the branch.
|
||||
* @param {String} project The name of the project.
|
||||
* @param {String} status The status to search.
|
||||
* @return {String}
|
||||
*/
|
||||
getUrlForBranch(branch, project, status) {
|
||||
return this._getUrlFor({
|
||||
view: Gerrit.Nav.View.SEARCH,
|
||||
branch,
|
||||
project,
|
||||
statuses: [status],
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} topic The name of the topic.
|
||||
* @return {String}
|
||||
*/
|
||||
getUrlForTopic(topic) {
|
||||
return this._getUrlFor({
|
||||
view: Gerrit.Nav.View.SEARCH,
|
||||
topic,
|
||||
statuses: ['open', 'merged'],
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} change The change object.
|
||||
* @return {String}
|
||||
*/
|
||||
getUrlForChange(change) {
|
||||
return this._getUrlFor({
|
||||
view: Gerrit.Nav.View.CHANGE,
|
||||
id: change._number,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} change The change object.
|
||||
* @return {String}
|
||||
*/
|
||||
navigateToChange(change) {
|
||||
this._navigate(this.getUrlForChange(change));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {String} owner The name of the owner.
|
||||
* @return {String}
|
||||
*/
|
||||
getUrlForOwner(owner) {
|
||||
return this._getUrlFor({
|
||||
view: Gerrit.Nav.View.SEARCH,
|
||||
owner,
|
||||
});
|
||||
},
|
||||
};
|
||||
})(window);
|
||||
</script>
|
||||
@@ -14,7 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
||||
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-reporting/gr-reporting.html">
|
||||
|
||||
|
||||
@@ -37,7 +37,11 @@
|
||||
getReporting().timeEnd('WebComponentsReady');
|
||||
});
|
||||
|
||||
function startRouter() {
|
||||
function encode(s) {
|
||||
return window.Gerrit.URLEncodingBehavior.encodeURL(s, false);
|
||||
}
|
||||
|
||||
function startRouter(generateUrl) {
|
||||
const base = window.Gerrit.BaseUrlBehavior.getBaseUrl();
|
||||
if (base) {
|
||||
page.base(base);
|
||||
@@ -46,6 +50,8 @@
|
||||
const restAPI = document.createElement('gr-rest-api-interface');
|
||||
const reporting = getReporting();
|
||||
|
||||
Gerrit.Nav.setup(url => { page.show(url); }, generateUrl);
|
||||
|
||||
// Middleware
|
||||
page((ctx, next) => {
|
||||
document.body.scrollTop = 0;
|
||||
@@ -352,7 +358,45 @@
|
||||
is: 'gr-router',
|
||||
start() {
|
||||
if (!app) { return; }
|
||||
startRouter();
|
||||
startRouter(this._generateUrl);
|
||||
},
|
||||
|
||||
_generateUrl(params) {
|
||||
const base = window.Gerrit.BaseUrlBehavior.getBaseUrl();
|
||||
let url = '';
|
||||
|
||||
if (params.view === Gerrit.Nav.View.SEARCH) {
|
||||
const operators = [];
|
||||
if (params.owner) {
|
||||
operators.push('owner:' + encode(params.owner));
|
||||
}
|
||||
if (params.project) {
|
||||
operators.push('project:' + encode(params.project));
|
||||
}
|
||||
if (params.branch) {
|
||||
operators.push('branch:' + encode(params.branch));
|
||||
}
|
||||
if (params.topic) {
|
||||
operators.push('topic:"' + encode(params.topic) + '"');
|
||||
}
|
||||
if (params.statuses) {
|
||||
if (params.statuses.length === 1) {
|
||||
operators.push('status:' + encode(params.statuses[0]));
|
||||
} else if (params.statuses.length > 1) {
|
||||
operators.push(
|
||||
'(' +
|
||||
params.statuses.map(s => `status:${encode(s)}`).join(' OR ') +
|
||||
')');
|
||||
}
|
||||
}
|
||||
url = '/q/' + operators.join('+');
|
||||
} else if (params.view === Gerrit.Nav.View.CHANGE) {
|
||||
url = '/c/' + params.id;
|
||||
} else {
|
||||
throw new Error('Can\'t generate');
|
||||
}
|
||||
|
||||
return base + url;
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||
<title>gr-router</title>
|
||||
|
||||
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||
<link rel="import" href="gr-router.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-router></gr-router>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-router tests', () => {
|
||||
suite('generateUrl', () => {
|
||||
let element;
|
||||
|
||||
setup(() => {
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
test('search', () => {
|
||||
let params = {
|
||||
view: Gerrit.Nav.View.SEARCH,
|
||||
owner: 'a%b',
|
||||
project: 'c%d',
|
||||
branch: 'e%f',
|
||||
topic: 'g%h',
|
||||
statuses: ['op%en'],
|
||||
};
|
||||
assert.equal(element._generateUrl(params),
|
||||
'/q/owner:a%2525b+project:c%2525d+branch:e%2525f+' +
|
||||
'topic:"g%2525h"+status:op%2525en');
|
||||
|
||||
params = {
|
||||
view: Gerrit.Nav.View.SEARCH,
|
||||
statuses: ['a', 'b', 'c'],
|
||||
};
|
||||
assert.equal(element._generateUrl(params),
|
||||
'/q/(status:a OR status:b OR status:c)');
|
||||
});
|
||||
|
||||
test('change', () => {
|
||||
const params = {
|
||||
view: Gerrit.Nav.View.CHANGE,
|
||||
id: '1234',
|
||||
};
|
||||
assert.equal(element._generateUrl(params), '/c/1234');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
|
||||
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||
<link rel="import" href="../gr-account-label/gr-account-label.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
|
||||
_computeOwnerLink(account) {
|
||||
if (!account) { return; }
|
||||
const accountID = account.email || account._account_id;
|
||||
return this.getBaseUrl() + '/q/owner:' + encodeURIComponent(accountID);
|
||||
return Gerrit.Nav.getUrlForOwner(account.email || account._account_id);
|
||||
},
|
||||
|
||||
_computeShowEmail(account) {
|
||||
|
||||
@@ -43,18 +43,7 @@ limitations under the License.
|
||||
});
|
||||
|
||||
test('computed fields', () => {
|
||||
assert.equal(element._computeOwnerLink(
|
||||
{
|
||||
_account_id: 123,
|
||||
email: 'andybons+gerrit@gmail.com',
|
||||
}),
|
||||
'/q/owner:andybons%2Bgerrit%40gmail.com');
|
||||
|
||||
assert.equal(element._computeOwnerLink({_account_id: 42}),
|
||||
'/q/owner:42');
|
||||
|
||||
assert.equal(element._computeShowEmail({name: 'asd'}), false);
|
||||
|
||||
assert.equal(element._computeShowEmail({}), true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,5 +15,5 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<link rel="import"
|
||||
href="../bower_components/iron-test-helpers/iron-test-helpers.html" />
|
||||
<link rel="import" href="../bower_components/iron-test-helpers/iron-test-helpers.html" />
|
||||
<link rel="import" href="test-router.html" />
|
||||
|
||||
@@ -62,6 +62,7 @@ limitations under the License.
|
||||
'core/gr-account-dropdown/gr-account-dropdown_test.html',
|
||||
'core/gr-error-manager/gr-error-manager_test.html',
|
||||
'core/gr-main-header/gr-main-header_test.html',
|
||||
'core/gr-router/gr-router_test.html',
|
||||
'core/gr-reporting/gr-reporting_test.html',
|
||||
'core/gr-search-bar/gr-search-bar_test.html',
|
||||
'diff/gr-diff-builder/gr-diff-builder_test.html',
|
||||
|
||||
21
polygerrit-ui/app/test/test-router.html
Normal file
21
polygerrit-ui/app/test/test-router.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<link rel="import" href="../elements/core/gr-navigation/gr-navigation.html">
|
||||
<script>
|
||||
Gerrit.Nav.setup(url => { /* noop */ }, params => '');
|
||||
</script>
|
||||
Reference in New Issue
Block a user