Skip to content

Instantly share code, notes, and snippets.

@Schnodderbalken
Last active December 7, 2017 21:45
Show Gist options
  • Save Schnodderbalken/40114a2e500ae27864c6b4ae9688d3f2 to your computer and use it in GitHub Desktop.
Save Schnodderbalken/40114a2e500ae27864c6b4ae9688d3f2 to your computer and use it in GitHub Desktop.
Socket.io adapter for Ember.js - tested with ember 2.16.2, node 6.4.0 and npm 5.5.1. This is based on this gist https://gist.github.com/carlwoodward/8659793. The `websocket-connection-service` is nothing but a wrapper around socket.io. So in order to make it work: `npm install socket.io-client --save`. Can easily be adapted to websocket adapter.
import { Promise as EmberPromise } from 'rsvp';
import { inject as service } from '@ember/service';
import DS from 'ember-data';
import Ember from 'ember';
const {Logger} = Ember;
export default DS.Adapter.extend({
websocketConnectionService: service('websocket-connection-service'),
callbacks: {},
initializeSocket: function(callback) {
if(this.get('websocketConnectionService').connected()) {
callback();
return;
}
this.registerEventHandlerForWebsocket(callback);
},
registerEventHandlerForWebsocket(callback) {
var connectionService = this.get('websocketConnectionService');
var adapter = this;
connectionService.on('connect', function() {
adapter.reactOnConnection();
callback();
});
connectionService.on('connect_error', function(data) {adapter.reactOnDisconnection.call(adapter, data)});
connectionService.on('response', function(data) {adapter.onMessage.call(adapter, data)});
},
reactOnDisconnection: function() {
Logger.info('disconnected');
// Once the user should see the connection status this becomes important
},
reactOnConnection: function() {
Logger.info('connected');
// Once the user should see the connection status this becomes important
},
buildRequestObject: function(requestType, query, uuid) {
var request_object = {
meta: {
'request-method': requestType,
'data-type': query.type,
'uuid': uuid
}
};
if(query.data) {
request_object.content = query.data;
}
return request_object;
},
doSocketRequest: function (store, requestType, query) {
var adapter = this;
return new EmberPromise(function (resolve, reject) {
adapter.get('initializeSocket').call(adapter, function() {
if(requestType === null) {
reject({message: 'No type specified in the request.'});
}
if(adapter.get('websocketConnectionService').connected() === false) {
reject({ message : 'websocket is not available for requests'});
}
try {
var uuid = adapter.generateUuid();
var request_object = adapter.buildRequestObject(requestType, query, uuid);
var success = function(data) {
resolve(data);
};
var error = function(data) {
reject(data);
};
var callback = { success: success, error: error };
adapter.callbacks[uuid] = callback;
adapter.get('websocketConnectionService').emit('request', request_object);
} catch (e) {
Logger.error(e);
reject({ message : 'websocket is not available for requests. Exception: ' + e});
}
});
});
},
createRecord: function(store, type, snapshot) {
var data = this.serialize(snapshot);
if(snapshot.id) {
data.id = snapshot.id;
}
var requestData = { type : type.modelName, data: data };
return this.doSocketRequest(store, 'post', requestData);
},
findRecord: function (store, type, id) {
return this.doSocketRequest(store, 'get', {
type : type.modelName,
data : { id : id }
});
},
findAll: function (store, type) {
var result = this.doSocketRequest(store, 'get', {
type : type.modelName
});
return result;
},
query: function (store, type, query) {
return this.doSocketRequest(store, 'get', {
type : type.modelName,
data : query
});
},
updateRecord: function(store, type, snapshot) {
if(!snapshot.record.hasOwnProperty('_changedProperties')) {
return this.doPatch(store, type, snapshot);
}
return this.doPut(store, type, snapshot);
},
doPatch: function(store, type, snapshot) {
var data = this.serialize(snapshot);
data.id = snapshot.id;
var requestData = { type : type.modelName, data: data };
return this.doSocketRequest(store, 'patch', requestData);
},
doPut: function(store, type, snapshot) {
// This does not work for relationships but hopefully ember will support it in the future:
/*for (var key in snapshot._changedAttributes) {
data[key] = snapshot._changedAttributes[key][1];
}*/
var data = {
id: snapshot.id
}
if(snapshot.record._changedProperties.length === 0) {
return snapshot;
}
var serializedSnapshot = this.serialize(snapshot);
snapshot.record._changedProperties.forEach(function(propertyName) {
data[propertyName] = serializedSnapshot[propertyName];
});
var requestData = { type : type.modelName, data: data };
return this.doSocketRequest(store, 'patch', requestData);
},
deleteRecord: function(store, type, snapshot) {
var data = this.serialize(snapshot);
if(snapshot.id) {
data.id = snapshot.id;
}
var requestData = { type : type.modelName, data: data };
return this.doSocketRequest(store, 'delete' ,requestData);
},
generateUuid: function() {
var date = new Date().getTime();
var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(character) {
var random = (date + Math.random() * 16) % 16 | 0;
date = Math.floor(date/16);
return (character === "x" ? random : (random & 0x7 | 0x8)).toString(16);
});
if(!(uuid in this.callbacks)) {
return uuid;
}
return this.generateUuid();
},
onMessage: function(data) {
var data_object = {};
try{
data_object = JSON.parse(data);
}
catch(e) {
Logger.error(e);
return;
}
if(!('meta' in data_object)) {
return;
}
if(!('uuid' in data_object.meta)) {
return;
}
if(!(data_object.meta.uuid in this.callbacks)) {
return;
}
if(data_object.meta.code !== 200) {
var message = data_object.meta.message;
var error_log = 'Problem with ' + data_object.meta.request_method;
if(data_object.meta['data-type']) {
error_log += ' ' + data_object.meta['data-type'];
}
if (message) {
error_log += ': "' + message + '"';
}
var error_callback = this.callbacks[data_object.meta.uuid].error;
delete this.callbacks[data_object.meta.uuid];
Logger.error(error_log);
error_callback(new Error(error_log));
return;
}
var success_callback = this.callbacks[data_object.meta.uuid].success;
delete this.callbacks[data_object.meta.uuid];
success_callback(data_object.content);
}
});
import Service from '@ember/service';
import config from '../config/environment';
import io from 'npm:socket.io-client';
export default Service.extend({
socketConnection: null,
init() {
this.getConnection();
},
getConnection: function() {
if(this.socketConnection === null) {
this.set('socketConnection', io.connect(config.APP.websocketHost + ':' + config.APP.websocketPort + '/'));
}
return this.socketConnection;
},
on: function(eventType, callback, context) {
this.get('socketConnection').on(eventType, callback, context);
},
emit: function(eventType, data) {
this.get('socketConnection').emit(eventType, data);
},
connected: function() {
return this.get('socketConnection').connected;
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment