UNPKG

diffusion

Version:

Diffusion JavaScript client

190 lines (153 loc) 5.87 kB
var parseSelector = require('topics/topic-selector-parser'); var canonicalise = require('topics/topic-path-utils').canonicalise; var Services = require('services/services'); var DataTypes = require('data/datatypes'); var Emitter = require('events/emitter'); var Result = require('events/result'); var UpdateFailReason = require('../../topics/topics').UpdateFailReason; var CloseReason = require('client/close-reason'); function dataToBytes(d) { return d.$buffer.slice(d.$offset, d.$length); } function clearCache(cache, path) { var selector = parseSelector('?' + canonicalise(path) + '//'); cache.remove(selector); } function Updater(cid, dispatch) { var self = this; this.isClosed = false; var update = function(topic, value, getDataType) { var emitter = new Emitter(); var result = new Result(emitter); if (self.isClosed) { emitter.error(new Error('Updater is closed')); } else if (!topic) { emitter.error(new Error('Topic can not be null or empty')); } else if (value === undefined || value === null) { emitter.error(new Error('Update cannot be null')); } else { dispatch(emitter, cid, topic, value, getDataType); } return result; }; this.update = function(topic, value) { return update(topic, value, function(content) { return DataTypes.get(content); }); }; this.updateValue = function(topic, value, dataType) { return update(topic, value, function() { return dataType; }); }; } module.exports = function UpdateResponseHandler(internal, valueCache, topic, handler) { var UPDATE_SOURCE_DEREGISTRATION = internal.getServiceLocator().obtain(Services.UPDATE_SOURCE_DEREGISTRATION); var UPDATE_SOURCE_DELTA = internal.getServiceLocator().obtain(Services.UPDATE_SOURCE_DELTA); var UPDATE_SOURCE_SET = internal.getServiceLocator().obtain(Services.UPDATE_SOURCE_SET); var dispatch = function (emitter, cid, path, content, getDataType) { var callback = function (err, result) { if (err) { emitter.error(err); } else if (result.error) { emitter.error(new Error("Topic update error for topic " + path + " : " + result.error)); } else { emitter.emit('complete'); } }; if (internal.checkConnected(emitter)) { var datatype = getDataType(content); if (!datatype) { emitter.error(UpdateFailReason.INCOMPATIBLE_UPDATE); return; } var value = datatype.from(content); var prev = valueCache.get(path); if (prev) { var deltaType = datatype.deltaType("binary"); var delta = deltaType.diff(prev, value); if (delta === deltaType.noChange()) { callback(null, {}); return; } UPDATE_SOURCE_DELTA.send({ id: 0, cid: cid, path: path, bytes: dataToBytes(delta) }, callback); } else { UPDATE_SOURCE_SET.send({ cid: cid, path: path, bytes: dataToBytes(value) }, callback); } valueCache.put(path, value); } }; var state = 'init'; var close, updater; return { onOpen: function (cid) { close = function close() { var emitter = new Emitter(); var result = new Result(emitter); UPDATE_SOURCE_DEREGISTRATION.send({cid: cid}, function (err) { if (err) { internal.getConversationSet().discard(cid, err); emitter.error(err); } else { internal.getConversationSet().respond(cid, { old: state, current: 'closed' }); emitter.emit('complete'); } }); return result; }; }, onResponse: function (cid, change) { if (change.old !== state) { internal.getConversationSet().discard(cid, new Error("Inconsistent server/client update source state. Current: " + state + ", expected: " + change.old)); return false; } if (state === 'init' && change.current !== 'closed') { handler.onRegister(topic, close); } if (updater) { updater.isClosed = true; } state = change.current; switch (state) { case 'active' : updater = new Updater(cid, dispatch); clearCache(valueCache, topic); handler.onActive(topic, updater); return false; case 'standby' : handler.onStandBy(topic); return false; default : clearCache(valueCache, topic); handler.onClose(topic); return true; } }, onDiscard: function (cid, reason) { state = 'closed'; if (updater) { updater.isClosed = true; } clearCache(valueCache, topic); if (reason instanceof CloseReason) { handler.onClose(topic); } else { handler.onClose(topic, reason); } } }; };