UNPKG

webgme-engine

Version:

WebGME server and Client API without a GUI

527 lines (455 loc) 20.6 kB
/*globals define*/ /*eslint-env node, browser*/ /** * @author pmeijer / https://github.com/pmeijer */ // socket.io-client // define([ 'common/EventDispatcher', 'common/storage/constants', 'q' ], function (EventDispatcher, CONSTANTS, Q) { 'use strict'; function WebSocket(ioClient, mainLogger, gmeConfig) { var self = this, logger = mainLogger.fork('WebSocket'), forcedDisconnect, beenConnected = false; self.socket = null; self.userId = null; self.serverVersion = null; self.ioClient = ioClient; self._handleWebsocketRouterMessage = () => {}; logger.debug('ctor'); EventDispatcher.call(this); function emitWithToken(data, eventName, callback) { logger.debug('emitting event', eventName, {metadata: data}); data.webgmeToken = ioClient.getToken(); if (callback) { self.socket.emit(eventName, data, callback); } else { return Q.ninvoke(self.socket, 'emit', eventName, data); } } this.connect = function (networkHandler) { logger.debug('Connecting via ioClient.'); forcedDisconnect = false; ioClient.connect(function (err, socket_) { if (err) { networkHandler(err); return; } self.socket = socket_; self.socket.on('connect', function () { var i, sendBufferSave = []; if (beenConnected) { logger.debug('Socket got reconnected.'); networkHandler(null, CONSTANTS.RECONNECTING); // #368 for (i = 0; i < self.socket.sendBuffer.length; i += 1) { // Clear all makeCommit and document operations. // If pushed - they would be emitted back to the socket! if (self.socket.sendBuffer[i].data[0] === 'makeCommit' || self.socket.sendBuffer[i].data[0] === CONSTANTS.DOCUMENT_OPERATION) { logger.debug('Removed makeCommit from sendBuffer...'); } else { sendBufferSave.push(self.socket.sendBuffer[i]); } } if (self.socket.receiveBuffer.length > 0) { // TODO: In which cases is this applicable?? logger.debug('receiveBuffer not empty after reconnect'); } self.socket.sendBuffer = sendBufferSave; self.socket.emit('getConnectionInfo', {webgmeToken: ioClient.getToken()}, function (err, info) { if (err) { networkHandler(new Error('Could not get info on reconnect')); } else { if (self.serverVersion === info.serverVersion) { networkHandler(null, CONSTANTS.RECONNECTED); } else { logger.error('Got reconnected to different webgme version (old !== new)', self.serverVersion, '!==', info.serverVersion); networkHandler(null, CONSTANTS.INCOMPATIBLE_CONNECTION); } } }); } else { logger.debug('Socket got connected for the first time.'); beenConnected = true; self.socket.emit('getConnectionInfo', {webgmeToken: ioClient.getToken()}, function (err, info) { if (err) { networkHandler(new Error('Could not get info on connect')); } else { self.userId = info.userId || gmeConfig.authentication.guestAccount; self.serverVersion = info.serverVersion; networkHandler(null, CONSTANTS.CONNECTED); } }); } }); self.socket.on('disconnect', function () { logger.debug('Socket got disconnected!'); networkHandler(null, CONSTANTS.DISCONNECTED); // When the server is shut-down the skipReconnect is set to false // create a new socket connect. if (self.socket.io.skipReconnect === true && forcedDisconnect === false) { self.connect(networkHandler); } }); self.socket.on(CONSTANTS.JWT_ABOUT_TO_EXPIRE, function (data) { data.etype = CONSTANTS.JWT_ABOUT_TO_EXPIRE; logger.debug('JWT_ABOUT_TO_EXPIRE event', {metadata: data}); networkHandler(null, CONSTANTS.JWT_ABOUT_TO_EXPIRE); }); self.socket.on(CONSTANTS.JWT_EXPIRED, function (data) { data.etype = CONSTANTS.JWT_EXPIRED; logger.debug('JWT_EXPIRED event', {metadata: data}); networkHandler(null, CONSTANTS.JWT_EXPIRED); }); self.socket.on(CONSTANTS.PROJECT_DELETED, function (data) { data.etype = CONSTANTS.PROJECT_DELETED; logger.debug('PROJECT_DELETED event', {metadata: data}); self.dispatchEvent(CONSTANTS.PROJECT_DELETED, data); }); self.socket.on(CONSTANTS.PROJECT_CREATED, function (data) { data.etype = CONSTANTS.PROJECT_CREATED; logger.debug('PROJECT_CREATED event', {metadata: data}); self.dispatchEvent(CONSTANTS.PROJECT_CREATED, data); }); self.socket.on(CONSTANTS.BRANCH_CREATED, function (data) { data.etype = CONSTANTS.BRANCH_CREATED; logger.debug('BRANCH_CREATED event', {metadata: data}); self.dispatchEvent(CONSTANTS.BRANCH_CREATED + data.projectId, data); }); self.socket.on(CONSTANTS.BRANCH_DELETED, function (data) { data.etype = CONSTANTS.BRANCH_DELETED; logger.debug('BRANCH_DELETED event', {metadata: data}); self.dispatchEvent(CONSTANTS.BRANCH_DELETED + data.projectId, data); }); self.socket.on(CONSTANTS.BRANCH_HASH_UPDATED, function (data) { data.etype = CONSTANTS.BRANCH_HASH_UPDATED; logger.debug('BRANCH_HASH_UPDATED event', {metadata: data}); self.dispatchEvent(CONSTANTS.BRANCH_HASH_UPDATED + data.projectId, data); }); self.socket.on(CONSTANTS.TAG_CREATED, function (data) { data.etype = CONSTANTS.TAG_CREATED; logger.debug('TAG_CREATED event', {metadata: data}); self.dispatchEvent(CONSTANTS.TAG_CREATED + data.projectId, data); }); self.socket.on(CONSTANTS.TAG_DELETED, function (data) { data.etype = CONSTANTS.TAG_DELETED; logger.debug('TAG_DELETED event', {metadata: data}); self.dispatchEvent(CONSTANTS.TAG_DELETED + data.projectId, data); }); self.socket.on(CONSTANTS.BRANCH_UPDATED, function (data) { logger.debug('BRANCH_UPDATED event', {metadata: data}); self.dispatchEvent(self.getBranchUpdateEventName(data.projectId, data.branchName), data); }); self.socket.on(CONSTANTS.NOTIFICATION, function (data) { logger.debug('NOTIFICATION event', {metadata: data}); self.dispatchEvent(CONSTANTS.NOTIFICATION, data); }); self.socket.on(CONSTANTS.DOCUMENT_OPERATION, function (data) { logger.debug('DOCUMENT_OPERATION event', {metadata: data}); self.dispatchEvent(self.getDocumentUpdatedEventName(data), data); }); self.socket.on(CONSTANTS.DOCUMENT_SELECTION, function (data) { logger.debug('DOCUMENT_SELECTION event', {metadata: data}); self.dispatchEvent(self.getDocumentSelectionEventName(data), data); }); self.socket.on('websocketRouterMessage', function (data) { logger.debug('websocketRouterMessage', {metadata: data}); self._handleWebsocketRouterMessage(data.routerId, data.messageType, data.payload); }); }); }; this.disconnect = function () { forcedDisconnect = true; self.socket.disconnect(); beenConnected = false; //This is a forced disconnect from the storage and all listeners are removed }; // watcher functions this.watchDatabase = function (data, callback) { return emitWithToken(data, 'watchDatabase') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.watchProject = function (data, callback) { return emitWithToken(data, 'watchProject') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.watchBranch = function (data, callback) { return emitWithToken(data, 'watchBranch') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; // model editing functions this.openProject = function (data, callback) { return emitWithToken(data, 'openProject') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.closeProject = function (data, callback) { return emitWithToken(data, 'closeProject') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.openBranch = function (data, callback) { return emitWithToken(data, 'openBranch') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.closeBranch = function (data, callback) { return emitWithToken(data, 'closeBranch') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.makeCommit = function (data, callback) { return emitWithToken(data, 'makeCommit') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.loadObjects = function (data, callback) { return emitWithToken(data, 'loadObjects') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.loadPaths = function (data, callback) { return emitWithToken(data, 'loadPaths') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.setBranchHash = function (data, callback) { return emitWithToken(data, 'setBranchHash') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.getBranchHash = function (data, callback) { return emitWithToken(data, 'getBranchHash') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.squashCommits = function (data, callback) { return emitWithToken(data, 'squashCommits') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; // REST like functions this.getProjects = function (data, callback) { return emitWithToken(data, 'getProjects') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.deleteProject = function (data, callback) { return emitWithToken(data, 'deleteProject') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.createProject = function (data, callback) { return emitWithToken(data, 'createProject') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.transferProject = function (data, callback) { return emitWithToken(data, 'transferProject') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.duplicateProject = function (data, callback) { return emitWithToken(data, 'duplicateProject') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.getBranches = function (data, callback) { return emitWithToken(data, 'getBranches') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.createTag = function (data, callback) { return emitWithToken(data, 'createTag') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.deleteTag = function (data, callback) { return emitWithToken(data, 'deleteTag') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.getTags = function (data, callback) { return emitWithToken(data, 'getTags') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.getCommits = function (data, callback) { return emitWithToken(data, 'getCommits') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.getHistory = function (data, callback) { return emitWithToken(data, 'getHistory') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.getLatestCommitData = function (data, callback) { return emitWithToken(data, 'getLatestCommitData') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.getCommonAncestorCommit = function (data, callback) { return emitWithToken(data, 'getCommonAncestorCommit') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; //temporary simple request / result functions this.simpleRequest = function (data, callback) { var deferred = Q.defer(); emitWithToken(data, 'simpleRequest', function (errStr, result) { var err; if (errStr) { err = new Error(errStr); if (result) { // webgme #1570 For failed plugin executions we need the result details err.result = result; } deferred.reject(err); } else { deferred.resolve(result); } }); return deferred.promise.nodeify(callback); }; this.simpleQuery = function (workerId, data, callback) { return Q.reject(new Error('Not implemented!')).nodeify(callback); }; this.sendNotification = function (data, callback) { return emitWithToken(data, 'notification') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; // OT handling this.watchDocument = function (data, callback) { return emitWithToken(data, 'watchDocument') .catch(function (err) { return Q.reject(new Error(err)); }) .nodeify(callback); }; this.sendDocumentOperation = function (data, callback) { emitWithToken(data, CONSTANTS.DOCUMENT_OPERATION, function (err, res) { if (err) { callback(new Error(err)); } else { callback(null, res); } }); }; this.sendDocumentSelection = function (data, callback) { emitWithToken(data, CONSTANTS.DOCUMENT_SELECTION, function (err, res) { if (err) { callback(new Error(err)); } else { callback(null, res); } }); }; // Helper functions this.getBranchUpdateEventName = function (projectId, branchName) { return CONSTANTS.BRANCH_UPDATED + projectId + CONSTANTS.ROOM_DIVIDER + branchName; }; this.getDocumentUpdatedEventName = function (data) { if (typeof data.docId === 'string') { return CONSTANTS.DOCUMENT_OPERATION + data.docId; } else { return [CONSTANTS.DOCUMENT_OPERATION + data.projectId, data.branchName, data.nodeId, data.attrName] .join(CONSTANTS.ROOM_DIVIDER); } }; this.getDocumentSelectionEventName = function (data) { if (typeof data.docId === 'string') { return CONSTANTS.DOCUMENT_SELECTION + data.docId; } else { return [CONSTANTS.DOCUMENT_SELECTION + data.projectId, data.branchName, data.nodeId, data.attrName] .join(CONSTANTS.ROOM_DIVIDER); } }; // Router websocket relay messaging this.sendWsRouterMessage = function (routerId, messageType, payload, callback) { const data = { routerId: routerId, messageType: messageType, payload: payload, }; return emitWithToken(data, 'websocketRouterMessage', (err, result) => { if (err) { callback(new Error(err), result); } else { callback(err, result); } }); }; this.onWebsocketRouterMessage = function (handleFn) { self._handleWebsocketRouterMessage = handleFn; }; } WebSocket.prototype = Object.create(EventDispatcher.prototype); WebSocket.prototype.constructor = WebSocket; return WebSocket; });