mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
webui: move RPC code from IPA module to its own module
- moves RPC code from ipa.js to it's own module - part of ongoing effort where the ultimate goal is to get rid of ipa.js and IPA namespace Reviewed-By: Adam Misnyovszki <amisnyov@redhat.com>
This commit is contained in:
@@ -621,799 +621,6 @@ IPA.password_selfservice = function() {
|
||||
reset_dialog.open();
|
||||
};
|
||||
|
||||
/**
|
||||
* Call an IPA command over JSON-RPC.
|
||||
*
|
||||
* @class IPA.command
|
||||
*
|
||||
* @param {Object} spec - construct specification
|
||||
* @param {string} spec.name - command name (optional)
|
||||
* @param {string} spec.entity - command entity(name) (optional)
|
||||
* @param {string} spec.method - command method
|
||||
* @param {string[]} spec.args - list of arguments, e.g. ['username']
|
||||
* @param {Object} spec.options - dict of options, e.g. {givenname: 'Petr'}
|
||||
* @param {Function} spec.on_success - callback function if command succeeds
|
||||
* @param {Function} spec.on_error - callback function if command fails
|
||||
*
|
||||
*/
|
||||
IPA.command = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
var that = IPA.object();
|
||||
|
||||
/** @property {string} name Name */
|
||||
that.name = spec.name;
|
||||
|
||||
/** @property {entity.entity} entity Entity */
|
||||
that.entity = spec.entity;
|
||||
|
||||
/** @property {string} method Method */
|
||||
that.method = spec.method;
|
||||
|
||||
/** @property {string[]} args Command Arguments */
|
||||
that.args = $.merge([], spec.args || []);
|
||||
|
||||
/** @property {Object} options Option map */
|
||||
that.options = $.extend({}, spec.options || {});
|
||||
|
||||
/**
|
||||
* Success handler
|
||||
* @property {Function}
|
||||
* @param {Object} data
|
||||
* @param {string} text_status
|
||||
* @param {XMLHttpRequest} xhr
|
||||
*/
|
||||
that.on_success = spec.on_success;
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
* @property {Function}
|
||||
* @param {XMLHttpRequest} xhr
|
||||
* @param {string} text_status
|
||||
* @param {{name:string,message:string}} error_thrown
|
||||
*/
|
||||
that.on_error = spec.on_error;
|
||||
|
||||
/**
|
||||
* Allow retrying of execution if previous ended as error
|
||||
*
|
||||
* Manifested by error dialog. Set it to `false` for custom error dialogs or
|
||||
* error handling without any dialog.
|
||||
* @property {Boolean} retry=true
|
||||
*/
|
||||
that.retry = typeof spec.retry == 'undefined' ? true : spec.retry;
|
||||
|
||||
/** @property {string} error_message Default error message */
|
||||
that.error_message = text.get(spec.error_message || '@i18n:dialogs.batch_error_message', 'Some operations failed.');
|
||||
|
||||
/** @property {ordered_map.<number,string>} error_messages Error messages map */
|
||||
that.error_messages = $.ordered_map({
|
||||
911: 'Missing HTTP referer. <br/> You have to configure your browser to send HTTP referer header.'
|
||||
});
|
||||
|
||||
/**
|
||||
* Get command name
|
||||
*
|
||||
* - it's `entity.name + '_' + method`
|
||||
* - or `method`
|
||||
* @return {string}
|
||||
*/
|
||||
that.get_command = function() {
|
||||
return (that.entity ? that.entity+'_' : '') + that.method;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add argument
|
||||
* @param {string} arg
|
||||
*/
|
||||
that.add_arg = function(arg) {
|
||||
that.args.push(arg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add arguments
|
||||
* @param {string[]} args
|
||||
*/
|
||||
that.add_args = function(args) {
|
||||
$.merge(that.args, args);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set option
|
||||
* @param {string} name
|
||||
* @param {Mixed} value
|
||||
*/
|
||||
that.set_option = function(name, value) {
|
||||
that.options[name] = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends options map with another options map
|
||||
*
|
||||
* @param {{opt1:Mixed, opt2:Mixed}} options
|
||||
*/
|
||||
that.set_options = function(options) {
|
||||
$.extend(that.options, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add value to an option
|
||||
*
|
||||
* - creates a new option if it does not exist yet
|
||||
* - for option overriding use `set_option` method
|
||||
* @param {string} name
|
||||
* @param {Mixed} value
|
||||
*/
|
||||
that.add_option = function(name, value) {
|
||||
var values = that.options[name];
|
||||
if (!values) {
|
||||
values = [];
|
||||
that.options[name] = values;
|
||||
}
|
||||
values.push(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get option value
|
||||
* @return {Mixed}
|
||||
*/
|
||||
that.get_option = function(name) {
|
||||
return that.options[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove option from option map
|
||||
*/
|
||||
that.remove_option = function(name) {
|
||||
delete that.options[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute the command.
|
||||
*
|
||||
* Set `on_success` and/or `on_error` handlers to be informed about result.
|
||||
*/
|
||||
that.execute = function() {
|
||||
|
||||
function dialog_open(xhr, text_status, error_thrown) {
|
||||
|
||||
var ajax = this;
|
||||
|
||||
var dialog = IPA.error_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: error_thrown,
|
||||
command: that
|
||||
});
|
||||
|
||||
dialog.on_cancel = function() {
|
||||
dialog.close();
|
||||
if (that.on_error) {
|
||||
that.on_error.call(ajax, xhr, text_status, error_thrown);
|
||||
}
|
||||
};
|
||||
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
function auth_dialog_open(xhr, text_status, error_thrown) {
|
||||
|
||||
var ajax = this;
|
||||
|
||||
var dialog = IPA.unauthorized_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: error_thrown,
|
||||
close_on_escape: false,
|
||||
command: that
|
||||
});
|
||||
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
/*
|
||||
* Special error handler used the first time this command is
|
||||
* submitted. It checks to see if the session credentials need
|
||||
* to be acquired and if so sends a request to a special url
|
||||
* to establish the sesion credentials. If acquiring the
|
||||
* session credentials is successful it simply resubmits the
|
||||
* exact same command after setting the error handler back to
|
||||
* the normal error handler. If aquiring the session
|
||||
* credentials fails the normal error handler is invoked to
|
||||
* process the error returned from the attempt to aquire the
|
||||
* session credentials.
|
||||
*/
|
||||
function error_handler_login(xhr, text_status, error_thrown) {
|
||||
if (xhr.status === 401) {
|
||||
var login_status = IPA.get_credentials();
|
||||
|
||||
if (login_status === 200) {
|
||||
that.request.error = error_handler;
|
||||
$.ajax(that.request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// error_handler() calls IPA.hide_activity_icon()
|
||||
error_handler.call(this, xhr, text_status, error_thrown);
|
||||
}
|
||||
|
||||
/*
|
||||
* Normal error handler, handles all errors.
|
||||
* error_handler_login() is initially used to trap the
|
||||
* special case need to aquire session credentials, this is
|
||||
* not a true error, rather it's an indication an extra step
|
||||
* needs to be taken before normal processing can continue.
|
||||
*/
|
||||
function error_handler(xhr, text_status, error_thrown) {
|
||||
|
||||
IPA.hide_activity_icon();
|
||||
|
||||
if (xhr.status === 401) {
|
||||
auth_dialog_open(xhr, text_status, error_thrown);
|
||||
return;
|
||||
} else if (!error_thrown) {
|
||||
error_thrown = {
|
||||
name: xhr.responseText || text.get('@i18n:errors.unknown_error', 'Unknown Error'),
|
||||
message: xhr.statusText || text.get('@i18n:errors.unknown_error', 'Unknown Error')
|
||||
};
|
||||
|
||||
} else if (typeof error_thrown == 'string') {
|
||||
error_thrown = {
|
||||
name: error_thrown,
|
||||
message: error_thrown
|
||||
};
|
||||
}
|
||||
|
||||
// custom messages for set of codes
|
||||
var error_msg = that.error_messages.get(error_thrown.code);
|
||||
if (error_msg) {
|
||||
error_msg = error_msg.replace('${message}', error_thrown.message);
|
||||
error_thrown.message = error_msg;
|
||||
}
|
||||
|
||||
// global specical cases error handlers section
|
||||
|
||||
// With trusts, user from trusted domain can use his ticket but he
|
||||
// doesn't have rights for LDAP modify. It will throw internal errror.
|
||||
// We should offer form base login.
|
||||
if (xhr.status === 500 && IPA.ui.logged_kerberos && !IPA.ui.initialized) {
|
||||
auth_dialog_open(xhr, text_status, error_thrown);
|
||||
return;
|
||||
}
|
||||
|
||||
if (that.retry) {
|
||||
dialog_open.call(this, xhr, text_status, error_thrown);
|
||||
|
||||
} else if (that.on_error) {
|
||||
//custom error handling, maintaining AJAX call's context
|
||||
that.on_error.call(this, xhr, text_status, error_thrown);
|
||||
}
|
||||
}
|
||||
|
||||
function success_handler(data, text_status, xhr) {
|
||||
|
||||
if (!data) {
|
||||
// error_handler() calls IPA.hide_activity_icon()
|
||||
error_handler.call(this, xhr, text_status, /* error_thrown */ {
|
||||
name: text.get('@i18n:errors.http_error', 'HTTP Error')+' '+xhr.status,
|
||||
url: this.url,
|
||||
message: data ? xhr.statusText : text.get('@i18n:errors.no_response', 'No response')
|
||||
});
|
||||
|
||||
} else if (IPA.version && data.version && IPA.version !== data.version) {
|
||||
window.location.reload();
|
||||
|
||||
} else if (IPA.principal && data.principal && IPA.principal !== data.principal) {
|
||||
window.location.reload();
|
||||
|
||||
} else if (data.error) {
|
||||
// error_handler() calls IPA.hide_activity_icon()
|
||||
error_handler.call(this, xhr, text_status, /* error_thrown */ {
|
||||
name: text.get('@i18n:errors.ipa_error', 'IPA Error') + ' ' +
|
||||
data.error.code + ': ' + data.error.name,
|
||||
code: data.error.code,
|
||||
message: data.error.message,
|
||||
data: data
|
||||
});
|
||||
|
||||
} else {
|
||||
IPA.hide_activity_icon();
|
||||
|
||||
var ajax = this;
|
||||
var failed = that.get_failed(that, data.result, text_status, xhr);
|
||||
if (!failed.is_empty()) {
|
||||
var dialog = IPA.error_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: {
|
||||
name: text.get('@i18n:dialogs.batch_error_title', 'Operations Error'),
|
||||
message: that.error_message
|
||||
},
|
||||
command: that,
|
||||
errors: failed.errors,
|
||||
visible_buttons: ['ok']
|
||||
});
|
||||
|
||||
dialog.on_ok = function() {
|
||||
dialog.close();
|
||||
if (that.on_success) that.on_success.call(ajax, data, text_status, xhr);
|
||||
};
|
||||
|
||||
dialog.open();
|
||||
|
||||
} else {
|
||||
//custom success handling, maintaining AJAX call's context
|
||||
if (that.on_success) that.on_success.call(this, data, text_status, xhr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
that.data = {
|
||||
method: that.get_command(),
|
||||
params: [that.args, that.options]
|
||||
};
|
||||
|
||||
that.request = {
|
||||
url: IPA.json_url || IPA.json_path + '/' + (that.name || that.data.method) + '.json',
|
||||
data: JSON.stringify(that.data),
|
||||
success: success_handler,
|
||||
error: error_handler_login
|
||||
};
|
||||
|
||||
IPA.display_activity_icon();
|
||||
$.ajax(that.request);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse successful command result and get all errors.
|
||||
* @protected
|
||||
* @param {IPA.command} command
|
||||
* @param {Object} result
|
||||
* @param {string} text_status
|
||||
* @param {XMLHttpRequest} xhr
|
||||
* @return {IPA.error_list}
|
||||
*/
|
||||
that.get_failed = function(command, result, text_status, xhr) {
|
||||
var errors = IPA.error_list();
|
||||
if(result && result.failed) {
|
||||
for(var association in result.failed) {
|
||||
for(var member_name in result.failed[association]) {
|
||||
var member = result.failed[association][member_name];
|
||||
for(var i = 0; i < member.length; i++) {
|
||||
if(member[i].length > 1) {
|
||||
var name = text.get('@i18n:errors.ipa_error', 'IPA Error');
|
||||
var message = member[i][1];
|
||||
if(member[i][0])
|
||||
message = member[i][0] + ': ' + message;
|
||||
errors.add(command, name, message, text_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if command accepts option
|
||||
* @param {string} option_name
|
||||
* @return {Boolean}
|
||||
*/
|
||||
that.check_option = function(option_name) {
|
||||
|
||||
var metadata = IPA.get_command_option(that.get_command(), option_name);
|
||||
return metadata !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes command into JSON-RPC command object
|
||||
* @return {Object}
|
||||
*/
|
||||
that.to_json = function() {
|
||||
var json = {};
|
||||
|
||||
json.method = that.get_command();
|
||||
|
||||
json.params = [];
|
||||
json.params[0] = that.args || [];
|
||||
json.params[1] = that.options || {};
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes command into CLI command string
|
||||
* @return {string}
|
||||
*/
|
||||
that.to_string = function() {
|
||||
var string = that.get_command().replace(/_/g, '-');
|
||||
|
||||
for (var i=0; i<that.args.length; i++) {
|
||||
string += ' '+that.args[i];
|
||||
}
|
||||
|
||||
for (var name in that.options) {
|
||||
string += ' --'+name+'=\''+that.options[name]+'\'';
|
||||
}
|
||||
|
||||
return string;
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call multiple IPA commands in a batch over JSON-RPC.
|
||||
*
|
||||
* @class IPA.batch_command
|
||||
* @extends IPA.command
|
||||
*
|
||||
* @param {Object} spec
|
||||
* @param {Array.<IPA.command>} spec.commands - IPA commands to be executed
|
||||
* @param {Function} spec.on_success - callback function if command succeeds
|
||||
* @param {Function} spec.on_error - callback function if command fails
|
||||
*/
|
||||
IPA.batch_command = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
spec.method = 'batch';
|
||||
|
||||
var that = IPA.command(spec);
|
||||
|
||||
/** @property {IPA.command[]} commands Commands */
|
||||
that.commands = [];
|
||||
/** @property {IPA.error_list} errors Errors */
|
||||
that.errors = IPA.error_list();
|
||||
|
||||
/**
|
||||
* Show error if some command fail
|
||||
* @property {Boolean} show_error=true
|
||||
*/
|
||||
that.show_error = typeof spec.show_error == 'undefined' ?
|
||||
true : spec.show_error;
|
||||
|
||||
/**
|
||||
* Add command
|
||||
* @param {IPA.command} command
|
||||
*/
|
||||
that.add_command = function(command) {
|
||||
that.commands.push(command);
|
||||
that.add_arg(command.to_json());
|
||||
};
|
||||
|
||||
/**
|
||||
* Add commands
|
||||
* @param {IPA.command[]} commands
|
||||
*/
|
||||
that.add_commands = function(commands) {
|
||||
for (var i=0; i<commands.length; i++) {
|
||||
that.add_command(commands[i]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
that.execute = function() {
|
||||
that.errors.clear();
|
||||
|
||||
var command = IPA.command({
|
||||
name: that.name,
|
||||
entity: that.entity,
|
||||
method: that.method,
|
||||
args: that.args,
|
||||
options: that.options,
|
||||
retry: that.retry
|
||||
});
|
||||
|
||||
command.on_success = that.batch_command_on_success;
|
||||
command.on_error = that.batch_command_on_error;
|
||||
|
||||
command.execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal XHR success handler
|
||||
*
|
||||
* Parses data and looks for errors. `on_success` or `on_error` is then
|
||||
* called.
|
||||
* @protected
|
||||
* @param {Object} data
|
||||
* @param {string} text_status
|
||||
* @param {XMLHttpRequest} xhr
|
||||
*/
|
||||
that.batch_command_on_success = function(data, text_status, xhr) {
|
||||
|
||||
for (var i=0; i<that.commands.length; i++) {
|
||||
var command = that.commands[i];
|
||||
var result = data.result.results[i];
|
||||
|
||||
var name = '';
|
||||
var message = '';
|
||||
|
||||
if (!result) {
|
||||
name = text.get('@i18n:errors.internal_error', 'Internal Error')+' '+xhr.status;
|
||||
message = result ? xhr.statusText : text.get('@i18n:errors.internal_error', 'Internal Error');
|
||||
|
||||
that.errors.add(command, name, message, text_status);
|
||||
|
||||
if (command.on_error) command.on_error.call(
|
||||
this,
|
||||
xhr,
|
||||
text_status,
|
||||
{
|
||||
name: name,
|
||||
message: message
|
||||
}
|
||||
);
|
||||
|
||||
} else if (result.error) {
|
||||
var code = result.error.code || result.error_code;
|
||||
name = text.get('@i18n:errors.ipa_error', 'IPA Error')+(code ? ' '+code : '');
|
||||
message = result.error.message || result.error;
|
||||
|
||||
if (command.retry) that.errors.add(command, name, message, text_status);
|
||||
|
||||
if (command.on_error) command.on_error.call(
|
||||
this,
|
||||
xhr,
|
||||
text_status,
|
||||
{
|
||||
name: name,
|
||||
code: code,
|
||||
message: message,
|
||||
data: result
|
||||
}
|
||||
);
|
||||
|
||||
} else {
|
||||
var failed = that.get_failed(command, result, text_status, xhr);
|
||||
that.errors.add_range(failed);
|
||||
|
||||
if (command.on_success) command.on_success.call(this, result, text_status, xhr);
|
||||
}
|
||||
}
|
||||
|
||||
//check for partial errors and show error dialog
|
||||
if (that.show_error && that.errors.errors.length > 0) {
|
||||
var ajax = this;
|
||||
var dialog = IPA.error_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: {
|
||||
name: text.get('@i18n:dialogs.batch_error_title', 'Operations Error'),
|
||||
message: that.error_message
|
||||
},
|
||||
command: that,
|
||||
errors: that.errors.errors,
|
||||
visible_buttons: [ 'ok' ]
|
||||
});
|
||||
|
||||
dialog.on_ok = function() {
|
||||
dialog.close();
|
||||
if (that.on_success) that.on_success.call(ajax, data, text_status, xhr);
|
||||
};
|
||||
|
||||
dialog.open();
|
||||
|
||||
} else {
|
||||
if (that.on_success) that.on_success.call(this, data, text_status, xhr);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal XHR error handler
|
||||
* @protected
|
||||
* @param {XMLHttpRequest} xhr
|
||||
* @param {string} text_status
|
||||
* @param {{name:string,message:string}} error_thrown
|
||||
*/
|
||||
that.batch_command_on_error = function(xhr, text_status, error_thrown) {
|
||||
// TODO: undefined behavior
|
||||
if (that.on_error) {
|
||||
that.on_error.call(this, xhr, text_status, error_thrown);
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call multiple IPA commands over JSON-RPC separately and wait for every
|
||||
* command's response.
|
||||
*
|
||||
* - concurrent command fails if any command fails
|
||||
* - result is reported when each command finishes
|
||||
*
|
||||
* @class IPA.concurrent_command
|
||||
*
|
||||
* @param {Object} spec - construct specification
|
||||
* @param {Array.<IPA.command>} spec.commands - IPA commands to execute
|
||||
* @param {Function} spec.on_success - callback function if each command succeed
|
||||
* @param {Function} spec.on_error - callback function one command fails
|
||||
*
|
||||
*/
|
||||
IPA.concurrent_command = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
var that = IPA.object();
|
||||
|
||||
/** @property {IPA.command[]} commands Commands */
|
||||
that.commands = [];
|
||||
|
||||
/**
|
||||
* Success handler
|
||||
* @property {Function}
|
||||
*/
|
||||
that.on_success = spec.on_success;
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
* @property {Function}
|
||||
*/
|
||||
that.on_error = spec.on_error;
|
||||
|
||||
/**
|
||||
* Add commands
|
||||
* @param {IPA.command[]} commands
|
||||
*/
|
||||
that.add_commands = function(commands) {
|
||||
|
||||
if(commands && commands.length) {
|
||||
for(var i=0; i < commands.length; i++) {
|
||||
that.commands.push({
|
||||
command: commands[i]
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute the commands one by one.
|
||||
*/
|
||||
that.execute = function() {
|
||||
|
||||
var command_info, command, i;
|
||||
|
||||
//prepare for execute
|
||||
for(i=0; i < that.commands.length; i++) {
|
||||
command_info = that.commands[i];
|
||||
command = command_info.command;
|
||||
if(!command) {
|
||||
var dialog = IPA.message_dialog({
|
||||
name: 'internal_error',
|
||||
title: text.get('@i18n:errors.error', 'Error'),
|
||||
message: text.get('@i18n:errors.internal_error', 'Internal error.')
|
||||
});
|
||||
break;
|
||||
}
|
||||
command_info.completed = false;
|
||||
command_info.success = false;
|
||||
command_info.on_success = command_info.on_success || command.on_success;
|
||||
command_info.on_error = command_info.on_error || command.on_error;
|
||||
command.on_success = function(command_info) {
|
||||
return function(data, text_status, xhr) {
|
||||
that.success_handler.call(this, command_info, data, text_status, xhr);
|
||||
};
|
||||
}(command_info);
|
||||
command.on_error = function(command_info) {
|
||||
return function(xhr, text_status, error_thrown) {
|
||||
that.error_handler.call(this, command_info, xhr, text_status, error_thrown);
|
||||
};
|
||||
}(command_info);
|
||||
}
|
||||
|
||||
//execute
|
||||
for(i=0; i < that.commands.length; i++) {
|
||||
command = that.commands[i].command;
|
||||
command.execute();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal error handler
|
||||
* @protected
|
||||
*/
|
||||
that.error_handler = function(command_info, xhr, text_status, error_thrown) {
|
||||
|
||||
command_info.completed = true;
|
||||
command_info.success = false;
|
||||
command_info.xhr = xhr;
|
||||
command_info.text_status = text_status;
|
||||
command_info.error_thrown = error_thrown;
|
||||
command_info.context = this;
|
||||
that.command_completed();
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal success handler
|
||||
* @protected
|
||||
*/
|
||||
that.success_handler = function(command_info, data, text_status, xhr) {
|
||||
|
||||
command_info.completed = true;
|
||||
command_info.success = true;
|
||||
command_info.data = data;
|
||||
command_info.text_status = text_status;
|
||||
command_info.xhr = xhr;
|
||||
command_info.context = this;
|
||||
that.command_completed();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if all commands finished.
|
||||
* If so, report it.
|
||||
* @protected
|
||||
*/
|
||||
that.command_completed = function() {
|
||||
|
||||
var all_completed = true;
|
||||
var all_success = true;
|
||||
|
||||
for(var i=0; i < that.commands.length; i++) {
|
||||
var command_info = that.commands[i];
|
||||
all_completed = all_completed && command_info.completed;
|
||||
all_success = all_success && command_info.success;
|
||||
}
|
||||
|
||||
if(all_completed) {
|
||||
if(all_success) {
|
||||
that.on_success_all();
|
||||
} else {
|
||||
that.on_error_all();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Call each command's success handler and `on_success`.
|
||||
* @protected
|
||||
*/
|
||||
that.on_success_all = function() {
|
||||
|
||||
for(var i=0; i < that.commands.length; i++) {
|
||||
var command_info = that.commands[i];
|
||||
if(command_info.on_success) {
|
||||
command_info.on_success.call(
|
||||
command_info.context,
|
||||
command_info.data,
|
||||
command_info.text_status,
|
||||
command_info.xhr);
|
||||
}
|
||||
}
|
||||
|
||||
if(that.on_success) {
|
||||
that.on_success();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Call each command's error handler and `on_success`.
|
||||
* @protected
|
||||
*/
|
||||
that.on_error_all = function() {
|
||||
|
||||
if(that.on_error) {
|
||||
that.on_error();
|
||||
|
||||
} else {
|
||||
var dialog = IPA.message_dialog({
|
||||
name: 'operation_error',
|
||||
title: text.get('@i18n:dialogs.batch_error_title', 'Operations Error'),
|
||||
message: text.get('@i18n:dialogs.batch_error_message', 'Some operations failed.')
|
||||
});
|
||||
|
||||
dialog.open();
|
||||
}
|
||||
};
|
||||
|
||||
that.add_commands(spec.commands);
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build object with {@link builder}.
|
||||
* @member IPA
|
||||
@@ -1794,97 +1001,6 @@ IPA.error_dialog = function(spec) {
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Error list
|
||||
*
|
||||
* Collection for RPC command errors.
|
||||
*
|
||||
* @class IPA.error_list
|
||||
* @private
|
||||
*/
|
||||
IPA.error_list = function() {
|
||||
var that = IPA.object();
|
||||
|
||||
/** Clear errors */
|
||||
that.clear = function() {
|
||||
that.errors = [];
|
||||
};
|
||||
|
||||
/** Add error */
|
||||
that.add = function(command, name, message, status) {
|
||||
that.errors.push({
|
||||
command: command,
|
||||
name: name,
|
||||
message: message,
|
||||
status: status
|
||||
});
|
||||
};
|
||||
|
||||
/** Add errors */
|
||||
that.add_range = function(error_list) {
|
||||
that.errors = that.errors.concat(error_list.errors);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there are no errors
|
||||
* @return {Boolean}
|
||||
*/
|
||||
that.is_empty = function () {
|
||||
return that.errors.length === 0;
|
||||
};
|
||||
|
||||
that.clear();
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Error handler for IPA.command which handles error #4304 as success.
|
||||
*
|
||||
* 4304 is raised when part of an operation succeeds and the part that failed
|
||||
* isn't critical.
|
||||
* @member IPA
|
||||
* @param {IPA.entity_adder_dialog} adder_dialog
|
||||
*/
|
||||
IPA.create_4304_error_handler = function(adder_dialog) {
|
||||
|
||||
var set_pkey = function(result) {
|
||||
|
||||
var pkey_name = adder_dialog.entity.metadata.primary_key;
|
||||
var args = adder_dialog.command.args;
|
||||
var pkey = args[args.length-1];
|
||||
result[pkey_name] = pkey;
|
||||
};
|
||||
|
||||
return function (xhr, text_status, error_thrown) {
|
||||
|
||||
var ajax = this;
|
||||
var command = adder_dialog.command;
|
||||
var data = error_thrown.data;
|
||||
var dialog = null;
|
||||
|
||||
if (data && data.error && data.error.code === 4304) {
|
||||
dialog = IPA.message_dialog({
|
||||
name: 'error_4304_info',
|
||||
message: data.error.message,
|
||||
title: adder_dialog.title,
|
||||
on_ok: function() {
|
||||
data.result = { result: {} };
|
||||
set_pkey(data.result.result);
|
||||
command.on_success.call(ajax, data, text_status, xhr);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
dialog = IPA.error_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: error_thrown,
|
||||
command: command
|
||||
});
|
||||
}
|
||||
|
||||
dialog.open();
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Unauthorized dialog
|
||||
|
||||
930
install/ui/src/freeipa/rpc.js
Normal file
930
install/ui/src/freeipa/rpc.js
Normal file
@@ -0,0 +1,930 @@
|
||||
/* Authors:
|
||||
* Pavel Zuna <pzuna@redhat.com>
|
||||
* Adam Young <ayoung@redhat.com>
|
||||
* Endi Dewata <edewata@redhat.com>
|
||||
* John Dennis <jdennis@redhat.com>
|
||||
* Petr Vobornik <pvoborni@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2014 Red Hat
|
||||
* see file 'COPYING' for use and warranty information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
define([
|
||||
'dojo/_base/lang',
|
||||
'./ipa',
|
||||
'./text',
|
||||
'exports'
|
||||
],
|
||||
function(lang, IPA, text, exports) {
|
||||
|
||||
var rpc = {};
|
||||
|
||||
/**
|
||||
* Call an IPA command over JSON-RPC.
|
||||
*
|
||||
* @class rpc.command
|
||||
*
|
||||
* @param {Object} spec - construct specification
|
||||
* @param {string} spec.name - command name (optional)
|
||||
* @param {string} spec.entity - command entity(name) (optional)
|
||||
* @param {string} spec.method - command method
|
||||
* @param {string[]} spec.args - list of arguments, e.g. ['username']
|
||||
* @param {Object} spec.options - dict of options, e.g. {givenname: 'Petr'}
|
||||
* @param {Function} spec.on_success - callback function if command succeeds
|
||||
* @param {Function} spec.on_error - callback function if command fails
|
||||
*
|
||||
*/
|
||||
rpc.command = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
var that = IPA.object();
|
||||
|
||||
/** @property {string} name Name */
|
||||
that.name = spec.name;
|
||||
|
||||
/** @property {entity.entity} entity Entity */
|
||||
that.entity = spec.entity;
|
||||
|
||||
/** @property {string} method Method */
|
||||
that.method = spec.method;
|
||||
|
||||
/** @property {string[]} args Command Arguments */
|
||||
that.args = $.merge([], spec.args || []);
|
||||
|
||||
/** @property {Object} options Option map */
|
||||
that.options = $.extend({}, spec.options || {});
|
||||
|
||||
/**
|
||||
* Success handler
|
||||
* @property {Function}
|
||||
* @param {Object} data
|
||||
* @param {string} text_status
|
||||
* @param {XMLHttpRequest} xhr
|
||||
*/
|
||||
that.on_success = spec.on_success;
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
* @property {Function}
|
||||
* @param {XMLHttpRequest} xhr
|
||||
* @param {string} text_status
|
||||
* @param {{name:string,message:string}} error_thrown
|
||||
*/
|
||||
that.on_error = spec.on_error;
|
||||
|
||||
/**
|
||||
* Allow retrying of execution if previous ended as error
|
||||
*
|
||||
* Manifested by error dialog. Set it to `false` for custom error dialogs or
|
||||
* error handling without any dialog.
|
||||
* @property {Boolean} retry=true
|
||||
*/
|
||||
that.retry = typeof spec.retry == 'undefined' ? true : spec.retry;
|
||||
|
||||
/** @property {string} error_message Default error message */
|
||||
that.error_message = text.get(spec.error_message || '@i18n:dialogs.batch_error_message', 'Some operations failed.');
|
||||
|
||||
/** @property {ordered_map.<number,string>} error_messages Error messages map */
|
||||
that.error_messages = $.ordered_map({
|
||||
911: 'Missing HTTP referer. <br/> You have to configure your browser to send HTTP referer header.'
|
||||
});
|
||||
|
||||
/**
|
||||
* Get command name
|
||||
*
|
||||
* - it's `entity.name + '_' + method`
|
||||
* - or `method`
|
||||
* @return {string}
|
||||
*/
|
||||
that.get_command = function() {
|
||||
return (that.entity ? that.entity+'_' : '') + that.method;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add argument
|
||||
* @param {string} arg
|
||||
*/
|
||||
that.add_arg = function(arg) {
|
||||
that.args.push(arg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add arguments
|
||||
* @param {string[]} args
|
||||
*/
|
||||
that.add_args = function(args) {
|
||||
$.merge(that.args, args);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set option
|
||||
* @param {string} name
|
||||
* @param {Mixed} value
|
||||
*/
|
||||
that.set_option = function(name, value) {
|
||||
that.options[name] = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends options map with another options map
|
||||
*
|
||||
* @param {{opt1:Mixed, opt2:Mixed}} options
|
||||
*/
|
||||
that.set_options = function(options) {
|
||||
$.extend(that.options, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add value to an option
|
||||
*
|
||||
* - creates a new option if it does not exist yet
|
||||
* - for option overriding use `set_option` method
|
||||
* @param {string} name
|
||||
* @param {Mixed} value
|
||||
*/
|
||||
that.add_option = function(name, value) {
|
||||
var values = that.options[name];
|
||||
if (!values) {
|
||||
values = [];
|
||||
that.options[name] = values;
|
||||
}
|
||||
values.push(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get option value
|
||||
* @return {Mixed}
|
||||
*/
|
||||
that.get_option = function(name) {
|
||||
return that.options[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove option from option map
|
||||
*/
|
||||
that.remove_option = function(name) {
|
||||
delete that.options[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute the command.
|
||||
*
|
||||
* Set `on_success` and/or `on_error` handlers to be informed about result.
|
||||
*/
|
||||
that.execute = function() {
|
||||
|
||||
function dialog_open(xhr, text_status, error_thrown) {
|
||||
|
||||
var ajax = this;
|
||||
|
||||
var dialog = IPA.error_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: error_thrown,
|
||||
command: that
|
||||
});
|
||||
|
||||
dialog.on_cancel = function() {
|
||||
dialog.close();
|
||||
if (that.on_error) {
|
||||
that.on_error.call(ajax, xhr, text_status, error_thrown);
|
||||
}
|
||||
};
|
||||
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
function auth_dialog_open(xhr, text_status, error_thrown) {
|
||||
|
||||
var ajax = this;
|
||||
|
||||
var dialog = IPA.unauthorized_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: error_thrown,
|
||||
close_on_escape: false,
|
||||
command: that
|
||||
});
|
||||
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
/*
|
||||
* Special error handler used the first time this command is
|
||||
* submitted. It checks to see if the session credentials need
|
||||
* to be acquired and if so sends a request to a special url
|
||||
* to establish the sesion credentials. If acquiring the
|
||||
* session credentials is successful it simply resubmits the
|
||||
* exact same command after setting the error handler back to
|
||||
* the normal error handler. If aquiring the session
|
||||
* credentials fails the normal error handler is invoked to
|
||||
* process the error returned from the attempt to aquire the
|
||||
* session credentials.
|
||||
*/
|
||||
function error_handler_login(xhr, text_status, error_thrown) {
|
||||
if (xhr.status === 401) {
|
||||
var login_status = IPA.get_credentials();
|
||||
|
||||
if (login_status === 200) {
|
||||
that.request.error = error_handler;
|
||||
$.ajax(that.request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// error_handler() calls IPA.hide_activity_icon()
|
||||
error_handler.call(this, xhr, text_status, error_thrown);
|
||||
}
|
||||
|
||||
/*
|
||||
* Normal error handler, handles all errors.
|
||||
* error_handler_login() is initially used to trap the
|
||||
* special case need to aquire session credentials, this is
|
||||
* not a true error, rather it's an indication an extra step
|
||||
* needs to be taken before normal processing can continue.
|
||||
*/
|
||||
function error_handler(xhr, text_status, error_thrown) {
|
||||
|
||||
IPA.hide_activity_icon();
|
||||
|
||||
if (xhr.status === 401) {
|
||||
auth_dialog_open(xhr, text_status, error_thrown);
|
||||
return;
|
||||
} else if (!error_thrown) {
|
||||
error_thrown = {
|
||||
name: xhr.responseText || text.get('@i18n:errors.unknown_error', 'Unknown Error'),
|
||||
message: xhr.statusText || text.get('@i18n:errors.unknown_error', 'Unknown Error')
|
||||
};
|
||||
|
||||
} else if (typeof error_thrown == 'string') {
|
||||
error_thrown = {
|
||||
name: error_thrown,
|
||||
message: error_thrown
|
||||
};
|
||||
}
|
||||
|
||||
// custom messages for set of codes
|
||||
var error_msg = that.error_messages.get(error_thrown.code);
|
||||
if (error_msg) {
|
||||
error_msg = error_msg.replace('${message}', error_thrown.message);
|
||||
error_thrown.message = error_msg;
|
||||
}
|
||||
|
||||
// global specical cases error handlers section
|
||||
|
||||
// With trusts, user from trusted domain can use his ticket but he
|
||||
// doesn't have rights for LDAP modify. It will throw internal errror.
|
||||
// We should offer form base login.
|
||||
if (xhr.status === 500 && IPA.ui.logged_kerberos && !IPA.ui.initialized) {
|
||||
auth_dialog_open(xhr, text_status, error_thrown);
|
||||
return;
|
||||
}
|
||||
|
||||
if (that.retry) {
|
||||
dialog_open.call(this, xhr, text_status, error_thrown);
|
||||
|
||||
} else if (that.on_error) {
|
||||
//custom error handling, maintaining AJAX call's context
|
||||
that.on_error.call(this, xhr, text_status, error_thrown);
|
||||
}
|
||||
}
|
||||
|
||||
function success_handler(data, text_status, xhr) {
|
||||
|
||||
if (!data) {
|
||||
// error_handler() calls IPA.hide_activity_icon()
|
||||
error_handler.call(this, xhr, text_status, /* error_thrown */ {
|
||||
name: text.get('@i18n:errors.http_error', 'HTTP Error')+' '+xhr.status,
|
||||
url: this.url,
|
||||
message: data ? xhr.statusText : text.get('@i18n:errors.no_response', 'No response')
|
||||
});
|
||||
|
||||
} else if (IPA.version && data.version && IPA.version !== data.version) {
|
||||
window.location.reload();
|
||||
|
||||
} else if (IPA.principal && data.principal && IPA.principal !== data.principal) {
|
||||
window.location.reload();
|
||||
|
||||
} else if (data.error) {
|
||||
// error_handler() calls IPA.hide_activity_icon()
|
||||
error_handler.call(this, xhr, text_status, /* error_thrown */ {
|
||||
name: text.get('@i18n:errors.ipa_error', 'IPA Error') + ' ' +
|
||||
data.error.code + ': ' + data.error.name,
|
||||
code: data.error.code,
|
||||
message: data.error.message,
|
||||
data: data
|
||||
});
|
||||
|
||||
} else {
|
||||
IPA.hide_activity_icon();
|
||||
|
||||
var ajax = this;
|
||||
var failed = that.get_failed(that, data.result, text_status, xhr);
|
||||
if (!failed.is_empty()) {
|
||||
var dialog = IPA.error_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: {
|
||||
name: text.get('@i18n:dialogs.batch_error_title', 'Operations Error'),
|
||||
message: that.error_message
|
||||
},
|
||||
command: that,
|
||||
errors: failed.errors,
|
||||
visible_buttons: ['ok']
|
||||
});
|
||||
|
||||
dialog.on_ok = function() {
|
||||
dialog.close();
|
||||
if (that.on_success) that.on_success.call(ajax, data, text_status, xhr);
|
||||
};
|
||||
|
||||
dialog.open();
|
||||
|
||||
} else {
|
||||
//custom success handling, maintaining AJAX call's context
|
||||
if (that.on_success) that.on_success.call(this, data, text_status, xhr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
that.data = {
|
||||
method: that.get_command(),
|
||||
params: [that.args, that.options]
|
||||
};
|
||||
|
||||
that.request = {
|
||||
url: IPA.json_url || IPA.json_path + '/' + (that.name || that.data.method) + '.json',
|
||||
data: JSON.stringify(that.data),
|
||||
success: success_handler,
|
||||
error: error_handler_login
|
||||
};
|
||||
|
||||
IPA.display_activity_icon();
|
||||
$.ajax(that.request);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse successful command result and get all errors.
|
||||
* @protected
|
||||
* @param {IPA.command} command
|
||||
* @param {Object} result
|
||||
* @param {string} text_status
|
||||
* @param {XMLHttpRequest} xhr
|
||||
* @return {IPA.error_list}
|
||||
*/
|
||||
that.get_failed = function(command, result, text_status, xhr) {
|
||||
var errors = IPA.error_list();
|
||||
if(result && result.failed) {
|
||||
for(var association in result.failed) {
|
||||
for(var member_name in result.failed[association]) {
|
||||
var member = result.failed[association][member_name];
|
||||
for(var i = 0; i < member.length; i++) {
|
||||
if(member[i].length > 1) {
|
||||
var name = text.get('@i18n:errors.ipa_error', 'IPA Error');
|
||||
var message = member[i][1];
|
||||
if(member[i][0])
|
||||
message = member[i][0] + ': ' + message;
|
||||
errors.add(command, name, message, text_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if command accepts option
|
||||
* @param {string} option_name
|
||||
* @return {Boolean}
|
||||
*/
|
||||
that.check_option = function(option_name) {
|
||||
|
||||
var metadata = IPA.get_command_option(that.get_command(), option_name);
|
||||
return metadata !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes command into JSON-RPC command object
|
||||
* @return {Object}
|
||||
*/
|
||||
that.to_json = function() {
|
||||
var json = {};
|
||||
|
||||
json.method = that.get_command();
|
||||
|
||||
json.params = [];
|
||||
json.params[0] = that.args || [];
|
||||
json.params[1] = that.options || {};
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes command into CLI command string
|
||||
* @return {string}
|
||||
*/
|
||||
that.to_string = function() {
|
||||
var string = that.get_command().replace(/_/g, '-');
|
||||
|
||||
for (var i=0; i<that.args.length; i++) {
|
||||
string += ' '+that.args[i];
|
||||
}
|
||||
|
||||
for (var name in that.options) {
|
||||
string += ' --'+name+'=\''+that.options[name]+'\'';
|
||||
}
|
||||
|
||||
return string;
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call multiple IPA commands in a batch over JSON-RPC.
|
||||
*
|
||||
* @class rpc.batch_command
|
||||
* @extends rpc.command
|
||||
*
|
||||
* @param {Object} spec
|
||||
* @param {Array.<IPA.command>} spec.commands - IPA commands to be executed
|
||||
* @param {Function} spec.on_success - callback function if command succeeds
|
||||
* @param {Function} spec.on_error - callback function if command fails
|
||||
*/
|
||||
rpc.batch_command = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
|
||||
spec.method = 'batch';
|
||||
|
||||
var that = IPA.command(spec);
|
||||
|
||||
/** @property {IPA.command[]} commands Commands */
|
||||
that.commands = [];
|
||||
/** @property {IPA.error_list} errors Errors */
|
||||
that.errors = IPA.error_list();
|
||||
|
||||
/**
|
||||
* Show error if some command fail
|
||||
* @property {Boolean} show_error=true
|
||||
*/
|
||||
that.show_error = typeof spec.show_error == 'undefined' ?
|
||||
true : spec.show_error;
|
||||
|
||||
/**
|
||||
* Add command
|
||||
* @param {IPA.command} command
|
||||
*/
|
||||
that.add_command = function(command) {
|
||||
that.commands.push(command);
|
||||
that.add_arg(command.to_json());
|
||||
};
|
||||
|
||||
/**
|
||||
* Add commands
|
||||
* @param {IPA.command[]} commands
|
||||
*/
|
||||
that.add_commands = function(commands) {
|
||||
for (var i=0; i<commands.length; i++) {
|
||||
that.add_command(commands[i]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
that.execute = function() {
|
||||
that.errors.clear();
|
||||
|
||||
var command = IPA.command({
|
||||
name: that.name,
|
||||
entity: that.entity,
|
||||
method: that.method,
|
||||
args: that.args,
|
||||
options: that.options,
|
||||
retry: that.retry
|
||||
});
|
||||
|
||||
command.on_success = that.batch_command_on_success;
|
||||
command.on_error = that.batch_command_on_error;
|
||||
|
||||
command.execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal XHR success handler
|
||||
*
|
||||
* Parses data and looks for errors. `on_success` or `on_error` is then
|
||||
* called.
|
||||
* @protected
|
||||
* @param {Object} data
|
||||
* @param {string} text_status
|
||||
* @param {XMLHttpRequest} xhr
|
||||
*/
|
||||
that.batch_command_on_success = function(data, text_status, xhr) {
|
||||
|
||||
for (var i=0; i<that.commands.length; i++) {
|
||||
var command = that.commands[i];
|
||||
var result = data.result.results[i];
|
||||
|
||||
var name = '';
|
||||
var message = '';
|
||||
|
||||
if (!result) {
|
||||
name = text.get('@i18n:errors.internal_error', 'Internal Error')+' '+xhr.status;
|
||||
message = result ? xhr.statusText : text.get('@i18n:errors.internal_error', 'Internal Error');
|
||||
|
||||
that.errors.add(command, name, message, text_status);
|
||||
|
||||
if (command.on_error) command.on_error.call(
|
||||
this,
|
||||
xhr,
|
||||
text_status,
|
||||
{
|
||||
name: name,
|
||||
message: message
|
||||
}
|
||||
);
|
||||
|
||||
} else if (result.error) {
|
||||
var code = result.error.code || result.error_code;
|
||||
name = text.get('@i18n:errors.ipa_error', 'IPA Error')+(code ? ' '+code : '');
|
||||
message = result.error.message || result.error;
|
||||
|
||||
if (command.retry) that.errors.add(command, name, message, text_status);
|
||||
|
||||
if (command.on_error) command.on_error.call(
|
||||
this,
|
||||
xhr,
|
||||
text_status,
|
||||
{
|
||||
name: name,
|
||||
code: code,
|
||||
message: message,
|
||||
data: result
|
||||
}
|
||||
);
|
||||
|
||||
} else {
|
||||
var failed = that.get_failed(command, result, text_status, xhr);
|
||||
that.errors.add_range(failed);
|
||||
|
||||
if (command.on_success) command.on_success.call(this, result, text_status, xhr);
|
||||
}
|
||||
}
|
||||
|
||||
//check for partial errors and show error dialog
|
||||
if (that.show_error && that.errors.errors.length > 0) {
|
||||
var ajax = this;
|
||||
var dialog = IPA.error_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: {
|
||||
name: text.get('@i18n:dialogs.batch_error_title', 'Operations Error'),
|
||||
message: that.error_message
|
||||
},
|
||||
command: that,
|
||||
errors: that.errors.errors,
|
||||
visible_buttons: [ 'ok' ]
|
||||
});
|
||||
|
||||
dialog.on_ok = function() {
|
||||
dialog.close();
|
||||
if (that.on_success) that.on_success.call(ajax, data, text_status, xhr);
|
||||
};
|
||||
|
||||
dialog.open();
|
||||
|
||||
} else {
|
||||
if (that.on_success) that.on_success.call(this, data, text_status, xhr);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal XHR error handler
|
||||
* @protected
|
||||
* @param {XMLHttpRequest} xhr
|
||||
* @param {string} text_status
|
||||
* @param {{name:string,message:string}} error_thrown
|
||||
*/
|
||||
that.batch_command_on_error = function(xhr, text_status, error_thrown) {
|
||||
// TODO: undefined behavior
|
||||
if (that.on_error) {
|
||||
that.on_error.call(this, xhr, text_status, error_thrown);
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call multiple IPA commands over JSON-RPC separately and wait for every
|
||||
* command's response.
|
||||
*
|
||||
* - concurrent command fails if any command fails
|
||||
* - result is reported when each command finishes
|
||||
*
|
||||
* @class rpc.concurrent_command
|
||||
*
|
||||
* @param {Object} spec - construct specification
|
||||
* @param {Array.<rpc.command>} spec.commands - IPA commands to execute
|
||||
* @param {Function} spec.on_success - callback function if each command succeed
|
||||
* @param {Function} spec.on_error - callback function one command fails
|
||||
*
|
||||
*/
|
||||
rpc.concurrent_command = function(spec) {
|
||||
|
||||
spec = spec || {};
|
||||
var that = IPA.object();
|
||||
|
||||
/** @property {rpc.command[]} commands Commands */
|
||||
that.commands = [];
|
||||
|
||||
/**
|
||||
* Success handler
|
||||
* @property {Function}
|
||||
*/
|
||||
that.on_success = spec.on_success;
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
* @property {Function}
|
||||
*/
|
||||
that.on_error = spec.on_error;
|
||||
|
||||
/**
|
||||
* Add commands
|
||||
* @param {rpc.command[]} commands
|
||||
*/
|
||||
that.add_commands = function(commands) {
|
||||
|
||||
if(commands && commands.length) {
|
||||
for(var i=0; i < commands.length; i++) {
|
||||
that.commands.push({
|
||||
command: commands[i]
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute the commands one by one.
|
||||
*/
|
||||
that.execute = function() {
|
||||
|
||||
var command_info, command, i;
|
||||
|
||||
//prepare for execute
|
||||
for(i=0; i < that.commands.length; i++) {
|
||||
command_info = that.commands[i];
|
||||
command = command_info.command;
|
||||
if(!command) {
|
||||
var dialog = IPA.message_dialog({
|
||||
name: 'internal_error',
|
||||
title: text.get('@i18n:errors.error', 'Error'),
|
||||
message: text.get('@i18n:errors.internal_error', 'Internal error.')
|
||||
});
|
||||
break;
|
||||
}
|
||||
command_info.completed = false;
|
||||
command_info.success = false;
|
||||
command_info.on_success = command_info.on_success || command.on_success;
|
||||
command_info.on_error = command_info.on_error || command.on_error;
|
||||
command.on_success = function(command_info) {
|
||||
return function(data, text_status, xhr) {
|
||||
that.success_handler.call(this, command_info, data, text_status, xhr);
|
||||
};
|
||||
}(command_info);
|
||||
command.on_error = function(command_info) {
|
||||
return function(xhr, text_status, error_thrown) {
|
||||
that.error_handler.call(this, command_info, xhr, text_status, error_thrown);
|
||||
};
|
||||
}(command_info);
|
||||
}
|
||||
|
||||
//execute
|
||||
for(i=0; i < that.commands.length; i++) {
|
||||
command = that.commands[i].command;
|
||||
command.execute();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal error handler
|
||||
* @protected
|
||||
*/
|
||||
that.error_handler = function(command_info, xhr, text_status, error_thrown) {
|
||||
|
||||
command_info.completed = true;
|
||||
command_info.success = false;
|
||||
command_info.xhr = xhr;
|
||||
command_info.text_status = text_status;
|
||||
command_info.error_thrown = error_thrown;
|
||||
command_info.context = this;
|
||||
that.command_completed();
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal success handler
|
||||
* @protected
|
||||
*/
|
||||
that.success_handler = function(command_info, data, text_status, xhr) {
|
||||
|
||||
command_info.completed = true;
|
||||
command_info.success = true;
|
||||
command_info.data = data;
|
||||
command_info.text_status = text_status;
|
||||
command_info.xhr = xhr;
|
||||
command_info.context = this;
|
||||
that.command_completed();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if all commands finished.
|
||||
* If so, report it.
|
||||
* @protected
|
||||
*/
|
||||
that.command_completed = function() {
|
||||
|
||||
var all_completed = true;
|
||||
var all_success = true;
|
||||
|
||||
for(var i=0; i < that.commands.length; i++) {
|
||||
var command_info = that.commands[i];
|
||||
all_completed = all_completed && command_info.completed;
|
||||
all_success = all_success && command_info.success;
|
||||
}
|
||||
|
||||
if(all_completed) {
|
||||
if(all_success) {
|
||||
that.on_success_all();
|
||||
} else {
|
||||
that.on_error_all();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Call each command's success handler and `on_success`.
|
||||
* @protected
|
||||
*/
|
||||
that.on_success_all = function() {
|
||||
|
||||
for(var i=0; i < that.commands.length; i++) {
|
||||
var command_info = that.commands[i];
|
||||
if(command_info.on_success) {
|
||||
command_info.on_success.call(
|
||||
command_info.context,
|
||||
command_info.data,
|
||||
command_info.text_status,
|
||||
command_info.xhr);
|
||||
}
|
||||
}
|
||||
|
||||
if(that.on_success) {
|
||||
that.on_success();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Call each command's error handler and `on_success`.
|
||||
* @protected
|
||||
*/
|
||||
that.on_error_all = function() {
|
||||
|
||||
if(that.on_error) {
|
||||
that.on_error();
|
||||
|
||||
} else {
|
||||
var dialog = IPA.message_dialog({
|
||||
name: 'operation_error',
|
||||
title: text.get('@i18n:dialogs.batch_error_title', 'Operations Error'),
|
||||
message: text.get('@i18n:dialogs.batch_error_message', 'Some operations failed.')
|
||||
});
|
||||
|
||||
dialog.open();
|
||||
}
|
||||
};
|
||||
|
||||
that.add_commands(spec.commands);
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Error list
|
||||
*
|
||||
* Collection for RPC command errors.
|
||||
*
|
||||
* @class IPA.error_list
|
||||
* @private
|
||||
*/
|
||||
rpc.error_list = function() {
|
||||
var that = IPA.object();
|
||||
|
||||
/** Clear errors */
|
||||
that.clear = function() {
|
||||
that.errors = [];
|
||||
};
|
||||
|
||||
/** Add error */
|
||||
that.add = function(command, name, message, status) {
|
||||
that.errors.push({
|
||||
command: command,
|
||||
name: name,
|
||||
message: message,
|
||||
status: status
|
||||
});
|
||||
};
|
||||
|
||||
/** Add errors */
|
||||
that.add_range = function(error_list) {
|
||||
that.errors = that.errors.concat(error_list.errors);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there are no errors
|
||||
* @return {Boolean}
|
||||
*/
|
||||
that.is_empty = function () {
|
||||
return that.errors.length === 0;
|
||||
};
|
||||
|
||||
that.clear();
|
||||
return that;
|
||||
};
|
||||
|
||||
/**
|
||||
* Error handler for IPA.command which handles error #4304 as success.
|
||||
*
|
||||
* 4304 is raised when part of an operation succeeds and the part that failed
|
||||
* isn't critical.
|
||||
* @member IPA
|
||||
* @param {IPA.entity_adder_dialog} adder_dialog
|
||||
*/
|
||||
rpc.create_4304_error_handler = function(adder_dialog) {
|
||||
|
||||
var set_pkey = function(result) {
|
||||
|
||||
var pkey_name = adder_dialog.entity.metadata.primary_key;
|
||||
var args = adder_dialog.command.args;
|
||||
var pkey = args[args.length-1];
|
||||
result[pkey_name] = pkey;
|
||||
};
|
||||
|
||||
return function (xhr, text_status, error_thrown) {
|
||||
|
||||
var ajax = this;
|
||||
var command = adder_dialog.command;
|
||||
var data = error_thrown.data;
|
||||
var dialog = null;
|
||||
|
||||
if (data && data.error && data.error.code === 4304) {
|
||||
dialog = IPA.message_dialog({
|
||||
name: 'error_4304_info',
|
||||
message: data.error.message,
|
||||
title: adder_dialog.title,
|
||||
on_ok: function() {
|
||||
data.result = { result: {} };
|
||||
set_pkey(data.result.result);
|
||||
command.on_success.call(ajax, data, text_status, xhr);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
dialog = IPA.error_dialog({
|
||||
xhr: xhr,
|
||||
text_status: text_status,
|
||||
error_thrown: error_thrown,
|
||||
command: command
|
||||
});
|
||||
}
|
||||
|
||||
dialog.open();
|
||||
};
|
||||
};
|
||||
|
||||
// backwards compatibility:
|
||||
IPA.error_list = rpc.error_list;
|
||||
IPA.create_4304_error_handler = rpc.create_4304_error_handler;
|
||||
IPA.command = rpc.command;
|
||||
IPA.batch_command = rpc.batch_command;
|
||||
IPA.concurrent_command = rpc.concurrent_command;
|
||||
|
||||
lang.mixin(exports, rpc);
|
||||
|
||||
return rpc;
|
||||
});
|
||||
Reference in New Issue
Block a user