webui: move RPC result extraction logic to Adapter

It enables declarative extraction of values from partial
results of a batch commands and also further extensibility
in custom adapters.

The default adapter has detection logic for this extraction so
it can use bare record or extract data from normal or batch RPC
command.

Minor change of user plugin fixed:
https://fedorahosted.org/freeipa/ticket/4355

Reviewed-By: Endi Sukma Dewata <edewata@redhat.com>
This commit is contained in:
Petr Vobornik 2014-05-28 14:45:57 +02:00
parent 521df77744
commit 5a428608be
8 changed files with 120 additions and 90 deletions

View File

@ -455,7 +455,7 @@ IPA.automember.condition_field = function(spec) {
IPA.automember.condition_adapter = declare([field_mod.Adapter], {
load: function(record) {
load: function(data) {
var regexes = this.inherited(arguments);
var values = [];
if (regexes) {

View File

@ -237,15 +237,15 @@ exp.section_builder = IPA.section_builder = function(spec) {
*/
that.build_section = function(section_spec, index) {
var spec = section_spec;
var spec = {};
var overrides = {};
var spec_type = typeof that.section_spec;
if (spec_type === 'object') {
spec = lang.mixin({}, that.section_spec);
spec = lang.mixin(spec, section_spec);
} else if (spec_type === "function") {
overrides = that.section_spec;
}
spec = lang.mixin(spec, section_spec);
if (!spec.label && spec.name && that.container.entity) {
var section_label = '@i18n:objects.'+that.container.entity.name+
@ -261,7 +261,9 @@ exp.section_builder = IPA.section_builder = function(spec) {
}, overrides);
that.container.widgets.add_widget(section);
section.$field_adapter = spec.field_adapter;
that.create_fields(section, spec.fields);
delete section.$field_adapter;
};
/**
@ -283,8 +285,18 @@ exp.section_builder = IPA.section_builder = function(spec) {
*/
that.create_field = function(section, field_spec) {
if (typeof field_spec === 'string') {
field_spec = {
name: field_spec
};
}
var widget = that.widget_builder.build_widget(field_spec, section.widgets);
if (section.$field_adapter && !field_spec.adapter) {
field_spec.adapter = section.$field_adapter;
}
//spec.$factory refers to widget factory
if(field_spec.$factory) delete field_spec.$factory;
@ -714,7 +726,7 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) {
var fields = that.fields.get_fields();
for (var i=0; i<fields.length; i++) {
var field = fields[i];
field.load(data.result.result);
field.load(data);
}
that.policies.post_load(data);
that.post_load.notify([data], that);
@ -1467,6 +1479,10 @@ exp.boolean_state_evaluator = IPA.boolean_state_evaluator = function(spec) {
*/
that.field_name = spec.field;
that.param = spec.param || that.field_name;
that.adapter = builder.build('adapter', spec.adapter || 'adapter', { context: that });
/**
* State to set when value is `true`
* @property {string}
@ -1504,10 +1520,10 @@ exp.boolean_state_evaluator = IPA.boolean_state_evaluator = function(spec) {
that.on_event = function(data) {
var old_state = that.state;
var record = data.result.result;
that.state = [];
var value = that.parser.parse(record[that.field_name]);
var value = that.adapter.load(data);
value = that.parser.parse(value);
if (value === true) {
that.state.push(that.true_state);
@ -1570,20 +1586,23 @@ exp.acl_state_evaluator = IPA.acl_state_evaluator = function(spec) {
*/
that.attribute = spec.attribute;
that.param = spec.param || 'attributelevelrights';
that.adapter = builder.build('adapter', spec.adapter || 'adapter', { context: that });
/**
* @inheritDoc
*/
that.on_event = function(data) {
var old_state, record, rights, i, state;
var old_state, record, all_rights, rights, i, state;
old_state = that.state;
record = data.result.result;
that.state = [];
if (record.attributelevelrights) {
rights = record.attributelevelrights[that.attribute];
all_rights = that.adapter.load(data);
if (all_rights) {
rights = all_rights[that.attribute];
}
// Full rights if we don't know the rights. Better to allow action and

View File

@ -339,12 +339,6 @@ IPA.dnszone_details_facet = function(spec, no_init) {
that.permission_load = IPA.observer();
that.permission_status = 'unknown'; // [unknown, set, none]
that.refresh_on_success = function(data, text_status, xhr) {
// do not load data from batch
that.show_content();
};
that.create_refresh_command = function() {
var pkey = that.get_pkey();
@ -354,14 +348,6 @@ IPA.dnszone_details_facet = function(spec, no_init) {
});
var dnszone_command = that.details_facet_create_refresh_command();
dnszone_command.on_success = function(data, text_status, xhr) {
// create data that mimics dnszone-show output
var dnszone_data = {};
dnszone_data.result = data;
that.load(dnszone_data);
};
batch.add_command(dnszone_command);
var permission_name = IPA.dns.zone_permission_name.replace('${dnszone}', pkey);
@ -1608,8 +1594,9 @@ IPA.dns.record_type_adapter = declare([field_mod.Adapter], {
separator: ';',
load: function(record) {
load: function(rpcdata) {
var record = this.get_record(rpcdata);
var data = {};
data.idnsname = record.idnsname;
@ -2060,7 +2047,7 @@ IPA.dns.netaddr_adapter = declare([field_mod.Adapter], {
separator: ';',
load: function(record) {
load: function(data) {
var value = this.inherited(arguments)[0];
if (value) {
if (value[value.length-1] === this.separator) {

View File

@ -411,17 +411,18 @@ field.field = IPA.field = function(spec) {
* This function calls adapter to get value from record and date_parser to
* process it. The it sets is as `value`.
*/
that.load = function(record) {
that.load = function(data) {
var value = that.adapter.load(record);
var value = that.adapter.load(data);
var parsed = util.parse(that.data_parser, value, "Parse error:"+that.name);
value = parsed.value;
if (!parsed.ok) {
window.console.warn(parsed.message);
}
// this call is quite application specific and should be moved to
// this part is quite application specific and should be moved to
// different component
var record = that.adapter.get_record(data);
that.load_writable(record);
that.set_value(value, true);
@ -459,7 +460,7 @@ field.field = IPA.field = function(spec) {
}
}
if (record.attributelevelrights) {
if (record && record.attributelevelrights) {
var rights = record.attributelevelrights[that.acl_param];
var oc_rights= record.attributelevelrights['objectclass'];
var write_oc = oc_rights && oc_rights.indexOf('w') > -1;
@ -740,10 +741,10 @@ field.field = IPA.field = function(spec) {
};
/**
* Adapter's task is to select wanted data from record and vice-versa.
* Adapter's task is to select wanted data from RPC response
*
* This default adapter expects that context will be field and record
* will be FreeIPA JsonRPC result.
* This default adapter expects that context will be a field and data
* will be FreeIPA JsonRPC response.
*
* @class
*/
@ -756,6 +757,41 @@ field.Adapter = declare(null, {
*/
context: null,
/**
* Index of result in batch results array
* @type {Number}
*/
result_index: 0,
/**
* Extract record from RPC call response
*
* Tries to detect if supplied data is RPC call response if so, it
* extracts the record. Otherwise it returns supplied data as the record.
*
* @param {Object} data Response data or record
* @return {Object} record
*/
get_record: function(data) {
// detection if it's result or raw RPC command response
// all raw responses should contain `version` and `principal`
if (!data.version || !data.principal) {
return data;
}
var dr = data.result;
var record = null;
if (dr) {
if (dr.result) record = dr.result;
else if (dr.results) {
var result = dr.results[this.result_index];
if (result) record = result.result;
}
}
return record;
},
/**
* Get single value from record
* @param {Object} record Record
@ -772,13 +808,21 @@ field.Adapter = declare(null, {
* By default just select attribute with name defined by `context.param`
* from a record. Uses default value if value is not in record and context
* defines it.
* @param {Object} record
* @param {Object} data Object which contains the record or the record
* @param {string} [attribute] attribute name - overrides `context.param`
* @param {Mixed} [def_val] default value - overrides `context.default_value`
* @returns {Array} attribute value
*/
load: function(record) {
var value = this.get_value(record, this.context.param);
if (util.is_empty(value) && !util.is_empty(this.context.default_value)) {
value = util.normalize_value(this.context.default_value);
load: function(data, attribute, def_val) {
var record = this.get_record(data);
var value = null;
var attr = attribute || this.context.param;
var def = def_val || this.context.default_value;
if (record) {
value = this.get_value(record, attr);
}
if (util.is_empty(value) && !util.is_empty(def)) {
value = util.normalize_value(def);
}
return value;
},
@ -803,6 +847,7 @@ field.Adapter = declare(null, {
},
constructor: function(spec) {
declare.safeMixin(this, spec);
this.context = spec.context || {};
}
});
@ -1127,7 +1172,9 @@ field.SshKeysAdapter = declare([field.Adapter], {
* ]
* """
*/
load: function(record) {
load: function(data) {
var record = this.get_record(data);
var keys = this.get_value(record, this.context.param);
var fingerprints = this.get_value(record, 'sshpubkeyfp');
var values = [];
@ -1392,6 +1439,7 @@ reg.set('validator', field.validator_builder.registry);
* @member field
*/
field.adapter_builder = builder.get('adapter');
field.adapter_builder.ctor = field.Adapter;
field.adapter_builder.post_ops.push(function(obj, spec, context) {
obj.context = context.context;
return obj;

View File

@ -193,12 +193,12 @@ IPA.rule_association_table_field = function(spec) {
}
};
that.load = function(result) {
that.values = result[that.param] || [];
that.load = function(data) {
that.values = that.adapter.load(data);
if (that.external) {
that.set_values_external(that.values, '');
var external_values = result[that.external] || [];
var external_values = that.adapter.load(data, that.external, []);
that.set_values_external(external_values, 'true');
$.merge(that.values, external_values);
}

View File

@ -269,7 +269,8 @@ IPA.service_adder_dialog = function(spec) {
};
IPA.service_name_adapter = declare([field_mod.Adapter], {
load: function(record) {
load: function(data) {
var record = this.get_record(data);
var krbprincipalname = record.krbprincipalname[0];
var value = krbprincipalname.replace(/\/.*$/, '');
return [value];
@ -277,7 +278,8 @@ IPA.service_name_adapter = declare([field_mod.Adapter], {
});
IPA.service_host_adapter = declare([field_mod.Adapter], {
load: function(record) {
load: function(data) {
var record = this.get_record(data);
var krbprincipalname = record.krbprincipalname[0];
var value = krbprincipalname.replace(/^.*\//, '').replace(/@.*$/, '');
return [value];

View File

@ -153,6 +153,7 @@ return {
{
name: 'pwpolicy',
label: '@i18n:objects.pwpolicy.identity',
field_adapter: { result_index: 1 },
fields: [
{
name: 'krbmaxpwdlife',
@ -202,6 +203,7 @@ return {
{
name: 'krbtpolicy',
label: '@i18n:objects.krbtpolicy.identity',
field_adapter: { result_index: 2 },
fields: [
{
name: 'krbmaxrenewableage',
@ -272,9 +274,15 @@ return {
{
$factory: IPA.enable_state_evaluator,
field: 'nsaccountlock',
adapter: { $type: 'batch', result_index: 0 },
invert_value: true
},
IPA.user.reset_password_acl_evaluator
{
$factory: IPA.acl_state_evaluator,
name: 'reset_password_acl_evaluator',
adapter: { $type: 'batch', result_index: 0 },
attribute: 'userpassword'
}
],
summary_conditions: [
IPA.enabled_summary_cond,
@ -358,12 +366,6 @@ IPA.user.details_facet = function(spec) {
var that = IPA.details_facet(spec);
that.refresh_on_success = function(data, text_status, xhr) {
// do not load data from batch
that.show_content();
};
that.create_refresh_command = function() {
var pkey = that.get_pkey();
@ -373,19 +375,12 @@ IPA.user.details_facet = function(spec) {
});
var user_command = that.details_facet_create_refresh_command();
user_command.on_success = function(data, text_status, xhr) {
// create data that mimics user-show output
var user_data = {};
user_data.result = data;
that.load(user_data);
};
batch.add_command(user_command);
var pwpolicy_command = rpc.command({
entity: 'pwpolicy',
method: 'show',
retry: false,
options: {
user: pkey,
all: true,
@ -394,16 +389,11 @@ IPA.user.details_facet = function(spec) {
});
pwpolicy_command.on_success = function(data, text_status, xhr) {
// TODO: Use nested fields: that.fields.get_field('pwpolicy').get_fields();
var fields = that.fields.get_fields();
for (var i=0; i<fields.length; i++) {
var field = fields[i];
that.widgets.get_widget('pwpolicy').set_visible(true);
};
// load result into pwpolicy fields
if (field.widget_name.match(/^pwpolicy\./)) {
field.load(data.result);
}
}
pwpolicy_command.on_error = function(xhr, text_status, error_thrown) {
that.widgets.get_widget('pwpolicy').set_visible(false);
};
batch.add_command(pwpolicy_command);
@ -412,6 +402,7 @@ IPA.user.details_facet = function(spec) {
entity: 'krbtpolicy',
method: 'show',
args: [ pkey ],
retry: false,
options: {
all: true,
rights: true
@ -419,16 +410,11 @@ IPA.user.details_facet = function(spec) {
});
krbtpolicy_command.on_success = function(data, text_status, xhr) {
// TODO: Use nested fields: that.fields.get_field('krbtpolicy').get_fields();
var fields = that.fields.get_fields();
for (var i=0; i<fields.length; i++) {
var field = fields[i];
that.widgets.get_widget('krbtpolicy').set_visible(true);
};
// load result into krbtpolicy fields
if (field.widget_name.match(/^krbtpolicy\./)) {
field.load(data.result);
}
}
krbtpolicy_command.on_error = function(xhr, text_status, error_thrown) {
that.widgets.get_widget('krbtpolicy').set_visible(false);
};
batch.add_command(krbtpolicy_command);
@ -693,15 +679,6 @@ IPA.user.reset_password_action = function(spec) {
return that;
};
IPA.user.reset_password_acl_evaluator = function(spec) {
spec.name = spec.name || 'reset_password_acl_evaluator';
spec.attribute = spec.attribute || 'userpassword';
var that = IPA.acl_state_evaluator(spec);
return that;
};
exp.entity_spec = make_spec();
exp.register = function() {
var e = reg.entity;

View File

@ -209,9 +209,6 @@ define([
if (!(value instanceof Array)) {
value = value !== undefined ? [value] : [];
}
if (!value.length) {
value = [''];
}
return value;
}