
The REST API service was recently fixed to fire error events on the document instead of on itself. This is a change in behavior for plugins, because they were using a component instance that was detached from the DOM. We want to revert back to the behavior where plugins would typically not expect Gerrit to handle errors by showing error dialogs. So we are defining a default errFn at least for the known case of a Chrome plugin that would otherwise show a distracting dialog when no config was found. We are swapping the catch() and then() clauses in the rest-api-helper such that errFn can throw an error without the catch() clause catching it. errFn throwing an error is the only situation where the swap matters. We are planning a lot of further clean-ups and want to ultimately remove the errFn support. There are a lot of follow-up changes expected, but here we just want to fix Gerrit and bring it into a releasable state. Change-Id: I3f441a0a7587b791223c4ae89fe0547cb2ea090c
197 lines
5.1 KiB
TypeScript
197 lines
5.1 KiB
TypeScript
/**
|
|
* @license
|
|
* 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.
|
|
*/
|
|
import {
|
|
ErrorCallback,
|
|
RestApiService,
|
|
} from '../../../services/services/gr-rest-api/gr-rest-api';
|
|
import {HttpMethod} from '../../../constants/constants';
|
|
import {RequestPayload} from '../../../types/common';
|
|
|
|
let restApi: RestApiService | null = null;
|
|
|
|
export function _testOnlyResetRestApi() {
|
|
restApi = null;
|
|
}
|
|
|
|
function getRestApi(): RestApiService {
|
|
if (!restApi) {
|
|
restApi = (document.createElement(
|
|
'gr-rest-api-interface'
|
|
) as unknown) as RestApiService;
|
|
}
|
|
return restApi;
|
|
}
|
|
|
|
export class GrPluginRestApi {
|
|
constructor(private readonly prefix = '') {}
|
|
|
|
getLoggedIn() {
|
|
return getRestApi().getLoggedIn();
|
|
}
|
|
|
|
getVersion() {
|
|
return getRestApi().getVersion();
|
|
}
|
|
|
|
getConfig() {
|
|
return getRestApi().getConfig();
|
|
}
|
|
|
|
invalidateReposCache() {
|
|
getRestApi().invalidateReposCache();
|
|
}
|
|
|
|
getAccount() {
|
|
return getRestApi().getAccount();
|
|
}
|
|
|
|
getAccountCapabilities(capabilities: string[]) {
|
|
return getRestApi().getAccountCapabilities(capabilities);
|
|
}
|
|
|
|
getRepos(filter: string, reposPerPage: number, offset?: number) {
|
|
return getRestApi().getRepos(filter, reposPerPage, offset);
|
|
}
|
|
|
|
fetch(
|
|
method: HttpMethod,
|
|
url: string,
|
|
payload?: RequestPayload,
|
|
errFn?: undefined,
|
|
contentType?: string
|
|
): Promise<Response>;
|
|
|
|
fetch(
|
|
method: HttpMethod,
|
|
url: string,
|
|
payload: RequestPayload | undefined,
|
|
errFn: ErrorCallback,
|
|
contentType?: string
|
|
): Promise<Response | void>;
|
|
|
|
fetch(
|
|
method: HttpMethod,
|
|
url: string,
|
|
payload: RequestPayload | undefined,
|
|
errFn?: ErrorCallback,
|
|
contentType?: string
|
|
): Promise<Response | void>;
|
|
|
|
/**
|
|
* Fetch and return native browser REST API Response.
|
|
*/
|
|
fetch(
|
|
method: HttpMethod,
|
|
url: string,
|
|
payload?: RequestPayload,
|
|
errFn?: ErrorCallback,
|
|
contentType?: string
|
|
): Promise<Response | void> {
|
|
return getRestApi().send(
|
|
method,
|
|
this.prefix + url,
|
|
payload,
|
|
errFn,
|
|
contentType
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Fetch and parse REST API response, if request succeeds.
|
|
*/
|
|
send(
|
|
method: HttpMethod,
|
|
url: string,
|
|
payload?: RequestPayload,
|
|
errFn?: ErrorCallback,
|
|
contentType?: string
|
|
) {
|
|
// Plugins typically don't want Gerrit to show error dialogs for failed
|
|
// requests. So we are defining a default errFn here, even if it is not
|
|
// explicitly set by the caller.
|
|
// TODO: We are soon getting rid of the `errFn` altogether. There are only
|
|
// 2 known usages of errFn in plugins: delete-project and verify-status.
|
|
errFn =
|
|
errFn ??
|
|
((response: Response | null | undefined, error?: Error) => {
|
|
if (error) throw error;
|
|
if (response) throw new Error(`${response.status}`);
|
|
throw new Error('Generic REST API error.');
|
|
});
|
|
return this.fetch(method, url, payload, errFn, contentType).then(
|
|
response => {
|
|
// Will typically not happen. The response can only be unset, if the
|
|
// errFn handles the error and then returns void or undefined or null.
|
|
// But the errFn above always throws.
|
|
if (!response) {
|
|
throw new Error('plugin rest-api call failed');
|
|
}
|
|
// Will typically not happen. errFn will have dealt with that and the
|
|
// caller will get a rejected promise already.
|
|
if (response.status < 200 || response.status >= 300) {
|
|
return response.text().then(text => {
|
|
if (text) {
|
|
return Promise.reject(new Error(text));
|
|
} else {
|
|
return Promise.reject(new Error(`${response.status}`));
|
|
}
|
|
});
|
|
} else {
|
|
return getRestApi().getResponseObject(response);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
get(url: string) {
|
|
return this.send(HttpMethod.GET, url);
|
|
}
|
|
|
|
post(
|
|
url: string,
|
|
payload?: RequestPayload,
|
|
errFn?: ErrorCallback,
|
|
contentType?: string
|
|
) {
|
|
return this.send(HttpMethod.POST, url, payload, errFn, contentType);
|
|
}
|
|
|
|
put(
|
|
url: string,
|
|
payload?: RequestPayload,
|
|
errFn?: ErrorCallback,
|
|
contentType?: string
|
|
) {
|
|
return this.send(HttpMethod.PUT, url, payload, errFn, contentType);
|
|
}
|
|
|
|
delete(url: string) {
|
|
return this.fetch(HttpMethod.DELETE, url).then(response => {
|
|
if (response.status !== 204) {
|
|
return response.text().then(text => {
|
|
if (text) {
|
|
return Promise.reject(new Error(text));
|
|
} else {
|
|
return Promise.reject(new Error(`${response.status}`));
|
|
}
|
|
});
|
|
}
|
|
return response;
|
|
});
|
|
}
|
|
}
|