Using sails.io pubsub works with shortcuts, but not with RESTful record creation - sails.js

I'm having an issue where I'm getting events from the sails resourceful pubsub when I create a record with the shortcut routes, but not with the RESTful routes.
In my client code, I have a sails socket that I listen to for the model:
io.socket.on('users', function(event){console.log(event);})
If I use a shortcut route to create a record (http://localhost:1337/users/create?name=test), I get the callback in the console as expected:
>{verb: "created", data: {…}, id: 44}
However, if I use the socket to post from the client, the callback never fires.
io.socket.post('/users', { name: 'test' });
The record is created in the DB, and even more confusing is that the Sails server log says its publishing the message:
silly: Published message to sails_model_create_users : { verb: 'created',
data:
{ name: 'test',
createdAt: '2017-10-09T02:58:18.218Z',
updatedAt: '2017-10-09T02:58:18.218Z',
id: 44 },
id: 44 }
I'm the using generic blueprints, sails is v0.12.14. Any ideas what I'm doing wrong?

I figured out why I wasn't getting the events back on the pubsub. Sails excludes the socket that sent the req from the event. You can get the result from the post callback, but this breaks my nice dataflow. So I ended up using io.sails.connect() to create 2 sockets, one for the crud calls and one for the pubsib.

Related

How to connect to Actions builder project to update type entities?

I've got a project build migrated from AOG + Dialogflow to Actions Builder. I need to update (or insert new) type entries with the REST API.
To do that action I've found an endpoint, that gives the ability to update the whole project along with entities:
https://developers.google.com/assistant/actions/api/reference/rest/v2/projects.draft/write
However, I can't connect to that endpoint due to 401 error. Before that I've tried to emulate a similar request to another endpoint, which allows reading the project:
https://developers.google.com/assistant/actions/api/reference/rest/v2/projects.draft/read
Obviously, I've got the same error here.
Also, I've found this repo - https://github.com/actions-on-google/assistant-actions-nodejs
which adds a wrapper for easier manipulation with the REST API, but it also doesn't contain any information on how to properly authorize to get access to an app.
Can please somebody suggest how authorization should be done to start using this REST API?
Using Actions Builder, the type entities can be updated directly in your webhook calls rather than calling an API in parallel.
Look up the names of the types you want to override in Actions Builder and set the conv.session.typeOverrides field in your response.
Here's a code example of how it might be done:
const app = conversation()
// `app.middleware` will run on every invocation call
app.middleware(conv => {
// ...
// Obtain `trackTitles` and `trackGenres`
// ...
conv.session.typeOverrides = [{
name: 'track',
mode: Mode.TypeReplace,
synonym: {
entries: Array.from(trackTitles).map(title => ({
name: title,
synonyms: [title],
})),
},
}, {
name: 'genre',
mode: Mode.TypeReplace,
synonym: {
entries: Array.from(trackGenres).map(genre => ({
name: genre,
synonyms: [genre]
}))
}
}]
})

Sails pubsub how to subscribe to a model instance?

I am struggling to receive pubsub events in my client. The client store (reflux) gets the data from a project using its id. As I understand it this automatically subscribes the Sails socket for realtime events (from version 0.10), but I don't see it happening.
Here's my client store getting data from sails
(this is ES6 syntax)
onLoadProject(id) {
var url = '/api/projects/' + id;
io.socket.get(url, (p, jwres) => {
console.log('loaded project', id);
this.project = p;
this.trigger(p);
});
io.socket.on("project", function(event){
console.log('realtime event', event);
});
},
Then I created a test "touch" action in my project controller, just to have the modifiedAt field updated.
touch: function(req, res){
var id = req.param('id');
Project.findOne(id)
.then(function(project) {
if (!project) throw new Error('No project with id ' + id);
return Project.update({id: id}, {touched: project.touched+1});
})
.then(function(){
// this should not be required right?
return Project.publishUpdate(id);
})
.done(function() {
sails.log('touched ok');
res.ok();
}, function(e) {
sails.log("touch failed", e.message, e.stack);
res.serverError(e.message);
});
}
This doesn't trigger any realtime event in my client code. I also added a manual Project.publishUpdate(), but this shouldn't be required right?
What am I missing?
-------- edit ----------
There was a complication a result of my model touched attribute, since I set it to 'number' instead of 'integer' and the ORM exception wasn't caught by the promise error handling without a catch() part. So the code above works, hurray! But the realtime events are received for every instance of Project.
So let me rephrase my question:
How can I subscribe the client socket to an instance instead of a model? I could check the id on the client side and retrieve the updated instance data but that seems inefficient since every client receives a notification about every project even though they only should care about a single one.
----- edit again ------
So nevermind. The reason I was getting updates from every instance is simply because at the start of my application I triggered a findAll to get a list of available projects. As a result my socket got subscribed for all of them. The workaround would be to either initiate that call via plain http instead of a socket, or use a separate controller action for retrieving the list (therefor bypassing the blueprint route). I picked the second option because in my case it's silly to fetch all project data prior to picking one.
So to answer my own question. The reason I was getting updates from every instance is simply because at the start of my application I triggered a findAll to get a list of available projects. As a result my socket got subscribed for all of them.
The workaround would be to either initiate that call via plain http instead of a socket, or use a separate controller action for retrieving the list (therefor bypassing the blueprint route). I picked the second option because in my case it's silly to fetch all resources data prior to selecting one.
Here's the function I used to list all resources, where I filter part of the data which is not relevant for browsing the list initially.
list: function(req, res) {
Project.find()
.then(function(projects) {
var keys = [
'id',
'name',
'createdAt',
'updatedAt',
'author',
'description',
];
return projects.map(function(project){
return _.pick(project, keys);
});
})
.catch(function (e){
res.serverError(e.message);
})
.done(function(list){
res.json(list);
}, function(e) {
res.serverError(e.message);
});
},
Note that when the user loads a resource (project in my case) and then switches to another resource, the client is will be subscribed to both resources. I believe it requires a request to an action where you unsubscribe the socket explicitly to prevent this. In my case this isn't such a problem, but I plan to solve that later.
I hope this is helpful to someone.

Make a wirecloud query using NGSI API

I'm trying to make a query using NGSI API in my Wire Cloud widget but always fails and I don't receive anything:
var descubrimiento = connection.query([{
isPattern: true,
id: MashupPlatform.prefs.get('idfuente')
}],
null,
{
flat: true
}
);
connection is the object where I have the connection with the context broker and it works fine. Also if I make the query with the NGSI10 RESTful API via RESTclient I receive the data that I want but with the wirecloud NGSI API in my widget it is imposible to make the query.
Someone has this problem too?
Take a look at this tutorial about how to use the Orion Context Broker from WireCloud.
The main problem is that you're assuming the method the query response is returned synchronously while in reality is returned asynchronously. For being able to read the returned data, you need to pass a onSuccess callback. This callback will be called as soon the response from the Orion server is available. The data returned by Orion will be passed as the first parameter of the onSuccess callback function (see the referenced documentation for examples about how the returned data is formatted). E.g:
connection.query([{
isPattern: true,
id: MashupPlatform.prefs.get('idfuente')
}],
null,
{
flat: true,
onSuccess: function (descubrimiento) {
...
}
}
);

store in emberjs not loading the latest data from the server (in case of deletion at the server side)

There is a /users url in my ember application. I am using RESTAdapter to fetch data from server. When a template showing list of users is loaded, it loads the latest updated data from the server (e.g. if I make changes in one of the row in users table, say changing the name of the user, I get that change reflected at front-end as well). But if I delete the user from database, I still get that user in the users' list at the front-end.
The model-hook for users route simply returns the list of users from server:
this.store.find('user').then(onSuccess, onError);
When I try to do any operation on deleted user (say updating it's name at front-end), I get an error (which is obvious as the user is no more in the database and server responds with an appropriate error). How to force emberjs to load the list of users that are present in the database and not just in local ember-data store? Also, why local ember-data store is in-sync with database for updation (and addition as well) but not for deletion?
The problem is explained at the github issue.
The store has a cache for each model type, then when the store fetch data with the _findAll method method, it does not assume that is returning all the data, so the following steps are applied
store.pushMany(type, payload); // push the server response data to the local store
store.didUpdateAll(type); // update record with the most up-to-date record
return store.all(type); // return all the record in the local store
It will not take in consideration any record deletion as it is discussed in the issue, in this case the new response data would be added to the local store if a record with its same primaryKey cannot be found or in the other case, it will update the local copy.
I created a test to check the defined behaviour:
test('store.findAll does not manage record deletion', function() {
$.mockjaxClear();
$.mockjax({ url: '/categories', dataType: 'json', responseText: {
categories:[
{id: 1, name: "Name1"},
{id: 2, name: "Name2"}
]}});
var store = this.store();
stop();
store.find('category').then(function(result) {
equal(result.get('length'), 2, 'the new item returned with ajax is added to the local store');
$.mockjaxClear();
$.mockjax({ url: '/categories', dataType: 'json', responseText: {
categories:[
{id: 3, name: "Name3"},
]}});
store.find('category').then(function(result) {
equal(result.get('length'), 3);
$.mockjaxClear();
$.mockjax({ url: '/categories', dataType: 'json', responseText: {
categories:[
{id: 3, name: "Name4"},
]}});
store.find('category').then(function(result) {
equal(result.get('length'), 3);
equal(result.objectAt(2).get('name'), 'Name4', 'the old item returned with the new ajax request updates the local store');
start();
});
});
});
});
As you pointed something like find('modelName', {}) would solve your issue:
this.store.find('user', {})
It seems that this is a known bug in Emberjs. The workaround that I found (accidently) was : supply any arbitrary parameter to find() method and the data loaded will be the same one fetched from the server.
Below an example shows that xyz parameter is being passed with value abc.
this.store.find('user', {xyz:"abc"}).then(onSuccess, onError);
I am not sure if this is THE RIGHT WAY to do it, but it works for me. The other solutions would be those which are mentioned in the link (like sending some metadata from server i.e. maintaining the list of deleted records).
But I still couldn't figure out why this happens in case of DELETE only (and not in case of POST and PUT).
I am not sure if this really is a problem or I just do not understand EmberJS that much. Any help or comments on the same would be much appreciated.
To overcome this problem, you can unload all of the records from the store and then call find():
this.store.unloadAll('user');
this.store.find('user').then(
function(d) {
console.log(d.get('length')); // store now only contains the users returned from the server
}
);

SAP UI5 odata create fail

i'm working with SapUi5 to build a webapp which connects to our Netweaver Gateway. This App consumes the data successfully, i only have problems to create objects with the service.
The response data i get is
500
Internal Server Error
content-type
application/xml
-
-
001560AA0E081DEB8CA398CC1690D406
Error while parsing an XML stream
-
52FB96CF506729E0E1000000C0A8EA2A
The Gateway error log says
Exception /IWCOR/CX_BAD_REQUEST occured.
When i insert data with the Gateway Client everything works fine.
EDIT
My Object is created like this
var testObject = {
smtp_adr: "ui5#test.de",
first_name: "SapUI5",
last_name: "test",
nr: 9999
};
and i upload it with
oModel.create("/MyService", testObject, null, false, null,function() {
alert("Create successful");
});
500 internal server error is related to payload. You are not passing the data properly to back-end. Please check the data that you are passing from front-end.
The problem is stated in your error message "bad request" ... oModel.create allows 3 parameters (Source)
sPath e.g. /MyService
oData e.g. testObject
mParameters?
You are passing 4 parameters, which leads to a "BAD_REQUEST" ...
Please see this, where its explained how to pass data: SAPUI5 oModel.create() - how to post data to the SAP backend?
Do you have access to the gateway box through SAP GUI can you check the error logs there?
The error your are getting around parsing XML stream most often happens when there is either an additional field in your create model or there is a type mismatch. can you try passing in
var testObject = {
smtp_adr: "ui5#test.de",
first_name: "SapUI5",
last_name: "test",
nr: "9999"
};
And see if that makes a difference most of the times I have seen this the type mismatch has been the issue.