How to deal with asynchronous tasks in the front-end?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-All executing and most recently finished asynchronous tasks are displayed on the
-"Backgroud-Tasks" menu.
-
-The front-end developer should provide a description, success message and error
-messages for each task on ``TaskManagerMessageService.messages``.
-This messages can make use of the task metadata to provide more personalized messages.
-
-When submitting an asynchronous task, the developer should provide a callback
-that will be automatically triggered after the execution of that task.
-This can be done by using the ``TaskManagerService.subscribe``.
-
-Most of the times, all we want to do after a task completes the execution, is
-displaying a notification message based on the execution result. The
-``NotificationService.notifyTask`` will use the messages from
-``TaskManagerMessageService`` to display a success / error message based on the
-execution result of a task.
+All executing and most recently finished asynchronous tasks are displayed on
+"Background-Tasks" and if finished on "Recent-Notifications" in the menu bar.
+For each task a operation name for three states (running, success and failure),
+a function that tells who is involved and error descriptions, if any, have to
+be provided. This can be achieved by appending
+``TaskManagerMessageService.messages``. This has to be done to achieve
+consistency among all tasks and states.
+
+Operation Object
+ Ensures consistency among all tasks. It consists of three verbs for each
+ different state f.e.
+ ``{running: 'Creating', failure: 'create', success: 'Created'}``.
+
+#. Put running operations in present participle f.e. ``'Updating'``.
+#. Failed messages always start with ``'Failed to '`` and should be continued
+ with the operation in present tense f.e. ``'update'``.
+#. Put successful operations in past tense f.e. ``'Updated'``.
+
+Involves Function
+ Ensures consistency among all messages of a task, it resembles who's
+ involved by the operation. It's a function that returns a string which
+ takes the metadata from the task to return f.e.
+ ``"RBD 'somePool/someImage'"``.
+
+Both combined create the following messages:
+
+* Failure => ``"Failed to create RBD 'somePool/someImage'"``
+* Running => ``"Creating RBD 'somePool/someImage'"``
+* Success => ``"Created RBD 'somePool/someImage'"``
+
+For automatic task handling use ``TaskWrapperService.wrapTaskAroundCall``.
+
+If for some reason ``wrapTaskAroundCall`` is not working for you,
+you have to subscribe to your asynchronous task manually through
+``TaskManagerService.subscribe``, and provide it with a callback,
+in case of a success to notify the user. A notification can
+be triggered with ``NotificationService.notifyTask``. It will use
+``TaskManagerMessageService.messages`` to display a message based on the state
+of a task.
+
+Notifications of API errors are handled by ``ApiInterceptorService``.
Usage example:
.. code-block:: javascript
export class TaskManagerMessageService {
-
+ // ...
messages = {
- // Messages for 'rbd/create' task
+ // Messages for task 'rbd/create'
'rbd/create': new TaskManagerMessage(
- // Description
- (metadata) => `Create RBD '${metadata.pool_name}/${metadata.image_name}'`,
- // Success message
- (metadata) => `RBD '${metadata.pool_name}/${metadata.image_name}'
- have been created successfully`,
- // Error messages
- (metadata) => {
- return {
- '17': `Name '${metadata.pool_name}/${metadata.image_name}' is already
- in use.`
- };
- }
+ // Message prefixes
+ ['create', 'Creating', 'Created'],
+ // Message suffix
+ (metadata) => `RBD '${metadata.pool_name}/${metadata.image_name}'`,
+ (metadata) => ({
+ // Error code and description
+ '17': `Name is already used by RBD '${metadata.pool_name}/${
+ metadata.image_name}'.`
+ })
),
// ...
};
-
// ...
}
export class RBDFormComponent {
// ...
-
- submit() {
- // ...
- this.rbdService.create(request).then((resp) => {
- // Subscribe the submitted task
- this.taskManagerService.subscribe('rbd/create',
- {'pool_name': request.pool_name, 'rbd_name': request.name},
- // Callback that will be invoked after task is finished
- (finishedTask: FinishedTask) => {
- // Will display a notification message (success or error)
- this.notificationService.notifyTask(finishedTask, finishedTask.ret_value.success);
- });
- // ...
- })
+ createAction() {
+ const request = this.createRequest();
+ // Subscribes to 'call' with submitted 'task' and handles notifications
+ return this.taskWrapper.wrapTaskAroundCall({
+ task: new FinishedTask('rbd/create', {
+ pool_name: request.pool_name,
+ image_name: request.name
+ }),
+ call: this.rbdService.create(request)
+ });
}
+ // ...
}
Error Handling in Python
expect(component.executingTasks[0].description).toBe(`Deleting RBD 'somePool/someImage'`);
});
- it('should get finished message for task', () => {
+ it('should get finished message for successful task', () => {
component._handleTasks([], tasks.finished);
expect(component.finishedTasks.length).toBe(2);
- expect(component.finishedTasks[0].description).toBe(`Copy RBD 'somePool/someImage'`);
+ expect(component.finishedTasks[0].description).toBe(`Copied RBD 'somePool/someImage'`);
expect(component.finishedTasks[0].errorMessage).toBe(undefined);
- expect(component.finishedTasks[1].description).toBe(`Clone RBD 'somePool/someImage'`);
+ });
+
+ it('should get failed message for finished task', () => {
+ component._handleTasks([], tasks.finished);
+ expect(component.finishedTasks.length).toBe(2);
+ expect(component.finishedTasks[1].description).toBe(`Failed to clone RBD 'somePool/someImage'`);
expect(component.finishedTasks[1].errorMessage).toBe(
- `Name 'somePool/someImage' is already in use.`
+ `Name is already used by RBD 'somePool/someImage'.`
);
});
_handleTasks(executingTasks: ExecutingTask[], finishedTasks: FinishedTask[]) {
for (const excutingTask of executingTasks) {
- excutingTask.description = this.taskMessageManager.getRunningMessage(excutingTask);
+ excutingTask.description = this.taskMessageManager.getRunningTitle(excutingTask);
}
for (const finishedTask of finishedTasks) {
- finishedTask.description = this.taskMessageManager.getDescription(finishedTask);
if (finishedTask.success === false) {
+ finishedTask.description = this.taskMessageManager.getErrorTitle(finishedTask);
finishedTask.errorMessage = this.taskMessageManager.getErrorMessage(finishedTask);
+ } else {
+ finishedTask.description = this.taskMessageManager.getSuccessTitle(finishedTask);
}
}
this.executingTasks = executingTasks;
title: string;
type: NotificationType;
- constructor(type: NotificationType = NotificationType.info, message?: string, title?: string) {
+ constructor(type: NotificationType = NotificationType.info, title?: string, message?: string) {
this.type = type;
- this.message = message;
this.title = title;
+ this.message = message;
/* string representation of the Date object so it can be directly compared
with the timestamps parsed from localStorage */
notificationService.show(NotificationType.info, 'Simple test');
tick(100);
expect(notificationService['dataSource'].getValue().length).toBe(1);
- expect(notificationService['dataSource'].getValue()[0].type).toBe(NotificationType.info);
+ const notification = notificationService['dataSource'].getValue()[0];
+ expect(notification.type).toBe(NotificationType.info);
+ expect(notification.title).toBe('Simple test');
+ expect(notification.message).toBe(undefined);
})
);
notificationService.notifyTask(task, true);
tick(100);
expect(notificationService['dataSource'].getValue().length).toBe(1);
- expect(notificationService['dataSource'].getValue()[0].type).toBe(NotificationType.success);
+ const notification = notificationService['dataSource'].getValue()[0];
+ expect(notification.type).toBe(NotificationType.success);
+ expect(notification.title).toBe('Executed unknown task');
+ expect(notification.message).toBe(undefined);
})
);
it(
'should show a error task notification',
fakeAsync(() => {
- const task = _.assign(new FinishedTask(), {
- success: false,
- metadata: 'failed',
- exception: {
- code: 404
+ const task = _.assign(
+ new FinishedTask('rbd/create', {
+ pool_name: 'somePool',
+ image_name: 'someImage'
+ }),
+ {
+ success: false,
+ exception: {
+ code: 17
+ }
}
- });
+ );
notificationService.notifyTask(task);
tick(100);
expect(notificationService['dataSource'].getValue().length).toBe(1);
- expect(notificationService['dataSource'].getValue()[0].type).toBe(NotificationType.error);
+ const notification = notificationService['dataSource'].getValue()[0];
+ expect(notification.type).toBe(NotificationType.error);
+ expect(notification.title).toBe(`Failed to create RBD 'somePool/someImage'`);
+ expect(notification.message).toBe(`Name is already used by RBD 'somePool/someImage'.`);
})
);
});
* Method used for saving a shown notification (check show() method).
* @param {Notification} notification
*/
- save(type: NotificationType, message: string, title?: string) {
- const notification = new CdNotification(type, message, title);
+ save(type: NotificationType, title: string, message?: string) {
+ const notification = new CdNotification(type, title, message);
const recent = this.dataSource.getValue();
recent.push(notification);
/**
* Method for showing a notification.
* @param {NotificationType} type toastr type
- * @param {string} message
- * @param {string} [title]
+ * @param {string} title
+ * @param {string} [message]
* @param {*} [options] toastr compatible options, used when creating a toastr
* @memberof NotificationService
* @returns The timeout ID that is set to be able to cancel the notification.
*/
- show(type: NotificationType, message: string, title?: string, options?: any) {
+ show(type: NotificationType, title: string, message?: string, options?: any) {
return setTimeout(() => {
- this.save(type, message, title);
+ this.save(type, title, message);
+ if (!message) {
+ message = '';
+ }
switch (type) {
case NotificationType.error:
this.toastr.error(message, title, options);
if (finishedTask.success && success) {
this.show(
NotificationType.success,
- this.taskManagerMessageService.getSuccessMessage(finishedTask)
+ this.taskManagerMessageService.getSuccessTitle(finishedTask)
);
} else {
this.show(
NotificationType.error,
- this.taskManagerMessageService.getErrorMessage(finishedTask),
- this.taskManagerMessageService.getDescription(finishedTask)
+ this.taskManagerMessageService.getErrorTitle(finishedTask),
+ this.taskManagerMessageService.getErrorMessage(finishedTask)
);
}
}
import { FinishedTask } from '../models/finished-task';
import { TaskException } from '../models/task-exception';
-import { TaskManagerMessageService } from './task-manager-message.service';
+import { TaskManagerMessageService, TaskMessageOperation } from './task-manager-message.service';
describe('TaskManagerMessageService', () => {
let service: TaskManagerMessageService;
expect(service).toBeTruthy();
});
- it('should getDescription', () => {
- finishedTask.name = 'foo';
- finishedTask.exception = _.assign(new TaskException(), {
- code: 1
- });
- finishedTask.metadata = {};
-
- const message = service.getDescription(finishedTask);
- expect(message).toBe('Unknown Task');
+ it('should get default description', () => {
+ expect(service.getErrorTitle(finishedTask)).toBe('Failed to execute unknown task');
});
it('should get default running message', () => {
- finishedTask.metadata = {};
- let message = service.getRunningMessage(finishedTask);
- expect(message).toBe('Executing unknown task');
- finishedTask.metadata = { component: 'rbd' };
- message = service.getRunningMessage(finishedTask);
- expect(message).toBe('Executing RBD');
- });
-
- it('should get custom running message', () => {
- finishedTask.name = 'rbd/create';
- finishedTask.metadata = {
- pool_name: 'somePool',
- image_name: 'someImage'
- };
- const message = service.getRunningMessage(finishedTask);
- expect(message).toBe(`Creating RBD 'somePool/someImage'`);
+ expect(service.getRunningTitle(finishedTask)).toBe('Executing unknown task');
});
- it('should getErrorMessage', () => {
- finishedTask.exception = _.assign(new TaskException(), {
- code: 1
- });
- const message = service.getErrorMessage(finishedTask);
- expect(message).toBe(undefined);
+ it('should get default running message with a set component', () => {
+ finishedTask.metadata = { component: 'rbd' };
+ expect(service.getRunningTitle(finishedTask)).toBe('Executing RBD');
});
it('should getSuccessMessage', () => {
- const message = service.getSuccessMessage(finishedTask);
- expect(message).toBe('Task executed successfully');
+ expect(service.getSuccessTitle(finishedTask)).toBe('Executed unknown task');
});
- it('should test if all messages methods are defined', () => {
- _.forIn(service.messages, (value, key) => {
- expect(value.descr({})).toBeTruthy();
- expect(value.success({})).toBeTruthy();
- expect(value.error({})).toBeTruthy();
- expect(value.running({})).toBeTruthy();
+ describe('defined tasks messages', () => {
+ const testMessages = (operation: TaskMessageOperation, involves: string) => {
+ expect(service.getRunningTitle(finishedTask)).toBe(operation.running + ' ' + involves);
+ expect(service.getErrorTitle(finishedTask)).toBe(
+ 'Failed to ' + operation.failure + ' ' + involves
+ );
+ expect(service.getSuccessTitle(finishedTask)).toBe(operation.success + ' ' + involves);
+ };
+
+ const testCreate = (involves: string) => {
+ testMessages(new TaskMessageOperation('Creating', 'create', 'Created'), involves);
+ };
+
+ const testUpdate = (involves: string) => {
+ testMessages(new TaskMessageOperation('Updating', 'update', 'Updated'), involves);
+ };
+
+ const testDelete = (involves: string) => {
+ testMessages(new TaskMessageOperation('Deleting', 'delete', 'Deleted'), involves);
+ };
+
+ const testErrorCode = (code: number, msg: string) => {
+ finishedTask.exception = _.assign(new TaskException(), {
+ code: code
+ });
+ expect(service.getErrorMessage(finishedTask)).toBe(msg);
+ };
+
+ describe('rbd tasks', () => {
+ let defaultMsg: string;
+ let childMsg: string;
+ let destinationMsg: string;
+ let snapMsg: string;
+
+ beforeEach(() => {
+ const metadata = {
+ pool_name: 'somePool',
+ image_name: 'someImage',
+ snapshot_name: 'someSnapShot',
+ dest_pool_name: 'someDestinationPool',
+ dest_image_name: 'someDestinationImage',
+ child_pool_name: 'someChildPool',
+ child_image_name: 'someChildImage'
+ };
+ defaultMsg = `RBD '${metadata.pool_name}/${metadata.image_name}'`;
+ childMsg = `RBD '${metadata.child_pool_name}/${metadata.child_image_name}'`;
+ destinationMsg = `RBD '${metadata.dest_pool_name}/${metadata.dest_image_name}'`;
+ snapMsg = `RBD snapshot '${metadata.pool_name}/${metadata.image_name}@${
+ metadata.snapshot_name
+ }'`;
+ finishedTask.metadata = metadata;
+ });
+
+ it('tests rbd/create messages', () => {
+ finishedTask.name = 'rbd/create';
+ testCreate(defaultMsg);
+ testErrorCode(17, `Name is already used by ${defaultMsg}.`);
+ });
+
+ it('tests rbd/edit messages', () => {
+ finishedTask.name = 'rbd/edit';
+ testUpdate(defaultMsg);
+ testErrorCode(17, `Name is already used by ${defaultMsg}.`);
+ });
+
+ it('tests rbd/delete messages', () => {
+ finishedTask.name = 'rbd/delete';
+ testDelete(defaultMsg);
+ testErrorCode(39, `${defaultMsg} contains snapshots.`);
+ });
+
+ it('tests rbd/clone messages', () => {
+ finishedTask.name = 'rbd/clone';
+ testMessages(new TaskMessageOperation('Cloning', 'clone', 'Cloned'), childMsg);
+ testErrorCode(17, `Name is already used by ${childMsg}.`);
+ testErrorCode(22, `Snapshot of ${childMsg} must be protected.`);
+ });
+
+ it('tests rbd/copy messages', () => {
+ finishedTask.name = 'rbd/copy';
+ testMessages(new TaskMessageOperation('Copying', 'copy', 'Copied'), destinationMsg);
+ testErrorCode(17, `Name is already used by ${destinationMsg}.`);
+ });
+
+ it('tests rbd/flatten messages', () => {
+ finishedTask.name = 'rbd/flatten';
+ testMessages(new TaskMessageOperation('Flattening', 'flatten', 'Flattened'), defaultMsg);
+ });
+
+ it('tests rbd/snap/create messages', () => {
+ finishedTask.name = 'rbd/snap/create';
+ testCreate(snapMsg);
+ testErrorCode(17, `Name is already used by ${snapMsg}.`);
+ });
+
+ it('tests rbd/snap/edit messages', () => {
+ finishedTask.name = 'rbd/snap/edit';
+ testUpdate(snapMsg);
+ testErrorCode(16, `Cannot unprotect ${snapMsg} because it contains child images.`);
+ });
+
+ it('tests rbd/snap/delete messages', () => {
+ finishedTask.name = 'rbd/snap/delete';
+ testDelete(snapMsg);
+ testErrorCode(16, `Cannot delete ${snapMsg} because it's protected.`);
+ });
+
+ it('tests rbd/snap/rollback messages', () => {
+ finishedTask.name = 'rbd/snap/rollback';
+ testMessages(new TaskMessageOperation('Rolling back', 'rollback', 'Rolled back'), snapMsg);
+ });
});
});
});
import { Task } from '../models/task';
import { ServicesModule } from './services.module';
+export class TaskMessageOperation {
+ running: string;
+ failure: string;
+ success: string;
+
+ constructor(running: string, failure: string, success: string) {
+ this.running = running;
+ this.failure = failure;
+ this.success = success;
+ }
+}
+
class TaskManagerMessage {
- descr: (metadata) => string;
- running: (metadata) => string;
- success: (metadata) => string;
- error: (metadata) => object;
+ operation: TaskMessageOperation;
+ involves: (object) => string;
+ errors: (metadata) => object;
+
+ failure(metadata): string {
+ return `Failed to ${this.operation.failure} ${this.involves(metadata)}`;
+ }
+
+ running(metadata): string {
+ return `${this.operation.running} ${this.involves(metadata)}`;
+ }
+
+ success(metadata): string {
+ return `${this.operation.success} ${this.involves(metadata)}`;
+ }
constructor(
- descr: (metadata) => string,
- running: (metadata) => string,
- success: (metadata) => string,
- error: (metadata) => object
+ operation: TaskMessageOperation,
+ involves: (metadata) => string,
+ errors?: (metadata) => object
) {
- this.descr = descr;
- this.running = running;
- this.success = success;
- this.error = error;
+ this.operation = operation;
+ this.involves = involves;
+ this.errors = errors || (() => ({}));
}
}
providedIn: ServicesModule
})
export class TaskManagerMessageService {
+ defaultMessage = new TaskManagerMessage(
+ new TaskMessageOperation('Executing', 'execute', 'Executed'),
+ (metadata) => {
+ return (metadata && (Components[metadata.component] || metadata.component)) || 'unknown task';
+ },
+ () => {
+ return {};
+ }
+ );
+
+ commonOperations = {
+ create: new TaskMessageOperation('Creating', 'create', 'Created'),
+ update: new TaskMessageOperation('Updating', 'update', 'Updated'),
+ delete: new TaskMessageOperation('Deleting', 'delete', 'Deleted')
+ };
+
+ rbd = {
+ default: (metadata) => `RBD '${metadata.pool_name}/${metadata.image_name}'`,
+ child: (metadata) => `RBD '${metadata.child_pool_name}/${metadata.child_image_name}'`,
+ destination: (metadata) => `RBD '${metadata.dest_pool_name}/${metadata.dest_image_name}'`,
+ snapshot: (metadata) =>
+ `RBD snapshot '${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`
+ };
+
messages = {
'rbd/create': new TaskManagerMessage(
- (metadata) => `Create RBD '${metadata.pool_name}/${metadata.image_name}'`,
- (metadata) => `Creating RBD '${metadata.pool_name}/${metadata.image_name}'`,
- (metadata) =>
- `RBD '${metadata.pool_name}/${metadata.image_name}' has been created successfully`,
- (metadata) => {
- return {
- '17': `Name '${metadata.pool_name}/${metadata.image_name}' is already in use.`
- };
- }
+ this.commonOperations.create,
+ this.rbd.default,
+ (metadata) => ({
+ '17': `Name is already used by ${this.rbd.default(metadata)}.`
+ })
),
'rbd/edit': new TaskManagerMessage(
- (metadata) => `Update RBD '${metadata.pool_name}/${metadata.image_name}'`,
- (metadata) => `Updating RBD '${metadata.pool_name}/${metadata.image_name}'`,
- (metadata) =>
- `RBD '${metadata.pool_name}/${metadata.image_name}' has been updated successfully`,
- (metadata) => {
- return {
- '17': `Name '${metadata.pool_name}/${metadata.name}' is already in use.`
- };
- }
+ this.commonOperations.update,
+ this.rbd.default,
+ (metadata) => ({
+ '17': `Name is already used by ${this.rbd.default(metadata)}.`
+ })
),
'rbd/delete': new TaskManagerMessage(
- (metadata) => `Delete RBD '${metadata.pool_name}/${metadata.image_name}'`,
- (metadata) => `Deleting RBD '${metadata.pool_name}/${metadata.image_name}'`,
- (metadata) =>
- `RBD '${metadata.pool_name}/${metadata.image_name}' has been deleted successfully`,
- (metadata) => {
- return {
- '39': `RBD image contains snapshots.`
- };
- }
+ this.commonOperations.delete,
+ this.rbd.default,
+ (metadata) => ({
+ '39': `${this.rbd.default(metadata)} contains snapshots.`
+ })
),
'rbd/clone': new TaskManagerMessage(
- (metadata) => `Clone RBD '${metadata.child_pool_name}/${metadata.child_image_name}'`,
- (metadata) => `Cloning RBD '${metadata.child_pool_name}/${metadata.child_image_name}'`,
- (metadata) =>
- `RBD '${metadata.child_pool_name}/${
- metadata.child_image_name
- }' has been cloned successfully`,
- (metadata) => {
- return {
- '17': `Name '${metadata.child_pool_name}/${
- metadata.child_image_name
- }' is already in use.`,
- '22': `Snapshot must be protected.`
- };
- }
+ new TaskMessageOperation('Cloning', 'clone', 'Cloned'),
+ this.rbd.child,
+ (metadata) => ({
+ '17': `Name is already used by ${this.rbd.child(metadata)}.`,
+ '22': `Snapshot of ${this.rbd.child(metadata)} must be protected.`
+ })
),
'rbd/copy': new TaskManagerMessage(
- (metadata) => `Copy RBD '${metadata.dest_pool_name}/${metadata.dest_image_name}'`,
- (metadata) => `Copying RBD '${metadata.dest_pool_name}/${metadata.dest_image_name}'`,
- (metadata) =>
- `RBD '${metadata.dest_pool_name}/${metadata.dest_image_name}' has been copied successfully`,
- (metadata) => {
- return {
- '17': `Name '${metadata.dest_pool_name}/${metadata.dest_image_name}' is already in use.`
- };
- }
+ new TaskMessageOperation('Copying', 'copy', 'Copied'),
+ this.rbd.destination,
+ (metadata) => ({
+ '17': `Name is already used by ${this.rbd.destination(metadata)}.`
+ })
),
'rbd/flatten': new TaskManagerMessage(
- (metadata) => `Flatten RBD '${metadata.pool_name}/${metadata.image_name}'`,
- (metadata) => `Flattening RBD '${metadata.pool_name}/${metadata.image_name}'`,
- (metadata) =>
- `RBD '${metadata.pool_name}/${metadata.image_name}' has been flattened successfully`,
- () => {
- return {};
- }
+ new TaskMessageOperation('Flattening', 'flatten', 'Flattened'),
+ this.rbd.default
),
'rbd/snap/create': new TaskManagerMessage(
- (metadata) =>
- `Create snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,
- (metadata) =>
- `Creating snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,
- (metadata) =>
- `Snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}' ` +
- `has been created successfully`,
- (metadata) => {
- return {
- '17': `Name '${metadata.snapshot_name}' is already in use.`
- };
- }
+ this.commonOperations.create,
+ this.rbd.snapshot,
+ (metadata) => ({
+ '17': `Name is already used by ${this.rbd.snapshot(metadata)}.`
+ })
),
'rbd/snap/edit': new TaskManagerMessage(
- (metadata) =>
- `Update snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,
- (metadata) =>
- `Updating snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,
- (metadata) =>
- `Snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}' ` +
- `has been updated successfully`,
- () => {
- return {
- '16': `Cannot unprotect snapshot because it contains child images.`
- };
- }
+ this.commonOperations.update,
+ this.rbd.snapshot,
+ (metadata) => ({
+ '16': `Cannot unprotect ${this.rbd.snapshot(metadata)} because it contains child images.`
+ })
),
'rbd/snap/delete': new TaskManagerMessage(
- (metadata) =>
- `Delete snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,
- (metadata) =>
- `Deleting snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,
- (metadata) =>
- `Snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}' ` +
- `has been deleted successfully`,
- () => {
- return {
- '16': `Snapshot is protected.`
- };
- }
+ this.commonOperations.delete,
+ this.rbd.snapshot,
+ (metadata) => ({
+ '16': `Cannot delete ${this.rbd.snapshot(metadata)} because it's protected.`
+ })
),
'rbd/snap/rollback': new TaskManagerMessage(
- (metadata) =>
- `Rollback snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,
- (metadata) =>
- `Rolling back snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}'`,
- (metadata) =>
- `Snapshot ` +
- `'${metadata.pool_name}/${metadata.image_name}@${metadata.snapshot_name}' ` +
- `has been rolled back successfully`,
- () => {
- return {};
- }
+ new TaskMessageOperation('Rolling back', 'rollback', 'Rolled back'),
+ this.rbd.snapshot
)
};
- defaultMessage = new TaskManagerMessage(
- (metadata) => {
- return Components[metadata.component] || metadata.component || 'Unknown Task';
- },
- (metadata) => {
- return (
- 'Executing ' + (Components[metadata.component] || metadata.component || 'unknown task')
- );
- },
- (metadata) => 'Task executed successfully',
- () => {
- return {};
- }
- );
-
constructor() {}
- getSuccessMessage(finishedTask: FinishedTask) {
- const taskManagerMessage = this.messages[finishedTask.name] || this.defaultMessage;
- return taskManagerMessage.success(finishedTask.metadata);
+ _getTaskTitle(task: Task) {
+ return this.messages[task.name] || this.defaultMessage;
+ }
+
+ getSuccessTitle(task: FinishedTask) {
+ return this._getTaskTitle(task).success(task.metadata);
}
- getErrorMessage(finishedTask: FinishedTask) {
- const taskManagerMessage = this.messages[finishedTask.name] || this.defaultMessage;
+ getErrorMessage(task: FinishedTask) {
return (
- taskManagerMessage.error(finishedTask.metadata)[finishedTask.exception.code] ||
- finishedTask.exception.detail
+ this._getTaskTitle(task).errors(task.metadata)[task.exception.code] || task.exception.detail
);
}
- getDescription(task: Task) {
- const taskManagerMessage = this.messages[task.name] || this.defaultMessage;
- return taskManagerMessage.descr(task.metadata);
+ getErrorTitle(task: Task) {
+ return this._getTaskTitle(task).failure(task.metadata);
}
- getRunningMessage(task: Task) {
- const taskManagerMessage = this.messages[task.name] || this.defaultMessage;
- return taskManagerMessage.running(task.metadata);
+ getRunningTitle(task: Task) {
+ return this._getTaskTitle(task).running(task.metadata);
}
}
_handleExecutingTasks(task: FinishedTask) {
this.notificationService.show(
NotificationType.info,
- this.taskManagerMessageService.getRunningMessage(task),
- this.taskManagerMessageService.getDescription(task)
+ this.taskManagerMessageService.getRunningTitle(task),
+ this.taskManagerMessageService.getErrorTitle(task)
);
const executingTask = new ExecutingTask(task.name, task.metadata);