diff --git a/lib/eventApi.js b/lib/eventApi.js index 5458cbf811..2b41f37158 100644 --- a/lib/eventApi.js +++ b/lib/eventApi.js @@ -1,9 +1,11 @@ -var eventDb = require('./eventDb'); +var eventDb = require('./eventDb'); +var eventDiffer = require('./eventDiffer'); module.exports = function (app) { app.get('/events', function (req, res) { eventDb.getEvents().then(function (events) { + eventDiffer.addDiffs(events); res.json({events: events}); }); }); @@ -11,6 +13,7 @@ module.exports = function (app) { app.get('/events/:name', function (req, res) { eventDb.getEventsFilterByName(req.params.name).then(function (events) { if (events) { + eventDiffer.addDiffs(events); res.json(events); } else { res.status(404).json({error: 'Could not find events'}); @@ -18,5 +21,4 @@ module.exports = function (app) { }); }); - }; \ No newline at end of file diff --git a/lib/eventDiffer.js b/lib/eventDiffer.js new file mode 100644 index 0000000000..6b14292c49 --- /dev/null +++ b/lib/eventDiffer.js @@ -0,0 +1,74 @@ +var eventType = require('./eventType'); +var diff = require('deep-diff').diff; + +var strategyTypes = [ + eventType.strategyCreated, + eventType.strategyDeleted +]; + +var featureTypes = [ + eventType.featureCreated, + eventType.featureUpdated, + eventType.featureArchive, + eventType.featureRevive +]; + +function baseTypeFor(event) { + if (featureTypes.indexOf(event.type) !== -1) { + return 'features'; + } else if (strategyTypes.indexOf(event.type) !== -1) { + return 'strategies'; + } else { + throw new Error('unknown event type: ' + JSON.stringify(event)); + } +} + +function groupByBaseTypeAndName(events) { + var groups = {}; + + events.forEach(function (event) { + var baseType = baseTypeFor(event); + + groups[baseType] = groups[baseType] || {}; + groups[baseType][event.data.name] = groups[baseType][event.data.name] || []; + + groups[baseType][event.data.name].push(event); + }); + + return groups; +} + +function eachConsecutiveEvent(events, callback) { + var groups = groupByBaseTypeAndName(events); + + Object.keys(groups).forEach(function (baseType) { + var group = groups[baseType]; + + Object.keys(group).forEach(function (name) { + var events = group[name]; + var left, right, i, l; + for (i = 0, l = events.length; i < l; i++) { + left = events[i]; + right = events[i + 1]; + + callback(left, right); + } + }); + }); +} + +function addDiffs(events) { + eachConsecutiveEvent(events, function (left, right) { + if (right) { + left.diffs = diff(right.data, left.data); + left.diffs = left.diffs || []; + } else { + left.diffs = null; + } + }); +} + + +module.exports = { + addDiffs: addDiffs +}; \ No newline at end of file diff --git a/package.json b/package.json index b4561cc1bb..e7f250d9ae 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "bluebird": "2.6.2", "body-parser": "1.10.1", "db-migrate": "0.7.1", + "deep-diff": "^0.3.0", "errorhandler": "1.3.2", "express": "4.9.8", "express-validator": "2.8.0", diff --git a/public/css/unleash.css b/public/css/unleash.css index 2d77687794..4d903cbfe5 100644 --- a/public/css/unleash.css +++ b/public/css/unleash.css @@ -22,4 +22,16 @@ code { word-wrap: break-word; white-space: pre; +} + +code > .diff-N { + color: green; +} + +code > .diff-D { + color: red; +} + +code > .diff-A, .diff-E { + color: black; } \ No newline at end of file diff --git a/public/js/components/log/LogEntry.jsx b/public/js/components/log/LogEntry.jsx index 338c76a50a..12cf37f315 100644 --- a/public/js/components/log/LogEntry.jsx +++ b/public/js/components/log/LogEntry.jsx @@ -1,5 +1,19 @@ var React = require('react'); +var DIFF_PREFIXES = { + A: ' ', + E: ' ', + D: '-', + N: '+' +} + +var SPADEN_CLASS = { + A: 'blue', // array edited + E: 'blue', // edited + D: 'negative', // deleted + N: 'positive', // added +} + var LogEntry = React.createClass({ propTypes: { event: React.PropTypes.object.isRequired @@ -7,25 +21,64 @@ var LogEntry = React.createClass({ render: function() { var d = new Date(this.props.event.createdAt); + + return ( +
{JSON.stringify(localEventData, null, 2)}
- {prettyPrinted}
)
+ },
+
+ renderEventDiff: function() {
+ if (!this.props.showFullEvents && this.props.event.diffs) {
+ var changes = this.props.event.diffs.map(this.buildDiff);
+ return ({changes.length === 0 ? '(no changes)' : changes}
)
+ } else {
+ return this.renderFullEventData();
+ }
+ },
+
+ buildDiff: function(diff, idx) {
+ var change;
+ var key = diff.path.join('.');
+
+ if (diff.lhs !== undefined && diff.rhs !== undefined) {
+ change = (
+ When | Action | -Data | ++ Data + | Author |
---|