Updating client model without a full refresh - ember.js

Is there a way to add records to a client model and indicate to Ember that these records are already on the server side?
So, for instance if my Person model has records for "Peter" and "Paul" but then later the server adds "Mary" and my client becomes aware of that in a non ember-data sort of way. I'd like to be able to have the client add "Mary" in a way that will not cause state problems with the subsequent interactions.
For those that must know the "use case" ... I'm trying to:
have the first request to findAll() for a given model to pull the full set of data back from the server
have subsequent requests in a session call a custom AJAX request that only returns differences to the resultset since that last request
I want to be able to push these differences into the client model without screwing up it's "state"

Sometimes we create and update EmberData records on the client with data from websockets. In this case, the changes are already on the server, so we just want to make the changes on the client without changing state, etc, exactly as you describe.
When we create new records on the client, we push them into the store, e.g.:
this.store.push('post', { id: 1, body: 'Ember!' });
And, when we update existing records on the client, we update them in the store, e.g.:
this.store.update('post', { id: 1, hidden: true });

Related

strongloop/loopback - Change connection string based on route value

My application's users are geographically dispersed and data is stored in various regions. Each region has it's own data center and database server.
I would like to include a route value to indicate the region that the user wants to access and connect to, as follows:
/api/region/1/locations/
/api/region/2/locations/
/api/region/3/locations/
Depending on the region passed in, I would like to change the connection string being used. I assume this can be performed somewhere in the middleware chain, but don't know where/how. Any help is appreciated!
What should not be done
Loopback provides a method MyModel.attachTo (doesnt seem to be documented, but a reference to it is made there ).
But since it is a static method, it affects the entire Model, not a single instance.
So for this to work on a per-request basis, you must switch the DB right before the call to the datasource method, to make sure nothing async starts in between. I don't think this is possible.
This is an example using an operation hook (and define all datasources, include dbRegion1 below in datasources.json)
Bad, don't that below. Just for reference
Region.observe('loaded', function filterProperties(ctx, next) {
app.models.Region.attachTo(app.dataSources.dbRegion1);
}
But then you will most likely face concurrency issues when your API receives multiple requests in a short time.
(Another way to see it is that the server is no longer truly stateless, execution will not depend only on inputs but also on a shared state).
The hook may set region2 for request 2 while the method called after the hook was expecting to use region1 for request 1. This will be the case if something async is triggered between the hook and the actual call to the datasource method.
So ultimately, I don't think you should do that. I'm just putting it there because some people have recommended it in other SO posts, but it's just bad.
Potential option 1
Build an external re-routing server, that will re-route the requests from the API server to the appropriate region database.
Use the loopback-connector-rest in your API server to consume this microservice, and use it as a single datasource for all your models. This provides abstraction over database selection.
Then of course there is still the matter of implementing the microservice, but maybe you can find some other ORM than loopback's that will support database sharding, and use it in that microservice.
Potential option 2
Create a custom loopback connector that will act as router for MySQL queries. Depending on region value passed inside the query, re-route the query to the appropriate DB.
Option 3
Use a more distributed architecture.
Write a region-specific server to persist region-specific data.
Run for instance 3 different servers, each one configured for a region.
+ 1 common server for routing
Then build a routing middleware for your single user-facing REST api server.
Basic example:
var express = require('express');
var request = require('request');
var ips = ['127.0.0.1', '127.0.0.2'];
app.all('/api/region/:id', function (req, res, next) {
console.log('Reroute to region server ' + req.params.id);
request(ips[req.params.id], function (error, response, body) {
if (err) return next(err);
next(null, body);
});
});
Maybe this option is the easiest to do

Ember data doesn't send id when triggering PUT request

Does anybody know how can I handle this?
So, I execute the following code in my controller:
this.get('newEvent').save();
It gets my data and sends it to the server. Here's some data
Request URL:http://localhost:1337/api/v1/events/53a9cfee701b870000dc1d01
Data to send:
event:{
date: "Mon, 18 Aug 2014 07:00:00 GMT"
host: "Host"
repeatable: true
title: "Event name"
}
But for success I need to know the model id on my server which is normally included to the event object I send. Do you know any solutions? By the way DELETE request doesn't exclude id from the event object
You can easily override the saving behavior to include the id by overwriting the updateRecord hook in the RestAdapter: https://github.com/emberjs/data/blob/master/packages/ember-data/lib/adapters/rest_adapter.js#L457 to pass includeId: true the same way createRecord does https://github.com/emberjs/data/blob/master/packages/ember-data/lib/adapters/rest_adapter.js#L436
There are a few ways of handling this, and it comes down to personal preference. But I would recommend altering your backend code to pull the ID from the URL. Normally when you define the put request, you do it like /events/:event_id. If you did it this way on the backend, there's no need for it to be in your events object that's send over.
api/v1/events/53a9cfee701b870000dc1d01
You can capture this on the server by looking at the URL parameter that's sent over. If you're using Express on the backend, you can get the ID straight from the URL with little effort. I can't comment yet, but let me know your backend structure, and I can point you in the right direction. Possibly include the PUT request you wrote on the back end as a point of reference.
The other thing that you can do(not recommended, but will work) is to store a temporary id before doing the save.
var id = this.get('newEvent');
this.get('newEvent').set('temp_id', id).save();
And then you can access the temp_id on the server call without checking the param_id on the server call.

Trigger REST adapter to push records to store

I have the following setup:
The ember frontend is connected to a websocket server
The backend pushes records (real-time data) via websocket to the clients, as stringified JSON
The client receives the data, and must now update the store with this new data received
The problem that I have is that I do not know to process the raw JSON data to make it compatible to what is in the store. I can of course parse the json (JSON.parse), but this is just a part of what the REST adapter is doing.
When doing a normal REST request, more or less what happens is that:
server generates reply -> REST adapter converts it -> it gets pushed to the store
But now, since I am not using the REST adapter to process this data (because this is not a request triggered in the client side, but a notification coming from the server side), I do not know how to trigger the normal processing that the REST adapter performs.
How can I trigger the REST adapter programmatically? Can I pass it the stringified JSON coming from the websockets server?
Is it possible to hook the REST adapter to a generic websockets callback, where the only thing I have is the stringified JSON coming from the websockets server?
This is the code that I have (inspired in web2py)
function connect_websocket() {
console.log('connect_websocket > connecting to server');
var callback = function(e) {
var data = JSON.parse(e.data);
console.log('Data received > data=%o', data);
// TODO;
// - process the data as the REST adapter would do, and push new / updated records to the store.
// - handle record deletes too (how?)
};
if(!$.web2py.web2py_websocket('ws://127.0.0.1:8888/realtime/mygroup', callback)) {
alert('html5 websocket not supported by your browser, try Google Chrome');
}
}
I have taken a look at EmberSockets, but as far as I understand that does not offer a generic method of updating records in the store, but just a very specialized way of updating properties in the controllers (which requires a lot of configuration too).
What I am looking for is a generic method of triggering the ember REST adapter from a websockets server. Is there such a thing?

Subscribing to updates via Faye/websockets results in duplicate records

Update
Demo: http://jsbin.com/ogorab/311/edit
I'm trying to build a simple chat room that updates in realtime using Faye/Websockets. Messages are posted using regular REST, but there is also a subscription via Faye to /messages/created, which uses store.pushPayload to push the new messages.
Now the following scenario happens and I can see where it goes wrong but I have no clue how to solve it:
User submits chat message
ChatController handles the submit, calls createRecord with the chat message, and subsequently #save
The chat messages is instantly shown in the chat (triggered by createRecord). Note that no id has been assigned yet.
A REST request is send to the server
The server first publishes the message to Faye
The server responds to the REST request
Before the ajax call is resolved, a message has arrived at /messages/created
The message is again inserted in the view (it should be merged with the original message of course, but that one still hasn't been assigned an id)
The ajax call is resolved, and the id of the original message is assigned.
This results in duplicate messages, in the following order:
[message via createRecord, will resolve via ajax response]
[message inserted via pushPayload/Faye]
I hope you can understand so far. A solution would be to have Faye wait for the save call to resolve before pushing the payload. Unfortunately I don't have a reference to the record that is being saved (happens in a controller, faye subscription is set up in ApplicationRouter).
Also I would like this to work in a generic way:)
Finally found a solution for this, but other suggestions are still welcome.
Turns out that Store#didSaveRecord updates the id after the record is saved. By overriding this method (and then calling super, in that order), we can first check if a record for that id already exists:
App.Store = DS.Store.extend
didSaveRecord: (record, data) ->
# This will remove any existing records with the same id
#getById(record.constructor, data.id)?.unloadRecord()
#_super(record, data)

How to reload an ember data record?

I'm using the new router and ember data rev 11.
I have a need to force ember-data to re-load data for a record from the server. Using App.MyRecord.find(2) in setInterval function loads the data from the client local store.
How can I reload the data from the server?
I just pushed record.reload() to Ember Data. This will ask the adapter to reload the data from the server and update the record with the new data.
Constraints:
You can only call reload() on a record if it has completed loading and has not yet been modified. Otherwise, the returned data will conflict with the modified data. In the future, we will add support for a merge hook to address these sorts of conflicts, which will allow reload() in more record states.
If you call reload() and change or save the record before the adapter returns the new data, you will get an error for the same reason. The error currently looks something like Attempted to handle event 'reloadRecord' on <Person:ember263:1> while in state rootState.loaded.updated.uncommitted.. Basically, this means that your record was in the "updated but uncommitted" state, and you aren't allowed to call reload() outside of the "loading and unmodified" state.