Name
global.CIData
Description
Utility class for working with CI data structures in JavaScript.
Script
// Discovery class
gs.include("PrototypeServer");
/**
* Utility class for working with CI data structures. Instances of this class represent a CI (both its base record and
* any related lists), and the provided methods allow loading from or storing both the base record and related lists.
*
* Note that this class acts as a container for simple classes acting as maps of property name/value pairs. This class
* contains three kinds of such maps:
* 1. A single map representing the base CI table entry (such as one row cmdb_ci_linux_server and its superclasses).
* This map can be retrieved with the getData() method.
* 2. Arrays of maps representing the instances of related lists, with each array representing a single related list
* and each element of such an array representing a single row of that related list. These arrays can be
* retrieved with the getRelatedList() method.
* 3. Arrays of maps representing the instances of many-to-many tables, with each array representing a single
* many-to-many table and each element of such an array representing a single row of that many-to-many table.
* These arrays can be retrieved with the getM2MTable() method.
*
* Tom Dilatush tom.dilatush@service-now.com
*/
var CIData = Class.create();
CIData.prototype = {
/*
* Initializes a new instance of this class.
* @param {} data The initial data
* @param boolean debug True if debug logging is enabled.
*/
initialize: function(data, debug) {
this.ignoreFields = {
'sys_created_by' : true,
'sys_updated_by' : true,
'sys_mod_count' : true
};
this.GlideRecordUtil = new GlideRecordUtil();
this.debug_flag = ( !!debug );
this.init(data);
},
/*
* Initialize this instance in preparation for loading a new CI.
*/
init: function(data) {
// the map of data in the base CI record...
this.data = ( !gs.nil(data) ? data : {} );
// the map of CIRelatedList instances, keyed as table:ref_field...
this.rl_map = {};
// the sys_id of the CI this instance represents...
this.cmdb_ci = null;
/* @var { relationshipDescriptor: CIData } */
this.related = {};
},
/*
* Returns the map of data in the base CI record.
*/
getData: function() {
return this.data;
},
/**
* Adds a related list. Does not create dupes.
* Pass an instantiated CIRelatedList as the sole parameter to overwrite.
* @param string table
* @param string refField
*/
addRelatedList: function(table, refField) {
var list = null;
if (typeof refField !== 'undefined') { // replace behavior
var key = table + ':' + refField;
if (typeof this.rl_map[key] === 'undefined') {
list = new CIRelatedList(table, refField);
this.rl_map[key] = list;
} else { // don't overwrite
list = this.rl_map[key];
}
} else { // overwrite behavior
list = table;
this.rl_map[list.table_name + ':' + list.field_name] = list;
}
return list;
},
/*
* Returns an array of maps of data in the given related list (to this CI). The array is not in any particular
* order. Note that in the case of a many-to-many list, this will be an array of instances of the target table, not
* the many-to-many table. For example, given 'cmdb_software_instance' and 'installed_on' (a many-to-many table and
* the field that refers to a CI), this method will return an array of maps representing cmdb_ci_spkg (the target
* table) instances.
*
* table: the name of the table containing the related list. Note that in the case of a many-to-many list, this
* will be the name of the many-to-many table, NOT the target table.
* ref_field: the name of the field in the related list that refers to this CI. Note that in the case of a
* many-to-many list, this will be the name of the referring field in the many-to-many table, NOT in the
* target table.
*/
getRelatedList: function(table, ref_field) {
var rl = this.getRelatedListInstance(table, ref_field);
if (rl != null)
return rl.records;
return [];
},
/*
* Returns an array of maps of data in the given many-to-many list (to this CI). The array is not in any particular
* order. If this is not a many-to-many list, returns an empty array.
*
* table: the name of the table containing the related list. Note that in the case of a many-to-many list, this
* will be the name of the many-to-many table, NOT the target table.
* ref_field: the name of the field in the related list that refers to this CI. Note that in the case of a
* many-to-many list, this will be the name of the referring field in the many-to-many table, NOT in the
* target table.
*/
getM2MTable: function(table, ref_field) {
var rl = this.getRelatedListInstance(table, ref_field);
if (rl != null)
return rl.m2m_records;
return [];
},
/*
* Returns the instance of CIRelatedList for the given list.
*
* table: the name of the table containing the related list. Note that in the case of a many-to-many list, this
* will be the name of the many-to-many table, NOT the target table.
* ref_field: the name of the field in the related list that refers to this CI. Note that in the case of a
* many-to-many list, this will be the name of the referring field in the many-to-many table, NOT in the
* target table.
*/
getRelatedListInstance: function(table, ref_field) {
// see if we already have the list
var key = this._make_rl_key(table, ref_field);
var rl = this.rl_map[key];
// if yes, retrieve and return
if (rl !== undefined)
return rl;
// Normally if this was a constructed CI (such as in identification phase of Discovery), we would not reach here because
// if it had a related list, it would've been a constructed list and returned earlier.
// The point here is that if someone had accidentally requested a related list that didn't exist and needed it to be loaded from the db,
// the "this.cmdb_ci" variable has to exist, otherwise it would query the entire related table; which is very bad thing to do!
if (this.cmdb_ci == null)
return null;
// if no, create a new related list...
rl = new CIRelatedList(table, ref_field, this.cmdb_ci, this.debug_flag);
this.rl_map[key] = rl;
// retrieve and return
return rl;
},
/*
* Load this.data from the given sys_id.
*
* cmdb_ci: the sys_id of the CI we're to load from
*/
loadFromCI: function(cmdb_ci) {
this.init();
this.cmdb_ci = cmdb_ci;
var baseGR = this.GlideRecordUtil.getCIGR(cmdb_ci);
if (!baseGR)
return;
var ourTable = baseGR.getTableName();
// get our base record...
this.GlideRecordUtil.populateFromGR(this.data, baseGR, this.ignoreFields);
// skedaddle with our data...
return this.data;
},
/*
* Returns the key for this.rl_map, given a table name and reference field.
*
* table: the related list table name.
* ref_field: the reference field in the related list.
*/
_make_rl_key: function(table, ref_field) {
return table + ':' + ref_field;
},
/*
* Returns an XML string containing a serialized version of this instance (including any related lists).
*/
toXML: function() {
var xml_util = GlideXMLUtil;
var doc = xml_util.newDocument('CIData');
var root = doc.getDocumentElement();
// add the base CI's data elements...
var data = xml_util.newElement(root, 'data');
for (var field_name in this.data) {
var el = XMLUtilJS.valueToString(this.data[field_name]);
var data_el = xml_util.newElement(data, 'fld');
data_el.setAttribute('name', field_name);
xml_util.setText(data_el, el);
}
// now add our related lists...
for (var name in this.rl_map) {
var rl = this.rl_map[name];
var rl_el = xml_util.newElement(root, 'rl');
rl_el.setAttribute('name', name);
rl.toXML(doc, rl_el);
}
return xml_util.toFragmentString(doc);
},
/*
* Initializes this instance from the given XML string.
*/
fromXML: function(xml) {
this.init();
if (gs.nil(xml))
return;
var xml_util = GlideXMLUtil;
var doc = xml_util.parse(xml);
if (!doc)
return;
var root = doc.getDocumentElement();
if (root.getTagName() != 'CIData')
return;
// look through our children for data or related-list elements...
var it = xml_util.childElementIterator(root);
while (it.hasNext()) {
var el = it.next();
// if we've got a data element, slurp up the fields...
if (el.getTagName() == 'data') {
var flds = xml_util.getChildrenByTagName(el, 'fld');
for (var i = 0; i < flds.size(); i++) {
var fld_el = flds.get(i);
var fld_name = fld_el.getAttribute('name');
var fld_value = xml_util.getText(fld_el);
if (fld_value != null)
fld_value = XMLUtilJS.stringToValue(fld_value);
this.data[fld_name] = fld_value;
}
}
// if we've got a related list element, go get it...
else if (el.getTagName() == 'rl') {
var rl = new CIRelatedList();
rl.fromXML(el);
if (JSUtil.has(rl)) {
var rl_key = rl.table_name + ':' + rl.field_name;
this.rl_map[rl_key] = rl;
}
}
}
},
toString: function() {
var result = [];
result.push('CIData instance:');
for (var name in this.data)
result.push(' ' + name + ': ' + this.data[name]);
for (var name in this.rl_map)
this.rl_map[name].toString(result);
result.push(this.related.toString());
return result.join('\n');
},
/*
* Converts the specific given related list to a related list in the given sensor.
*/
convertRelatedList: function(sensor, table_name, ref_field, keyName) {
var ourKey = table_name + ':' + ref_field;
var rl = this.rl_map[ourKey];
if (typeof rl == 'undefined')
return;
sensor.addToRelatedList(table_name, rl.records, ref_field, keyName);
},
type: 'CIData'
};
Sys ID
a1d604430ab3015000127054313d8d41