Merge changes Ie291529b,Ie563ebf7,Ib909c984,I89e3db3f
* changes:
Migrate remaining gr-autocomplete usages from error dialog to dropdown.
Migrate some gr-autocomplete usages from error dialog to dropdown.
Update gr-change-metadata autocomplete errors
Allow errFn to wait and reject the Promise.
diff --git a/polygerrit-ui/app/api/rest.ts b/polygerrit-ui/app/api/rest.ts
index 283e029..a09f711 100644
--- a/polygerrit-ui/app/api/rest.ts
+++ b/polygerrit-ui/app/api/rest.ts
@@ -15,7 +15,10 @@
PUT = 'PUT',
}
-export type ErrorCallback = (response?: Response | null, err?: Error) => void;
+export type ErrorCallback = (
+ response?: Response | null,
+ err?: Error
+) => Promise<void> | void;
export declare interface RestPluginApi {
getLoggedIn(): Promise<boolean>;
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.ts b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.ts
index cee0fa4..3254b5c 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.ts
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.ts
@@ -29,6 +29,7 @@
import {configModelToken} from '../../../models/config/config-model';
import {resolve} from '../../../models/dependency';
import {createChangeUrl} from '../../../models/views/change';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const SUGGESTIONS_LIMIT = 15;
const REF_PREFIX = 'refs/heads/';
@@ -241,7 +242,13 @@
input = input.substring(REF_PREFIX.length);
}
return this.restApiService
- .getRepoBranches(input, this.repoName, SUGGESTIONS_LIMIT)
+ .getRepoBranches(
+ input,
+ this.repoName,
+ SUGGESTIONS_LIMIT,
+ /* offset=*/ undefined,
+ throwingErrorCallback
+ )
.then(response => {
if (!response) return [];
const branches: Array<{name: BranchName}> = [];
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
index c88b1c5..a90abbd 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
@@ -23,6 +23,7 @@
import {LitElement, css, html} from 'lit';
import {customElement, query, property, state} from 'lit/decorators.js';
import {fireEvent} from '../../../utils/event-util';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
declare global {
interface HTMLElementTagNameMap {
@@ -204,7 +205,11 @@
}
private async getRepoSuggestions(input: string) {
- const response = await this.restApiService.getSuggestedRepos(input);
+ const response = await this.restApiService.getSuggestedRepos(
+ input,
+ /* n=*/ undefined,
+ throwingErrorCallback
+ );
const repos = [];
for (const [name, repo] of Object.entries(response ?? {})) {
@@ -214,7 +219,12 @@
}
private async getGroupSuggestions(input: string) {
- const response = await this.restApiService.getSuggestedGroups(input);
+ const response = await this.restApiService.getSuggestedGroups(
+ input,
+ /* project=*/ undefined,
+ /* n=*/ undefined,
+ throwingErrorCallback
+ );
const groups = [];
for (const [name, group] of Object.entries(response ?? {})) {
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
index 8824009..af9977b 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
@@ -42,6 +42,7 @@
import {configModelToken} from '../../../models/config/config-model';
import {resolve} from '../../../models/dependency';
import {modalStyles} from '../../../styles/gr-modal-styles';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const SAVING_ERROR_TEXT =
'Group may not exist, or you may not have ' + 'permission to add it';
@@ -489,7 +490,7 @@
if (errResponse) {
if (errResponse.status === 404) {
fireAlert(this, SAVING_ERROR_TEXT);
- return errResponse;
+ return;
}
throw Error(errResponse.statusText);
}
@@ -529,13 +530,20 @@
/* private but used in test */
getGroupSuggestions(input: string) {
- return this.restApiService.getSuggestedGroups(input).then(response => {
- const groups: AutocompleteSuggestion[] = [];
- for (const [name, group] of Object.entries(response ?? {})) {
- groups.push({name, value: decodeURIComponent(group.id)});
- }
- return groups;
- });
+ return this.restApiService
+ .getSuggestedGroups(
+ input,
+ /* project=*/ undefined,
+ /* n=*/ undefined,
+ throwingErrorCallback
+ )
+ .then(response => {
+ const groups: AutocompleteSuggestion[] = [];
+ for (const [name, group] of Object.entries(response ?? {})) {
+ groups.push({name, value: decodeURIComponent(group.id)});
+ }
+ return groups;
+ });
}
private handleGroupMemberTextChanged(e: CustomEvent) {
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts b/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
index e65b16b..ba2b3fd 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
@@ -24,6 +24,7 @@
import {subpageStyles} from '../../../styles/gr-subpage-styles';
import {LitElement, PropertyValues, css, html} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;
@@ -427,13 +428,20 @@
}
private getGroupSuggestions(input: string) {
- return this.restApiService.getSuggestedGroups(input).then(response => {
- const groups: AutocompleteSuggestion[] = [];
- for (const [name, group] of Object.entries(response ?? {})) {
- groups.push({name, value: decodeURIComponent(group.id)});
- }
- return groups;
- });
+ return this.restApiService
+ .getSuggestedGroups(
+ input,
+ /* project=*/ undefined,
+ /* n=*/ undefined,
+ throwingErrorCallback
+ )
+ .then(response => {
+ const groups: AutocompleteSuggestion[] = [];
+ for (const [name, group] of Object.entries(response ?? {})) {
+ groups.push({name, value: decodeURIComponent(group.id)});
+ }
+ return groups;
+ });
}
// private but used in test
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
index 49fa99f..07b7c87 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
@@ -41,6 +41,7 @@
import {menuPageStyles} from '../../../styles/gr-menu-page-styles';
import {when} from 'lit/directives/when.js';
import {ValueChangedEvent} from '../../../types/events';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const MAX_AUTOCOMPLETE_RESULTS = 20;
@@ -471,7 +472,8 @@
.getSuggestedGroups(
this.groupFilter || '',
this.repo,
- MAX_AUTOCOMPLETE_RESULTS
+ MAX_AUTOCOMPLETE_RESULTS,
+ throwingErrorCallback
)
.then(response => {
const groups: GroupSuggestion[] = [];
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
index 27deb82..389e7c4 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
@@ -43,6 +43,7 @@
import {ifDefined} from 'lit/directives/if-defined.js';
import {resolve} from '../../../models/dependency';
import {createChangeUrl} from '../../../models/views/change';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const NOTHING_TO_SAVE = 'No changes to save.';
@@ -397,7 +398,12 @@
private getInheritFromSuggestions(): Promise<AutocompleteSuggestion[]> {
return this.restApiService
- .getRepos(this.inheritFromFilter, MAX_AUTOCOMPLETE_RESULTS)
+ .getRepos(
+ this.inheritFromFilter,
+ MAX_AUTOCOMPLETE_RESULTS,
+ /* offset=*/ undefined,
+ throwingErrorCallback
+ )
.then(response => {
const repos: AutocompleteSuggestion[] = [];
if (!response) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-hashtag-flow/gr-change-list-hashtag-flow.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-hashtag-flow/gr-change-list-hashtag-flow.ts
index ad1f718..f8fcad8 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-hashtag-flow/gr-change-list-hashtag-flow.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-hashtag-flow/gr-change-list-hashtag-flow.ts
@@ -27,6 +27,7 @@
import {fireAlert} from '../../../utils/event-util';
import {pluralize} from '../../../utils/string-util';
import {Interaction} from '../../../constants/reporting';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
@customElement('gr-change-list-hashtag-flow')
export class GrChangeListHashtagFlow extends LitElement {
@@ -298,7 +299,8 @@
query: string
): Promise<AutocompleteSuggestion[]> {
const suggestions = await this.restApiService.getChangesWithSimilarHashtag(
- query
+ query,
+ throwingErrorCallback
);
this.existingHashtagSuggestions = (suggestions ?? [])
.flatMap(change => change.hashtags ?? [])
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-topic-flow/gr-change-list-topic-flow.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-topic-flow/gr-change-list-topic-flow.ts
index 363b1ae..7752476 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-topic-flow/gr-change-list-topic-flow.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-topic-flow/gr-change-list-topic-flow.ts
@@ -28,6 +28,7 @@
import {fireAlert} from '../../../utils/event-util';
import {pluralize} from '../../../utils/string-util';
import {Interaction} from '../../../constants/reporting';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
@customElement('gr-change-list-topic-flow')
export class GrChangeListTopicFlow extends LitElement {
@@ -343,7 +344,8 @@
query: string
): Promise<AutocompleteSuggestion[]> {
const suggestions = await this.restApiService.getChangesWithSimilarTopic(
- query
+ query,
+ throwingErrorCallback
);
this.existingTopicSuggestions = (suggestions ?? [])
.map(change => change.topic)
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts
index f32bb8d..a06f8c9 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts
@@ -81,6 +81,7 @@
import {createSearchUrl} from '../../../models/views/search';
import {createChangeUrl} from '../../../models/views/change';
import {GeneratedWebLink, getChangeWeblinks} from '../../../utils/weblink-util';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const HASHTAG_ADD_MESSAGE = 'Add Hashtag';
@@ -1141,7 +1142,7 @@
input: string
): Promise<AutocompleteSuggestion[]> {
return this.restApiService
- .getChangesWithSimilarTopic(input)
+ .getChangesWithSimilarTopic(input, throwingErrorCallback)
.then(response =>
(response ?? [])
.map(change => change.topic)
@@ -1157,7 +1158,7 @@
input: string
): Promise<AutocompleteSuggestion[]> {
return this.restApiService
- .getChangesWithSimilarHashtag(input)
+ .getChangesWithSimilarHashtag(input, throwingErrorCallback)
.then(response =>
(response ?? [])
.flatMap(change => change.hashtags ?? [])
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
index ffb180d..5d7e55c 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
@@ -37,6 +37,7 @@
import {BindValueChangeEvent} from '../../../types/events';
import {resolve} from '../../../models/dependency';
import {createSearchUrl} from '../../../models/views/search';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const SUGGESTIONS_LIMIT = 15;
const CHANGE_SUBJECT_LIMIT = 50;
@@ -631,7 +632,13 @@
input = input.substring('refs/heads/'.length);
}
return this.restApiService
- .getRepoBranches(input, this.project, SUGGESTIONS_LIMIT)
+ .getRepoBranches(
+ input,
+ this.project,
+ SUGGESTIONS_LIMIT,
+ /* offset=*/ undefined,
+ throwingErrorCallback
+ )
.then(response => {
if (!response) return [];
const branches: Array<{name: BranchName}> = [];
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
index 7adc2ca..8f5c8dc 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
@@ -14,6 +14,7 @@
import {Key, Modifier} from '../../../utils/dom-util';
import {ValueChangedEvent} from '../../../types/events';
import {ShortcutController} from '../../lit/shortcut-controller';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const SUGGESTIONS_LIMIT = 15;
@@ -163,7 +164,13 @@
input = input.substring('refs/heads/'.length);
}
return this.restApiService
- .getRepoBranches(input, this.project, SUGGESTIONS_LIMIT)
+ .getRepoBranches(
+ input,
+ this.project,
+ SUGGESTIONS_LIMIT,
+ /* offest=*/ undefined,
+ throwingErrorCallback
+ )
.then(response => {
if (!response) return [];
const branches: Array<{name: BranchName}> = [];
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
index 14cd5e8..072ec73d 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
@@ -16,6 +16,7 @@
import {getAppContext} from '../../../services/app-context';
import {sharedStyles} from '../../../styles/shared-styles';
import {ValueChangedEvent} from '../../../types/events';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
export interface RebaseChange {
name: string;
@@ -222,7 +223,13 @@
// last time it was run.
fetchRecentChanges() {
return this.restApiService
- .getChanges(undefined, 'is:open -age:90d')
+ .getChanges(
+ undefined,
+ 'is:open -age:90d',
+ /* offset=*/ undefined,
+ /* options=*/ undefined,
+ throwingErrorCallback
+ )
.then(response => {
if (!response) return [];
const changes: RebaseChange[] = [];
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.ts b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.ts
index 5bcef11..b9c920a 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.ts
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.ts
@@ -19,6 +19,7 @@
import {resolve} from '../../../models/dependency';
import {configModelToken} from '../../../models/config/config-model';
import {createSearchUrl} from '../../../models/views/search';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const MAX_AUTOCOMPLETE_RESULTS = 10;
const SELF_EXPRESSION = 'self';
@@ -98,7 +99,11 @@
expression: string
): Promise<AutocompleteSuggestion[]> {
return this.restApiService
- .getSuggestedRepos(expression, MAX_AUTOCOMPLETE_RESULTS)
+ .getSuggestedRepos(
+ expression,
+ MAX_AUTOCOMPLETE_RESULTS,
+ throwingErrorCallback
+ )
.then(projects => {
if (!projects) {
return [];
@@ -128,7 +133,12 @@
return Promise.resolve([]);
}
return this.restApiService
- .getSuggestedGroups(expression, undefined, MAX_AUTOCOMPLETE_RESULTS)
+ .getSuggestedGroups(
+ expression,
+ undefined,
+ MAX_AUTOCOMPLETE_RESULTS,
+ throwingErrorCallback
+ )
.then(groups => {
if (!groups) {
return [];
@@ -158,7 +168,13 @@
return Promise.resolve([]);
}
return this.restApiService
- .getSuggestedAccounts(expression, MAX_AUTOCOMPLETE_RESULTS)
+ .getSuggestedAccounts(
+ expression,
+ MAX_AUTOCOMPLETE_RESULTS,
+ /* canSee=*/ undefined,
+ /* filterActive=*/ undefined,
+ throwingErrorCallback
+ )
.then(accounts => {
if (!accounts) {
return [];
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.ts b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.ts
index 6cfefe5..d1d05b7 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.ts
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.ts
@@ -33,6 +33,7 @@
import {resolve} from '../../../models/dependency';
import {modalStyles} from '../../../styles/gr-modal-styles';
import {whenVisible} from '../../../utils/dom-util';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
@customElement('gr-edit-controls')
export class GrEditControls extends LitElement {
@@ -514,7 +515,12 @@
assertIsDefined(this.change, 'this.change');
assertIsDefined(this.patchNum, 'this.patchNum');
return this.restApiService
- .queryChangeFiles(this.change._number, this.patchNum, input)
+ .queryChangeFiles(
+ this.change._number,
+ this.patchNum,
+ input,
+ throwingErrorCallback
+ )
.then(res => {
if (!res)
throw new Error('Failed to retrieve files. Response not set.');
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.ts b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.ts
index 15ec512..2996e50 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.ts
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.ts
@@ -21,6 +21,7 @@
import {when} from 'lit/directives/when.js';
import {fire} from '../../../utils/event-util';
import {PropertiesOfType} from '../../../utils/type-util';
+import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
type NotificationKey = PropertiesOfType<Required<ProjectWatchInfo>, boolean>;
@@ -193,13 +194,15 @@
// private but used in tests.
getProjectSuggestions(input: string) {
- return this.restApiService.getSuggestedRepos(input).then(response => {
- const repos: AutocompleteSuggestion[] = [];
- for (const [name, repo] of Object.entries(response ?? {})) {
- repos.push({name, value: repo.id});
- }
- return repos;
- });
+ return this.restApiService
+ .getSuggestedRepos(input, /* n=*/ undefined, throwingErrorCallback)
+ .then(response => {
+ const repos: AutocompleteSuggestion[] = [];
+ for (const [name, repo] of Object.entries(response ?? {})) {
+ repos.push({name, value: repo.id});
+ }
+ return repos;
+ });
}
private handleRemoveProject(project: ProjectWatchInfo) {
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.ts b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.ts
index 8fa351b..6d2fe20 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.ts
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.ts
@@ -21,6 +21,7 @@
import {assertIsDefined} from '../../../utils/common-util';
import {fire} from '../../../utils/event-util';
import {BindValueChangeEvent} from '../../../types/events';
+import {throwingErrorCallback} from '../gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
const SUGGESTIONS_LIMIT = 15;
const REF_PREFIX = 'refs/heads/';
@@ -121,7 +122,13 @@
input = input.substring(REF_PREFIX.length);
}
return this.restApiService
- .getRepoBranches(input, this.repo, SUGGESTIONS_LIMIT)
+ .getRepoBranches(
+ input,
+ this.repo,
+ SUGGESTIONS_LIMIT,
+ /* offset=*/ undefined,
+ throwingErrorCallback
+ )
.then(res => this.branchResponseToSuggestions(res));
}
@@ -143,7 +150,12 @@
// private but used in test
getRepoSuggestions(input: string) {
return this.restApiService
- .getRepos(input, SUGGESTIONS_LIMIT)
+ .getRepos(
+ input,
+ SUGGESTIONS_LIMIT,
+ /* offset=*/ undefined,
+ throwingErrorCallback
+ )
.then(res => this.repoResponseToSuggestions(res));
}
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
index 1fbe5b2..2de5b5f 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper.ts
@@ -183,6 +183,35 @@
[name: string]: string[] | string | number | boolean | undefined | null;
};
+/**
+ * Error callback that throws an error.
+ *
+ * Pass into REST API methods as errFn to make the returned Promises reject on
+ * error.
+ *
+ * If error is provided, it's thrown.
+ * Otherwise if response with error is provided the promise that will throw an
+ * error is returned.
+ */
+export function throwingErrorCallback(
+ response?: Response | null,
+ err?: Error
+): void | Promise<void> {
+ if (err) throw err;
+ if (!response) return;
+
+ return response.text().then(errorText => {
+ let message = `Error ${response.status}`;
+ if (response.statusText) {
+ message += ` (${response.statusText})`;
+ }
+ if (errorText) {
+ message += `: ${errorText}`;
+ }
+ throw new Error(message);
+ });
+}
+
interface SendRequestBase {
method: HttpMethod | undefined;
body?: RequestPayload;
@@ -361,27 +390,26 @@
*
* @param noAcceptHeader - don't add default accept json header
*/
- fetchJSON(
+ async fetchJSON(
req: FetchJSONRequest,
noAcceptHeader?: boolean
): Promise<ParsedJSON | undefined> {
if (!noAcceptHeader) {
req = this.addAcceptJsonHeader(req);
}
- return this.fetchRawJSON(req).then(response => {
- if (!response) {
+ const response = await this.fetchRawJSON(req);
+ if (!response) {
+ return;
+ }
+ if (!response.ok) {
+ if (req.errFn) {
+ await req.errFn.call(undefined, response);
return;
}
- if (!response.ok) {
- if (req.errFn) {
- req.errFn.call(null, response);
- return;
- }
- fireServerError(response, req);
- return;
- }
- return this.getResponseObject(response);
- });
+ fireServerError(response, req);
+ return;
+ }
+ return this.getResponseObject(response);
}
urlWithParams(url: string, fetchParams?: FetchParams): string {
@@ -474,7 +502,7 @@
* (i.e. no exception and response.ok is true). If response fails then
* promise resolves either to void if errFn is set or rejects if errFn
* is not set */
- send(req: SendRequest): Promise<Response | ParsedJSON | undefined> {
+ async send(req: SendRequest): Promise<Response | ParsedJSON | undefined> {
const options: AuthRequestInit = {method: req.method};
if (req.body) {
options.headers = new Headers();
@@ -499,38 +527,30 @@
fetchOptions: options,
anonymizedUrl: req.reportUrlAsIs ? url : req.anonymizedUrl,
};
- const xhr = this.fetch(fetchReq)
- .catch(err => {
- fireNetworkError(err);
- if (req.errFn) {
- req.errFn.call(undefined, null, err);
- return;
- } else {
- throw err;
- }
- })
- .then(response => {
- if (response && !response.ok) {
- if (req.errFn) {
- req.errFn.call(undefined, response);
- return;
- }
- fireServerError(response, fetchReq);
- }
- return response;
- });
+ let xhr;
+ try {
+ xhr = await this.fetch(fetchReq);
+ } catch (err) {
+ fireNetworkError(err as Error);
+ if (req.errFn) {
+ req.errFn.call(undefined, null, err as Error);
+ xhr = undefined;
+ } else {
+ throw err;
+ }
+ }
+ if (xhr && !xhr.ok) {
+ if (req.errFn) {
+ await req.errFn.call(undefined, xhr);
+ } else {
+ fireServerError(xhr, fetchReq);
+ }
+ }
if (req.parseResponse) {
- // TODO(TS): remove as Response and fix error.
- // Javascript code allows returning of a Response object from errFn.
- // This can be a mistake and we should add check here or it can be used
- // somewhere - in this case we should fix it carefully (define
- // different type of callback if parseResponse is true, etc...).
- return xhr.then(res => this.getResponseObject(res as Response));
+ xhr = xhr && this.getResponseObject(xhr);
}
- // The actual xhr type is Promise<Response|undefined|void> because of the
- // catch callback
- return xhr as Promise<Response | undefined>;
+ return xhr;
}
invalidateFetchPromisesPrefix(prefix: string) {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.ts
index 1234b59..9f0319e 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.ts
@@ -9,7 +9,7 @@
FetchPromisesCache,
GrRestApiHelper,
} from './gr-rest-api-helper';
-import {waitEventLoop} from '../../../../test/test-utils';
+import {assertFails, waitEventLoop} from '../../../../test/test-utils';
import {FakeScheduler} from '../../../../services/scheduler/fake-scheduler';
import {RetryScheduler} from '../../../../services/scheduler/retry-scheduler';
import {ParsedJSON} from '../../../../types/common';
@@ -229,6 +229,79 @@
assert.isTrue(cancelCalled);
});
+ suite('throwing in errFn', () => {
+ function throwInPromise(response?: Response | null, _?: Error) {
+ return response?.text().then(text => {
+ throw new Error(text);
+ });
+ }
+
+ function throwImmediately(_1?: Response | null, _2?: Error) {
+ throw new Error('Error Callback error');
+ }
+
+ setup(() => {
+ authFetchStub.returns(
+ Promise.resolve({
+ ...new Response(),
+ status: 400,
+ ok: false,
+ text() {
+ return Promise.resolve('Nope');
+ },
+ })
+ );
+ });
+
+ test('errFn with Promise throw cause send to reject on error', async () => {
+ const promise = helper.send({
+ method: HttpMethod.GET,
+ url: '/dummy/url',
+ parseResponse: false,
+ errFn: throwInPromise,
+ });
+ await assertReadRequest();
+
+ const err = await assertFails(promise);
+ assert.equal((err as Error).message, 'Nope');
+ });
+
+ test('errFn with Promise throw cause fetchJSON to reject on error', async () => {
+ const promise = helper.fetchJSON({
+ url: '/dummy/url',
+ errFn: throwInPromise,
+ });
+ await assertReadRequest();
+
+ const err = await assertFails(promise);
+ assert.equal((err as Error).message, 'Nope');
+ });
+
+ test('errFn with immediate throw cause send to reject on error', async () => {
+ const promise = helper.send({
+ method: HttpMethod.GET,
+ url: '/dummy/url',
+ parseResponse: false,
+ errFn: throwImmediately,
+ });
+ await assertReadRequest();
+
+ const err = await assertFails(promise);
+ assert.equal((err as Error).message, 'Error Callback error');
+ });
+
+ test('errFn with immediate Promise cause fetchJSON to reject on error', async () => {
+ const promise = helper.fetchJSON({
+ url: '/dummy/url',
+ errFn: throwImmediately,
+ });
+ await assertReadRequest();
+
+ const err = await assertFails(promise);
+ assert.equal((err as Error).message, 'Error Callback error');
+ });
+ });
+
suite('429 errors', () => {
setup(() => {
authFetchStub.returns(
diff --git a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
index 9b7986b..6ad03a3 100644
--- a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
+++ b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api-impl.ts
@@ -1076,7 +1076,8 @@
changesPerPage?: number,
query?: string,
offset?: 'n,z' | number,
- options?: string
+ options?: string,
+ errFn?: ErrorCallback
): Promise<ChangeInfo[] | undefined> {
const request = this.getRequestForGetChanges(
changesPerPage,
@@ -1086,9 +1087,13 @@
);
return Promise.resolve(
- this._restApiHelper.fetchJSON(request, true) as Promise<
- ChangeInfo[] | undefined
- >
+ this._restApiHelper.fetchJSON(
+ {
+ ...request,
+ errFn,
+ },
+ true
+ ) as Promise<ChangeInfo[] | undefined>
).then(response => {
if (!response) {
return;
@@ -1313,13 +1318,15 @@
queryChangeFiles(
changeNum: NumericChangeId,
patchNum: PatchSetNum,
- query: string
+ query: string,
+ errFn?: ErrorCallback
) {
return this._getChangeURLAndFetch({
changeNum,
endpoint: `/files?q=${encodeURIComponent(query)}`,
revision: patchNum,
anonymizedEndpoint: '/files?q=*',
+ errFn,
}) as Promise<string[] | undefined>;
}
@@ -1350,22 +1357,37 @@
>;
}
- getChangeSuggestedReviewers(changeNum: NumericChangeId, inputVal: string) {
+ getChangeSuggestedReviewers(
+ changeNum: NumericChangeId,
+ inputVal: string,
+ errFn?: ErrorCallback
+ ) {
return this._getChangeSuggestedGroup(
ReviewerState.REVIEWER,
changeNum,
- inputVal
+ inputVal,
+ errFn
);
}
- getChangeSuggestedCCs(changeNum: NumericChangeId, inputVal: string) {
- return this._getChangeSuggestedGroup(ReviewerState.CC, changeNum, inputVal);
+ getChangeSuggestedCCs(
+ changeNum: NumericChangeId,
+ inputVal: string,
+ errFn?: ErrorCallback
+ ) {
+ return this._getChangeSuggestedGroup(
+ ReviewerState.CC,
+ changeNum,
+ inputVal,
+ errFn
+ );
}
_getChangeSuggestedGroup(
reviewerState: ReviewerState,
changeNum: NumericChangeId,
- inputVal: string
+ inputVal: string,
+ errFn?: ErrorCallback
): Promise<SuggestedReviewerInfo[] | undefined> {
// More suggestions may obscure content underneath in the reply dialog,
// see issue 10793.
@@ -1381,6 +1403,7 @@
endpoint: '/suggest_reviewers',
params,
reportEndpointAsIs: true,
+ errFn,
}) as Promise<SuggestedReviewerInfo[] | undefined>;
}
@@ -1468,7 +1491,8 @@
async getRepos(
filter: string | undefined,
reposPerPage: number,
- offset?: number
+ offset?: number,
+ errFn?: ErrorCallback
): Promise<ProjectInfoWithName[] | undefined> {
const [isQuery, url] = this._getReposUrl(filter, reposPerPage, offset);
@@ -1482,11 +1506,13 @@
return this._fetchSharedCacheURL({
url,
anonymizedUrl: '/projects/?*',
+ errFn,
}) as Promise<ProjectInfoWithName[] | undefined>;
} else {
const result = await (this._fetchSharedCacheURL({
url,
anonymizedUrl: '/projects/?*',
+ errFn,
}) as Promise<NameToProjectInfoMap | undefined>);
if (result === undefined) return [];
return Object.entries(result).map(([name, project]) => {
@@ -1612,7 +1638,8 @@
getSuggestedGroups(
inputVal: string,
project?: RepoName,
- n?: number
+ n?: number,
+ errFn?: ErrorCallback
): Promise<GroupNameToGroupInfoMap | undefined> {
const params: QueryGroupsParams = {s: inputVal};
if (n) {
@@ -1625,12 +1652,14 @@
url: '/groups/',
params,
reportUrlAsIs: true,
+ errFn,
}) as Promise<GroupNameToGroupInfoMap | undefined>;
}
getSuggestedRepos(
inputVal: string,
- n?: number
+ n?: number,
+ errFn?: ErrorCallback
): Promise<NameToProjectInfoMap | undefined> {
const params = {
m: inputVal,
@@ -1644,6 +1673,7 @@
url: '/projects/',
params,
reportUrlAsIs: true,
+ errFn,
});
}
@@ -1651,7 +1681,8 @@
inputVal: string,
n?: number,
canSee?: NumericChangeId,
- filterActive?: boolean
+ filterActive?: boolean,
+ errFn?: ErrorCallback
): Promise<AccountInfo[] | undefined> {
const params: QueryAccountsParams = {o: 'DETAILS', q: ''};
const queryParams = [];
@@ -1678,6 +1709,7 @@
url: '/accounts/',
params,
anonymizedUrl: '/accounts/?n=*',
+ errFn,
}) as Promise<AccountInfo[] | undefined>;
}
@@ -1830,23 +1862,29 @@
}) as Promise<ChangeInfo[] | undefined>;
}
- getChangesWithSimilarTopic(topic: string): Promise<ChangeInfo[] | undefined> {
+ getChangesWithSimilarTopic(
+ topic: string,
+ errFn?: ErrorCallback
+ ): Promise<ChangeInfo[] | undefined> {
const query = `intopic:${escapeAndWrapSearchOperatorValue(topic)}`;
return this._restApiHelper.fetchJSON({
url: '/changes/',
params: {q: query},
anonymizedUrl: '/changes/intopic:*',
+ errFn,
}) as Promise<ChangeInfo[] | undefined>;
}
getChangesWithSimilarHashtag(
- hashtag: string
+ hashtag: string,
+ errFn?: ErrorCallback
): Promise<ChangeInfo[] | undefined> {
const query = `inhashtag:${escapeAndWrapSearchOperatorValue(hashtag)}`;
return this._restApiHelper.fetchJSON({
url: '/changes/',
params: {q: query},
anonymizedUrl: '/changes/inhashtag:*',
+ errFn,
}) as Promise<ChangeInfo[] | undefined>;
}
diff --git a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api.ts b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api.ts
index 984efa9..b4b1afb 100644
--- a/polygerrit-ui/app/services/gr-rest-api/gr-rest-api.ts
+++ b/polygerrit-ui/app/services/gr-rest-api/gr-rest-api.ts
@@ -127,7 +127,8 @@
getRepos(
filter: string | undefined,
reposPerPage: number,
- offset?: number
+ offset?: number,
+ errFn?: ErrorCallback
): Promise<ProjectInfoWithName[] | undefined>;
send(
@@ -152,11 +153,13 @@
getChangeSuggestedReviewers(
changeNum: NumericChangeId,
- input: string
+ input: string,
+ errFn?: ErrorCallback
): Promise<SuggestedReviewerInfo[] | undefined>;
getChangeSuggestedCCs(
changeNum: NumericChangeId,
- input: string
+ input: string,
+ errFn?: ErrorCallback
): Promise<SuggestedReviewerInfo[] | undefined>;
/**
* Request list of accounts via https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html#query-account
@@ -166,12 +169,14 @@
input: string,
n?: number,
canSee?: NumericChangeId,
- filterActive?: boolean
+ filterActive?: boolean,
+ errFn?: ErrorCallback
): Promise<AccountInfo[] | undefined>;
getSuggestedGroups(
input: string,
project?: RepoName,
- n?: number
+ n?: number,
+ errFn?: ErrorCallback
): Promise<GroupNameToGroupInfoMap | undefined>;
/**
* Execute a change action or revision action on a change.
@@ -267,7 +272,8 @@
queryChangeFiles(
changeNum: NumericChangeId,
patchNum: PatchSetNum,
- query: string
+ query: string,
+ errFn?: ErrorCallback
): Promise<string[] | undefined>;
getRepoAccessRights(
@@ -472,7 +478,8 @@
changesPerPage?: number,
query?: string,
offset?: 'n,z' | number,
- options?: string
+ options?: string,
+ errFn?: ErrorCallback
): Promise<ChangeInfo[] | undefined>;
getChangesForMultipleQueries(
changesPerPage?: number,
@@ -515,7 +522,8 @@
getSuggestedRepos(
inputVal: string,
- n?: number
+ n?: number,
+ errFn?: ErrorCallback
): Promise<NameToProjectInfoMap | undefined>;
invalidateGroupsCache(): void;
@@ -652,9 +660,13 @@
changeToExclude?: NumericChangeId;
}
): Promise<ChangeInfo[] | undefined>;
- getChangesWithSimilarTopic(topic: string): Promise<ChangeInfo[] | undefined>;
+ getChangesWithSimilarTopic(
+ topic: string,
+ errFn?: ErrorCallback
+ ): Promise<ChangeInfo[] | undefined>;
getChangesWithSimilarHashtag(
- hashtag: string
+ hashtag: string,
+ errFn?: ErrorCallback
): Promise<ChangeInfo[] | undefined>;
/**
diff --git a/polygerrit-ui/app/services/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.ts b/polygerrit-ui/app/services/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.ts
index 3b13dbc..842dace 100644
--- a/polygerrit-ui/app/services/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.ts
+++ b/polygerrit-ui/app/services/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.ts
@@ -29,6 +29,7 @@
GroupId,
ReviewerState,
} from '../../api/rest-api';
+import {throwingErrorCallback} from '../../elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
export interface ReviewerSuggestionsProvider {
getSuggestions(input: string): Promise<Suggestion[]>;
@@ -52,6 +53,11 @@
this.changes = changes;
}
+ /**
+ * Requests related suggestions.
+ *
+ * If the request fails the returned promise is rejected.
+ */
async getSuggestions(input: string): Promise<Suggestion[]> {
if (!this.loggedIn) return [];
@@ -121,8 +127,16 @@
input: string
): Promise<SuggestedReviewerInfo[] | undefined> {
return this.type === ReviewerState.REVIEWER
- ? this.restApi.getChangeSuggestedReviewers(changeNumber, input)
- : this.restApi.getChangeSuggestedCCs(changeNumber, input);
+ ? this.restApi.getChangeSuggestedReviewers(
+ changeNumber,
+ input,
+ throwingErrorCallback
+ )
+ : this.restApi.getChangeSuggestedCCs(
+ changeNumber,
+ input,
+ throwingErrorCallback
+ );
}
}
diff --git a/polygerrit-ui/app/utils/account-util.ts b/polygerrit-ui/app/utils/account-util.ts
index bfddbdc..ebf9e7a 100644
--- a/polygerrit-ui/app/utils/account-util.ts
+++ b/polygerrit-ui/app/utils/account-util.ts
@@ -24,6 +24,7 @@
import {getApprovalInfo} from './label-util';
import {RestApiService} from '../services/gr-rest-api/gr-rest-api';
import {ParsedChangeInfo} from '../types/types';
+import {throwingErrorCallback} from '../elements/shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper';
export const ACCOUNT_TEMPLATE_REGEX = '<GERRIT_ACCOUNT_(\\d+)>';
const SUGGESTIONS_LIMIT = 15;
@@ -196,7 +197,13 @@
filterActive = false
) {
return restApiService
- .getSuggestedAccounts(input, SUGGESTIONS_LIMIT, canSee, filterActive)
+ .getSuggestedAccounts(
+ input,
+ SUGGESTIONS_LIMIT,
+ canSee,
+ filterActive,
+ throwingErrorCallback
+ )
.then(accounts => {
if (!accounts) return [];
const accountSuggestions = [];