Show 404 when project lookup fails
Following Ic9c9f0071e, looking up the project of a nonexistent change
would result in a 404 in the getChange REST call. This network failure
would propagate to a visual not-found message automatically and the
router merely needed to stop attempting to redirect when it recognized
the failure.
In I7336ae90d7, the getChange method is updated to use a query because
the former endpoint had been deprecated. However, because the call
became a query, nonexistent changes would yield an empty set rather than
a 404 network error -- consequently, no visual 404 would appear and the
app would appear to hang.
With this change, instead of merely halting when recognizing a project
lookup failure, the router will display a 404.
The new project lookup query is updated to use the change ID operator
query as Ibe82272104 does.
Bug: Issue 8152
Change-Id: I37a816c41485c077d28f051de281225c20ed495b
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
index 8d91ef8..6a7966b 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -55,7 +55,7 @@
stub('gr-rest-api-interface', {
getConfig() { return Promise.resolve({}); },
getAccount() { return Promise.resolve({}); },
- getChange() { return Promise.resolve([{}]); },
+ getChange() { return Promise.resolve({}); },
getChangeSuggestedReviewers() { return Promise.resolve([]); },
});
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index dc264f2..d85b810 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -498,9 +498,12 @@
return this.$.restAPI.getFromProjectLookup(params.changeNum)
.then(project => {
- // Do nothing if the lookup request failed. This avoids an infinite
- // loop of project lookups.
- if (!project) { return; }
+ // Show a 404 and terminate if the lookup request failed. Attempting
+ // to redirect after failing to get the project loops infinitely.
+ if (!project) {
+ this._show404();
+ return;
+ }
params.project = project;
this._normalizePatchRangeParams(params);
@@ -1333,6 +1336,10 @@
* Catchall route for when no other route is matched.
*/
_handleDefaultRoute() {
+ this._show404();
+ },
+
+ _show404() {
// Note: the app's 404 display is tightly-coupled with catching 404
// network responses, so we simulate a 404 response status to display it.
// TODO: Decouple the gr-app error view from network responses.
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
index 7011c65..4af8130 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
@@ -430,11 +430,13 @@
suite('_normalizeLegacyRouteParams', () => {
let rangeStub;
let redirectStub;
+ let show404Stub;
setup(() => {
rangeStub = sandbox.stub(element, '_normalizePatchRangeParams')
.returns(Promise.resolve());
redirectStub = sandbox.stub(element, '_redirect');
+ show404Stub = sandbox.stub(element, '_show404');
});
test('w/o changeNum', () => {
@@ -445,6 +447,7 @@
assert.isFalse(rangeStub.called);
assert.isNotOk(params.project);
assert.isFalse(redirectStub.called);
+ assert.isFalse(show404Stub.called);
});
});
@@ -456,18 +459,19 @@
assert.isTrue(rangeStub.called);
assert.equal(params.project, 'foo/bar');
assert.isTrue(redirectStub.calledOnce);
+ assert.isFalse(show404Stub.called);
});
});
test('halts on project lookup failure', () => {
projectLookupStub.returns(Promise.resolve(undefined));
-
const params = {changeNum: 1234};
return element._normalizeLegacyRouteParams(params).then(() => {
assert.isTrue(projectLookupStub.called);
assert.isFalse(rangeStub.called);
assert.isUndefined(params.project);
assert.isFalse(redirectStub.called);
+ assert.isTrue(show404Stub.calledOnce);
});
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index b5bb35a..3f19f7e 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -1993,7 +1993,11 @@
*/
getChange(changeNum, opt_errFn) {
// Cannot use _changeBaseURL, as this function is used by _projectLookup.
- return this.fetchJSON(`/changes/?q=${changeNum}`, opt_errFn);
+ return this.fetchJSON(`/changes/?q=change:${changeNum}`, opt_errFn)
+ .then(res => {
+ if (!res || !res.length) { return null; }
+ return res[0];
+ });
},
/**
@@ -2026,8 +2030,7 @@
this.fire('page-error', {response});
};
- return this.getChange(changeNum, onError).then(res => {
- const change = res[0];
+ return this.getChange(changeNum, onError).then(change => {
if (!change || !change.project) { return; }
this.setInProjectLookup(changeNum, change.project);
return change.project;
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index a8c09d1..33eb181 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -1036,7 +1036,7 @@
suite('getFromProjectLookup', () => {
test('getChange fails', () => {
sandbox.stub(element, 'getChange')
- .returns(Promise.resolve([]));
+ .returns(Promise.resolve(null));
return element.getFromProjectLookup().then(val => {
assert.strictEqual(val, undefined);
assert.deepEqual(element._projectLookup, {});
@@ -1044,7 +1044,7 @@
});
test('getChange succeeds, no project', () => {
- sandbox.stub(element, 'getChange').returns(Promise.resolve([]));
+ sandbox.stub(element, 'getChange').returns(Promise.resolve(null));
return element.getFromProjectLookup().then(val => {
assert.strictEqual(val, undefined);
assert.deepEqual(element._projectLookup, {});
@@ -1053,7 +1053,7 @@
test('getChange succeeds with project', () => {
sandbox.stub(element, 'getChange')
- .returns(Promise.resolve([{project: 'project'}]));
+ .returns(Promise.resolve({project: 'project'}));
return element.getFromProjectLookup('test').then(val => {
assert.equal(val, 'project');
assert.deepEqual(element._projectLookup, {test: 'project'});