Show different error message if server config is invalid
If a code-owners operation cannot be performed because the project
contains an invalid OWNERS file the backend returns a '409 Conflict'
response informing about the invalid OWNERS file. An invalid OWNERS file
is a configuration issue that needs to be fixed by the project owner or
host admin, it's not an error in the code-owners plugin (an error would
be signaled as 500 response). At the moment the frontend reports invalid
OWNERS file as a red error banner. This gives the wrong impression that
there was an error in the code-owners plugin. The way how invalid OWNERS
files are reported/shown to the user is changed so that it's more clear
that this is a configuration issue and not an error in the plugin.
Change-Id: I0e2524cbd1c76f954b77aff19a0da06b78574aec
diff --git a/ui/code-owners-api.js b/ui/code-owners-api.js
index 408428b..a1a1aed 100644
--- a/ui/code-owners-api.js
+++ b/ui/code-owners-api.js
@@ -15,12 +15,10 @@
* limitations under the License.
*/
-
// TODO: Try to remove it. The ResponseError and getErrorMessage duplicates
// code from the gr-plugin-rest-api.ts. This code is required because
// we want custom error processing in some functions. For details see
// the original gr-plugin-rest-api.ts file/
-
class ResponseError extends Error {
constructor(response) {
super();
@@ -28,6 +26,12 @@
}
}
+export class ServerConfigurationError extends Error {
+ constructor(msg) {
+ super(msg);
+ }
+}
+
async function getErrorMessage(response) {
const text = await response.text();
return text ?
@@ -46,13 +50,39 @@
}
/**
+ * Send a get request and provides custom response-code handling
+ */
+ async _get(url) {
+ const errFn = (response, error) => {
+ if (error) throw error;
+ if (response) throw new ResponseError(response);
+ throw new Error('Generic REST API error');
+ };
+ try {
+ return await this.restApi.send(
+ 'GET',
+ url,
+ undefined,
+ errFn
+ );
+ } catch (err) {
+ if (err instanceof ResponseError && err.response.status === 409) {
+ return getErrorMessage(err.response).then(msg => {
+ throw new ServerConfigurationError(msg);
+ });
+ }
+ throw err;
+ }
+ }
+
+ /**
* Returns a promise fetching the owner statuses for all files within the change.
*
* @doc https://gerrit.googlesource.com/plugins/code-owners/+/refs/heads/master/resources/Documentation/rest-api.md#change-endpoints
* @param {string} changeId
*/
listOwnerStatus(changeId) {
- return this.restApi.get(`/changes/${changeId}/code_owners.status`);
+ return this._get(`/changes/${changeId}/code_owners.status`);
}
/**
@@ -63,7 +93,7 @@
* @param {string} path
*/
listOwnersForPath(changeId, path, limit) {
- return this.restApi.get(
+ return this._get(
`/changes/${changeId}/revisions/current/code_owners` +
`/${encodeURIComponent(path)}?limit=${limit}&o=DETAILS`
);
@@ -78,7 +108,7 @@
* @param {string} path
*/
getConfigForPath(project, branch, path) {
- return this.restApi.get(
+ return this._get(
`/projects/${encodeURIComponent(project)}/` +
`branches/${encodeURIComponent(branch)}/` +
`code_owners.config/${encodeURIComponent(path)}`
@@ -93,19 +123,11 @@
* @param {string} branch
*/
async getBranchConfig(project, branch) {
- const errFn = (response, error) => {
- if (error) throw error;
- if (response) throw new ResponseError(response);
- throw new Error('Generic REST API error');
- }
try {
- const config = await this.restApi.send(
- 'GET',
+ const config = await this._get(
`/projects/${encodeURIComponent(project)}/` +
`branches/${encodeURIComponent(branch)}/` +
- `code_owners.branch_config`,
- undefined,
- errFn
+ `code_owners.branch_config`
);
if (config.override_approval && !(config.override_approval
instanceof Array)) {
@@ -116,7 +138,7 @@
return {...config, override_approval: [config.override_approval]};
}
return config;
- } catch(err) {
+ } catch (err) {
if (err instanceof ResponseError) {
if (err.response.status === 404) {
// The 404 error means that the branch doesn't exist and
diff --git a/ui/code-owners-banner.js b/ui/code-owners-banner.js
index 4e4b07e..1aa9e58 100644
--- a/ui/code-owners-banner.js
+++ b/ui/code-owners-banner.js
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import {CodeOwnersModelMixin} from './code-owners-model-mixin.js';
-import {PluginState} from './code-owners-model.js';
+import {PluginState, isPluginErrorState} from './code-owners-model.js';
// There are 2 elements in this file:
// CodeOwnersBanner - visual elements. This element is shown at the top
@@ -79,7 +79,7 @@
margin-left: var(--spacing-l);
}
</style>
- <span class="text">Error: Code-owners plugin has failed</span>
+ <span class="text">[[_getErrorText(pluginStatus)]]</span>
<gr-button link on-click="_showFailDetails">
Details
</gr-button>
@@ -112,7 +112,14 @@
}
_computeHidden(pluginStatus) {
- return !pluginStatus || pluginStatus.state !== PluginState.Failed;
+ return !pluginStatus || !isPluginErrorState(pluginStatus.state);
+ }
+
+ _getErrorText(pluginStatus) {
+ return !pluginStatus || pluginStatus.state === PluginState.Failed ?
+ 'Error: Code-owners plugin has failed' :
+ 'The code-owners plugin has configuration issue. ' +
+ 'Please contact the project owner or the host admin.';
}
_showFailDetails() {
@@ -189,7 +196,7 @@
branchState: 'LOADING',
};
}
- if (pluginStatus.state === PluginState.Failed) {
+ if (isPluginErrorState(pluginStatus.state)) {
return {
change,
branchState: 'FAILED',
diff --git a/ui/code-owners-model-loader.js b/ui/code-owners-model-loader.js
index 2fa18ac..cdc04dd 100644
--- a/ui/code-owners-model-loader.js
+++ b/ui/code-owners-model-loader.js
@@ -16,6 +16,7 @@
*/
import {SuggestionsState} from './code-owners-model.js';
+import {ServerConfigurationError} from './code-owners-api.js';
/**
* ModelLoader provides a method for loading data into the model.
@@ -39,6 +40,10 @@
try {
newValue = await propertyLoader();
} catch (e) {
+ if (e instanceof ServerConfigurationError) {
+ this.ownersModel.setServerConfigurationError(e.message);
+ return;
+ }
console.error(e);
this.ownersModel.setPluginFailed(e.message);
return;
diff --git a/ui/code-owners-model.js b/ui/code-owners-model.js
index 079eb86..ad536b4 100644
--- a/ui/code-owners-model.js
+++ b/ui/code-owners-model.js
@@ -25,9 +25,15 @@
export const PluginState = {
Enabled: 'Enabled',
Disabled: 'Disabled',
+ ServerConfigurationError: 'ServerConfigurationError',
Failed: 'Failed',
};
+export function isPluginErrorState(state) {
+ return state === PluginState.ServerConfigurationError ||
+ state === PluginState.Failed;
+}
+
export const SuggestionsType = {
BEST_SUGGESTIONS: 'BEST_SUGGESTIONS',
ALL_SUGGESTIONS: 'ALL_SUGGESTIONS',
@@ -148,6 +154,11 @@
PluginState.Enabled : PluginState.Disabled});
}
+ setServerConfigurationError(failedMessage) {
+ this._setPluginStatus({state: PluginState.ServerConfigurationError,
+ failedMessage});
+ }
+
setPluginFailed(failedMessage) {
this._setPluginStatus({state: PluginState.Failed, failedMessage});
}
@@ -163,7 +174,7 @@
return status1 === status2;
}
if (status1.state !== status2.state) return false;
- return status1.state === PluginState.Failed ?
+ return isPluginErrorState(status1.state)?
status1.failedMessage === status2.failedMessage :
true;
}
diff --git a/ui/owner-requirement.js b/ui/owner-requirement.js
index a173c75..e6ced63 100644
--- a/ui/owner-requirement.js
+++ b/ui/owner-requirement.js
@@ -18,7 +18,7 @@
import {OwnerStatus} from './code-owners-fetcher.js';
import {CodeOwnersModelMixin} from './code-owners-model-mixin.js';
import {showPluginFailedMessage} from './code-owners-banner.js';
-import {PluginState} from './code-owners-model.js';
+import {isPluginErrorState} from './code-owners-model.js';
/**
* Owner requirement control for `submit-requirement-item-code-owners` endpoint.
@@ -137,7 +137,7 @@
}
_pluginFailed(pluginStatus) {
- return pluginStatus && pluginStatus.state === PluginState.Failed;
+ return pluginStatus && isPluginErrorState(pluginStatus.state);
}
_onStatusChanged(status, userRole) {