Merge pull request #284 from joefiorini/system-tracking--displayKeys

Adds support for formatting individual fact values
This commit is contained in:
Joe Fiorini
2015-06-16 09:28:57 -04:00
7 changed files with 643 additions and 96 deletions
@@ -0,0 +1,16 @@
export default
[ 'moment',
function(moment) {
return function(seconds, formatStr) {
if (!formatStr) {
formatStr = 'll LT';
}
var millis = seconds * 1000;
return moment(millis).format(formatStr);
};
}
];
@@ -0,0 +1,9 @@
import formatEpoch from './format-epoch.filter';
import moment from 'tower/shared/moment/main';
export default
angular.module('formatEpoch',
[ moment.name
])
.filter('formatEpoch', formatEpoch);
@@ -6,27 +6,48 @@
import compareNestedFacts from './compare-facts/nested';
import compareFlatFacts from './compare-facts/flat';
import FactTemplate from './compare-facts/fact-template';
export function compareFacts(module, facts) {
if (module.displayType === 'nested') {
return { factData: compareNestedFacts(facts),
isNestedDisplay: true
};
} else {
var renderOptions = _.merge({}, module);
// If the module has a template or includes a list of keys to display,
// then perform a flat comparison, otherwise assume nested
//
if (renderOptions.factTemplate || renderOptions.nameKey) {
// For flat structures we compare left-to-right, then right-to-left to
// make sure we get a good comparison between both hosts
var compare = _.partialRight(compareFlatFacts, module.nameKey, module.compareKey, module.factTemplate);
var leftToRight = compare(facts[0], facts[1]);
var rightToLeft = compare(facts[1], facts[0]);
if (_.isPlainObject(renderOptions.factTemplate)) {
renderOptions.factTemplate =
_.mapValues(renderOptions.factTemplate, function(template) {
if (typeof template === 'string' || typeof template === 'function') {
return new FactTemplate(template);
} else {
return template;
}
});
} else {
renderOptions.factTemplate = new FactTemplate(renderOptions.factTemplate);
}
var leftToRight = compareFlatFacts(facts[0], facts[1], renderOptions);
var rightToLeft = compareFlatFacts(facts[1], facts[0], renderOptions);
return _(leftToRight)
.concat(rightToLeft)
.unique('displayKeyPath')
.thru(function(result) {
return { factData: result,
isNestedDisplay: _.isUndefined(module.factTemplate)
isNestedDisplay: _.isPlainObject(renderOptions.factTemplate)
};
})
.value();
} else {
return { factData: compareNestedFacts(facts),
isNestedDisplay: true
};
}
}
@@ -0,0 +1,33 @@
import stringFilters from 'tower/shared/string-filters/main';
import formatEpoch from 'tower/shared/format-epoch/main';
var $injector = angular.injector(['ng', stringFilters.name, formatEpoch.name]);
var $interpolate = $injector.get('$interpolate');
function FactTemplate(templateString) {
this.templateString = templateString;
}
function loadFactTemplate(factTemplate, fact) {
if (_.isFunction(factTemplate)) {
return factTemplate(fact);
} else {
return factTemplate;
}
}
FactTemplate.prototype.render = function(factData) {
if (_.isUndefined(factData) || _.isEmpty(factData)) {
return 'absent';
}
var template = loadFactTemplate(this.templateString, factData);
return $interpolate(template)(factData);
};
FactTemplate.prototype.hasTemplate = function() {
return !_.isUndefined(this.templateString);
};
export default FactTemplate;
@@ -4,23 +4,6 @@
* All Rights Reserved
*************************************************/
import stringFilters from 'tower/shared/string-filters/main';
var $injector = angular.injector(['ng', stringFilters.name]);
var $interpolate = $injector.get('$interpolate');
function getFactTemplate(factTemplate, fact) {
if (_.isFunction(factTemplate)) {
return factTemplate(fact);
} else {
return factTemplate;
}
}
function renderFactTemplate(template, fact) {
return $interpolate(template)(fact);
}
function slotFactValues(basisPosition, basisValue, comparatorValue) {
var leftValue, rightValue;
@@ -38,92 +21,122 @@ function slotFactValues(basisPosition, basisValue, comparatorValue) {
}
export default
function flatCompare(basisFacts, comparatorFacts, nameKey, compareKeys, factTemplate) {
function flatCompare(basisFacts,
comparatorFacts, renderOptions) {
var nameKey = renderOptions.nameKey;
var compareKeys = renderOptions.compareKey;
var keyNameMap = renderOptions.keyNameMap;
var factTemplate = renderOptions.factTemplate;
return basisFacts.facts.reduce(function(arr, basisFact) {
var searcher = {};
searcher[nameKey] = basisFact[nameKey];
var basisTemplate, comparatorTemplate, slottedValues, basisValue, comparatorValue;
var matchingFact = _.where(comparatorFacts.facts, searcher);
var diffs;
if (_.isEmpty(matchingFact)) {
// Perform comparison and get back comparisonResults; like:
// { 'value':
// { leftValue: 'blah',
// rightValue: 'doo'
// }
// };
//
var comparisonResults =
_.reduce(compareKeys, function(result, compareKey) {
if (!_.isUndefined(factTemplate)) {
var comparatorFact = matchingFact[0] || {};
var isNestedDisplay = false;
basisTemplate = getFactTemplate(factTemplate, basisFact);
basisValue = renderFactTemplate(basisTemplate, basisFact);
slottedValues = slotFactValues(basisFacts.position, basisValue, 'absent');
diffs =
{ keyName: basisFact[nameKey],
value1: slottedValues.left,
value2: slottedValues.right
};
} else {
diffs =
_.map(basisFact, function(value, key) {
var slottedValues = slotFactValues(basisFacts.position, value, 'absent');
return { keyName: key,
value1: slottedValues.left,
value1IsAbsent: slottedValues.left === 'absent',
value2: slottedValues.right,
value2IsAbsent: slotFactValues.right === 'absent'
};
});
}
} else {
matchingFact = matchingFact[0];
if (!_.isUndefined(factTemplate)) {
basisTemplate = getFactTemplate(factTemplate, basisFact);
comparatorTemplate = getFactTemplate(factTemplate, matchingFact);
basisValue = renderFactTemplate(basisTemplate, basisFact);
comparatorValue = renderFactTemplate(comparatorTemplate, matchingFact);
slottedValues = slotFactValues(basisFacts.position, basisValue, comparatorValue);
if (basisValue !== comparatorValue) {
diffs =
{ keyName: basisFact[nameKey],
value1: slottedValues.left,
value2: slottedValues.right
};
var slottedValues = slotFactValues(basisFacts.position,
basisFact[compareKey],
comparatorFact[compareKey]);
if (_.isUndefined(slottedValues.left) && _.isUndefined(slottedValues.right)) {
return result;
}
} else {
var template = factTemplate;
diffs = _(compareKeys)
.map(function(key) {
var slottedValues = slotFactValues(basisFacts.position,
basisFact[key],
matchingFact[key]);
if (_.isObject(template) && template.hasOwnProperty(compareKey)) {
template = template[compareKey];
if (slottedValues.left !== slottedValues.right) {
return {
keyName: key,
value1: slottedValues.left,
value2: slottedValues.right
// 'true' means render the key without formatting
if (template === true) {
template =
{ render: function(fact) { return fact[compareKey]; }
};
}
isNestedDisplay = true;
} else if (typeof template.hasTemplate === 'function' && !template.hasTemplate()) {
template =
{ render: function(fact) { return fact[compareKey]; }
};
isNestedDisplay = true;
} else if (typeof factTemplate.render === 'function') {
template = factTemplate;
} else if (!template.hasOwnProperty(compareKey)) {
return result;
}
if (basisFacts.position === 'left') {
slottedValues.left = template.render(basisFact);
slottedValues.right = template.render(comparatorFact);
} else {
slottedValues.left = template.render(comparatorFact);
slottedValues.right = template.render(basisFact);
}
if (slottedValues.left !== slottedValues.right) {
slottedValues.isDivergent = true;
} else {
slottedValues.isDivergent = false;
}
if (isNestedDisplay) {
result[compareKey] = slottedValues;
} else {
result = slottedValues;
}
return result;
}, {});
var hasDiffs =
_.any(comparisonResults, { isDivergent: true }) ||
comparisonResults.isDivergent === true;
if (hasDiffs && typeof factTemplate.render === 'function') {
diffs =
{ keyName: basisFact[nameKey],
value1: comparisonResults.left,
value2: comparisonResults.right
};
} else if (hasDiffs) {
diffs =
_(comparisonResults).map(function(slottedValues, key) {
var keyName = key;
if (keyNameMap && keyNameMap[key]) {
keyName = keyNameMap[key];
}
return { keyName: keyName,
value1: slottedValues.left,
value2: slottedValues.right,
isDivergent: slottedValues.isDivergent
};
}).compact()
.value();
}
}
var descriptor =
{ displayKeyPath: basisFact[nameKey],
nestingLevel: 0,
@@ -2,29 +2,34 @@ var moduleConfig =
{ 'packages':
{ compareKey: ['release', 'version'],
nameKey: 'name',
displayType: 'flat',
sortKey: 1,
factTemplate: "{{epoch|append:':'}}{{version}}-{{release}}{{arch|prepend:'.'}}"
},
'services':
{ compareKey: ['state', 'source'],
nameKey: 'name',
displayType: 'flat',
factTemplate: '{{state}} ({{source}})',
sortKey: 2
},
'files':
{ compareKey: ['size', 'mode', 'md5', 'mtime', 'gid', 'uid'],
keyNameMap:
{ 'uid': 'ownership'
},
factTemplate:
{ 'uid': 'user id: {{uid}}, group id: {{gid}}',
'mode': true,
'md5': true,
'mtime': '{{mtime|formatEpoch}}'
},
nameKey: 'path',
displayType: 'flat',
sortKey: 3
},
'ansible':
{ displayType: 'nested',
sortKey: 4
{ sortKey: 4
},
'custom':
{ displayType: 'nested'
{
}
};