Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Use server-sent error messages when applicable #9909

Merged
merged 4 commits into from
Jan 28, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions ui/app/components/job-page/parts/latest-deployment.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Component from '@ember/component';
import { task } from 'ember-concurrency';
import { ForbiddenError } from '@ember-data/adapter/error';
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
import { tagName } from '@ember-decorators/component';
import classic from 'ember-classic-decorator';
Expand All @@ -18,15 +17,9 @@ export default class LatestDeployment extends Component {
try {
yield this.get('job.latestDeployment.content').promote();
} catch (err) {
let message = messageFromAdapterError(err);

if (err instanceof ForbiddenError) {
message = 'Your ACL token does not grant permission to promote deployments.';
}

this.handleError({
title: 'Could Not Promote Deployment',
description: message,
description: messageFromAdapterError(err, 'promote deployments'),
});
}
})
Expand Down
11 changes: 2 additions & 9 deletions ui/app/components/job-page/parts/title.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import Component from '@ember/component';
import { task } from 'ember-concurrency';
import { ForbiddenError } from '@ember-data/adapter/error';
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
import { tagName } from '@ember-decorators/component';
import classic from 'ember-classic-decorator';
Expand All @@ -22,7 +21,7 @@ export default class Title extends Component {
} catch (err) {
this.handleError({
title: 'Could Not Stop Job',
description: 'Your ACL token does not grant permission to stop jobs.',
description: messageFromAdapterError(err, 'stop jobs'),
});
}
})
Expand All @@ -41,15 +40,9 @@ export default class Title extends Component {
// Eagerly update the job status to avoid flickering
job.set('status', 'running');
} catch (err) {
let message = messageFromAdapterError(err);

if (err instanceof ForbiddenError) {
message = 'Your ACL token does not grant permission to stop jobs.';
}

this.handleError({
title: 'Could Not Start Job',
description: message,
description: messageFromAdapterError(err, 'start jobs'),
});
}
})
Expand Down
5 changes: 3 additions & 2 deletions ui/app/components/job-page/periodic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import AbstractJobPage from './abstract';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import classic from 'ember-classic-decorator';
import messageForError from 'nomad-ui/utils/message-from-adapter-error';

@classic
export default class Periodic extends AbstractJobPage {
Expand All @@ -11,10 +12,10 @@ export default class Periodic extends AbstractJobPage {

@action
forceLaunch() {
this.job.forcePeriodic().catch(() => {
this.job.forcePeriodic().catch(err => {
this.set('errorMessage', {
title: 'Could Not Force Launch',
description: 'Your ACL token does not grant permission to submit jobs.',
description: messageForError(err, 'submit jobs'),
});
});
}
Expand Down
5 changes: 3 additions & 2 deletions ui/app/controllers/allocations/allocation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { task } from 'ember-concurrency';
import Sortable from 'nomad-ui/mixins/sortable';
import { lazyClick } from 'nomad-ui/helpers/lazy-click';
import { watchRecord } from 'nomad-ui/utils/properties/watch';
import messageForError from 'nomad-ui/utils/message-from-adapter-error';
import classic from 'ember-classic-decorator';

@classic
Expand Down Expand Up @@ -73,7 +74,7 @@ export default class IndexController extends Controller.extend(Sortable) {
} catch (err) {
this.set('error', {
title: 'Could Not Stop Allocation',
description: 'Your ACL token does not grant allocation lifecycle permissions.',
description: messageForError(err, 'manage allocation lifecycle'),
});
}
})
Expand All @@ -85,7 +86,7 @@ export default class IndexController extends Controller.extend(Sortable) {
} catch (err) {
this.set('error', {
title: 'Could Not Restart Allocation',
description: 'Your ACL token does not grant allocation lifecycle permissions.',
description: messageForError(err, 'manage allocation lifecycle'),
});
}
})
Expand Down
3 changes: 2 additions & 1 deletion ui/app/controllers/allocations/allocation/task/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Controller from '@ember/controller';
import { computed as overridable } from 'ember-overridable-computed';
import { task } from 'ember-concurrency';
import classic from 'ember-classic-decorator';
import messageForError from 'nomad-ui/utils/message-from-adapter-error';

@classic
export default class IndexController extends Controller {
Expand All @@ -21,7 +22,7 @@ export default class IndexController extends Controller {
} catch (err) {
this.set('error', {
title: 'Could Not Restart Task',
description: 'Your ACL token does not grant allocation lifecycle permissions.',
description: messageForError(err, 'manage allocation lifecycle'),
});
}
})
Expand Down
12 changes: 10 additions & 2 deletions ui/app/utils/message-from-adapter-error.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { ForbiddenError } from '@ember-data/adapter/error';

// Returns a single string based on the response the adapter received
export default function messageFromAdapterError(error) {
if (error.errors) {
export default function messageFromAdapterError(error, actionMessage) {
if (error instanceof ForbiddenError) {
return `Your ACL token does not grant permission to ${actionMessage}.`;
}

if (error.errors?.length) {
return error.errors.mapBy('detail').join('\n\n');
}

return 'Unknown Error';
}
44 changes: 44 additions & 0 deletions ui/tests/unit/utils/message-from-adapter-error-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { module, test } from 'qunit';
import { ServerError, ForbiddenError } from '@ember-data/adapter/error';
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';

const testCases = [
{
name: 'Forbidden Error',
in: [new ForbiddenError([], "Can't do that"), 'run tests'],
out: 'Your ACL token does not grant permission to run tests.',
},
{
name: 'Generic Error',
in: [new ServerError([{ detail: 'DB Max Connections' }], 'Server Error'), 'run tests'],
out: 'DB Max Connections',
},
{
name: 'Multiple Errors',
in: [
new ServerError(
[{ detail: 'DB Max Connections' }, { detail: 'Service timeout' }],
'Server Error'
),
'run tests',
],
out: 'DB Max Connections\n\nService timeout',
},
{
name: 'Malformed Error (not from Ember Data which should always have an errors list)',
in: [new Error('Go boom'), 'handle custom error messages'],
out: 'Unknown Error',
},
];

module('Unit | Util | messageFromAdapterError', function() {
testCases.forEach(testCase => {
test(testCase.name, function(assert) {
assert.equal(
messageFromAdapterError.apply(null, testCase.in),
testCase.out,
`[${testCase.in.join(', ')}] => ${testCase.out}`
);
});
});
});