I was browsing through the code of node-wit and came across this
const validateActions = (logger, actions) => {
if (typeof actions !== 'object') {
throw new Error('Actions should be an object. ' + learnMore);
}
if (!actions.send) {
throw new Error('The \'send\' action is missing. ' + learnMore);
}
Object.keys(actions).forEach(key => {
if (typeof actions[key] !== 'function') {
logger.warn('The \'' + key + '\' action should be a function.');
}
if (key === 'say' && actions[key].length > 2 ||
key === 'merge' && actions[key].length > 2 ||
key === 'error' && actions[key].length > 2
) {
logger.warn('The \'' + key + '\' action has been deprecated. ' + learnMore);
}
if (key === 'send') {
if (actions[key].length !== 2) {
logger.warn('The \'send\' action should accept 2 arguments: request and response. ' + learnMore);
}
} else if (actions[key].length !== 1) {
logger.warn('The \'' + key + '\' action should accept 1 argument: request. ' + learnMore);
}
});
return actions;
};
Notice the part where it says key===merge and the logger prints deprecated. Does this mean the merge action is deprecated? If yes, what would be the alternative way of handling multiple stories?
According to https://wit.ai/docs/http/20160526#post--converse-link, merge is deprecated as of July 27th 2017, when stories were deprecated. (https://wit.ai/blog/2017/07/27/sunsetting-stories) And since stories are deprecated there's no way of handling multiple stories; any state will have to be kept by you, through e.g. a database.
Related
I have a function that counts all the members that are going to do a event.
I have the registered_people key to count how many users are on this event. This key is updated +1 or -1 when someone adds itself to the /registrations/approved link.
This works very well. See the method below.
exports.reservation = functions.database.ref('/agenda/activitys/{year}/{month}/{day}/{time}/{event}/registrations/approved/{key}').onWrite((event) => {
var collectionRef = event.data.adminRef.parent.parent;
var countRef = collectionRef.parent.child('registered_people');
console.log("Fired of reservation watcher");
return countRef.transaction(function(current) {
if (event.data.exists() && !event.data.previous.exists()) {
return (current || 0) + 1;
}
else if (!event.data.exists() && event.data.previous.exists()) {
return (current || 0) - 1;
}
});
});
But my problem is when the admin deletes an event. Url /agenda/activitys/{year}/{month}/{day}/{time}/{event} gets deleted, and the method defined above gets triggered and writes data again to the url. How can I prevent that this method writes anything when an admin deletes the event?
And this code is not working:
if (event.data.previous.exists()) {
return;
}
Because when a user wants to sign out from an event the registered_people must be updated. With the code defined above the delete does not work anymore. So I need to check if the event is deleted.
First, you are running and old version of functions & admin, update to be sure your firebase-functions & firebase-admin are updated:
In your functions folder run:
npm install firebase-functions#latest --save
npm install firebase-admin#latest --save
Then your code should look like this:
exports.reservation = functions.database.ref('/agenda/activitys/{year}/{month}/{day}/{time}/{event}/registrations/approved/{key}').onWrite((change, context) => {
var collectionRef = change.after.ref.parent.parent;
var countRef = collectionRef.parent.child('registered_people');
let increment;
if (change.after.exists() && !change.before.exists()) {
increment = 1;
} else if (!change.after.exists() && change.before.exists()) {
return null;
} else {
return null;
}
return countRef.transaction((current) => {
return (current || 0) + increment;
}).then(() => {
return console.log('Counter updated.');
});
});
I have the following data structure:
/users
/{user_uid}
/lists
/{list_uid}
Using a cloud function, i'd like to be able to have a /list_count reference on the root of my database, to be able to easily track list count without having to do a fat client-side call to count them.
at the moment I have this implementation, which I find a bit ugly:
exports.countlists = functions.database.ref('/users/{uuid}/lists').onWrite(event => {
const ref = event.data.ref.parent.parent.parent.child('list_count');
return ref.transaction(current => {
if (event.data.exists() && !event.data.previous.exists()) {
return (current || 0) + 1;
}
else if (!event.data.exists() && event.data.previous.exists()) {
return (current || 0) - 1;
}
});
});
The issue being that I get an error in the firebase console:
Error serializing return value: TypeError: Converting circular structure to JSON
as Inlined said here:
The problem you're running into is that the promised value that ref.transaction returns isn't serializeable as JSON. The easiest way to fix this (before we fix it in the Firebase SDK) is to transform the value to something like null.
I think to fix your problem, do this:
exports.countlists = functions.database.ref('/users/{uuid}/lists').onWrite(event => {
let root = admin.database().ref(`users/${event.params.uuid}/lists/list_count`)
return root.transaction(function(current){
if (event.data.exists() && !event.data.previous.exists()) {
return (current || 0) + 1;
}
else{
return (current || 0) - 1;
}
}.then(() => null));
});
I am creating an insert script that does some business logic.
Basically, I want to check to see if a value in the inserted item exists in a table. But, it seems like if I find a problem Request.Send() doesn't stop execution and get an error.
I think there is an async issue here. I'm not 100% sure how to solve.
Is there a way to stop execution of the script?
if (item.memberType === 'Family' && item.primaryFamilyMember) {
table
.where({
memberNumber: item.primaryFamilyMember,
memberType: 'Family',
primaryFamilyMember: null })
.read({
success: function(results) {
if (results.length == 0) {
request.respond(statusCodes.BAD_REQUEST,
'Invalid Primary Family Member specified.');
console.error('Invalid Primary Family Member specified:' + item.primaryFamilyMember);
validInsert = false;
} else {
item.memberType = results[0].memberType;
item.memberLevel = results[0].memberLevel;
item.dateOfExpiry = results[0].dateOfExpiry;
}
}
});
}
if (validInsert) {
var today = new Date();
var prefix = today.getFullYear().toString().substr(2,2) + ('0' + (today.getMonth() + 1)).slice(-2);
table.includeTotalCount().where(function(prefix){
return this.memberNumber.substring(0, 4) === prefix;
}, prefix)
.take(0).read({
success: function (results) {
if (isNaN(results.totalCount)) {
results.totalCount = 0;
}
item.memberNumber = prefix + ('00' + (results.totalCount + 1)).slice(-3);
request.execute();
}
});
}
Yes, validInsert is declared at the top of the insert function.
I assume what's happening is the if(validInsert) runs before the read callback. But if so, i'm not sure why I'm getting "Error: Execute cannot be called after respond has been called." That implies the callback is running first.
Also, the record is being inserted when it shouldn't be even though the 400 error is sent back to the client.
This is an express app right? Should I just call response.end() after the error occurs?
Yes, there are definitely asyn issues in that code. To solve get rid of your validInsert flag and simply move the if (validInsert) section into the success callback (or make it a function called from the success callback). For example:
success: function(results) {
if (results.length == 0) {
request.respond(statusCodes.BAD_REQUEST,
'Invalid Primary Family Member specified.');
console.error('Invalid Primary Family Member specified:' + item.primaryFamilyMember);
} else {
item.memberType = results[0].memberType;
item.memberLevel = results[0].memberLevel;
item.dateOfExpiry = results[0].dateOfExpiry;
var today = new Date();
var prefix = today.getFullYear().toString().substr(2,2) + ('0' + (today.getMonth() + 1)).slice(-2);
...
//respond successfully
}
}
buses_near_stops begins as an empty array. Inside the asynchronous calls to the database, it is supposed to be filled. Then after the calls finish, I want to use the data inside of it. When I run this code, the final console log of buses_near_stops executes before the inner database calls, even though I have the looped calls inside of a closure. According to this post, a closure should work, but here it is doing nothing for me.
var buses_near_stops = [];
buses_near_stops.test = "TEST";
// return these fields of each location document in the database
Location.find({}, 'service_name coordinates vehicle_id last_gps_fix', function(err, doc) {
//console.log('location found ' + JSON.stringify(doc));
if(err){return next(err);}
doc.forEach(function(j,k) {
//Find a stop that is near enough to each given bus that we can say the bus is 'at' that stop
//Making sure it returns 1 stop now because I don't know proper distance
(function(buses_near_stops) {
Stop.findOne({coordinates: { $near : j.coordinates, $maxDistance: .0001}
}, function(err, stop){
if(err){return next(err);}
console.log('stop found ' + j.service_name + " " + JSON.stringify(stop));
// service_name is null if bus is out of service (I believe)
if(stop !== null && j.service_name !== null) {
var service_name_of_bus = j.service_name;
console.log('service name of bus ' + service_name_of_bus);
// Find the service document associated with service_name_of_bus
var service_of_name = Service.findOne({name: service_name_of_bus}, function(err, service_of_name){
if(err){return next(err);}
// If the service has 'stop' on its route
if(service_of_name != null && service_of_name.routes[0].stops.indexOf(stop.stop_id) > -1) {
console.log('stop found on service');
// We have now found a bus that is stopped at a stop on its route
console.log('test ' + buses_near_stops.test);
buses_near_stops.push(
{
time: j.last_gps_fix,
bus_coords: j.coordinates,
stop_coords: stop.coordinates,
vehicle_id: j.vehicle_id,
stop_id: stop.stop_id,
service_name: service_name_of_bus
});
console.log('length ' + buses_near_stops.length);
}
});
}
})}(buses_near_stops));
});
console.log('buses near stops ' + JSON.stringify(buses_near_stops));
});
I am using Sails' ORM (Waterline). I have written a geturl service that should return the url of several models/actions in my app. I am currently calling this service inside my templates.
(As I am alone to develop this, don't hesitate to warn me if this design pattern is wrong)
Now it occurs that Waterline's .find() method is asynchronous (as it should). I always use callbacks to do things when inserting or fetching things in database.
Now I have seen everywhere that I cannot return any data from asynchronous methods. As a consequence I am puzzled because I want to create this [damned] service to centralize the URL management.
Here is my current code:
module.exports = {
variete: function(id_objet) {
var string = '/default_url';
return onvariete(id_objet, function (err, url) {
if (err) {
sails.log.error('Error : ', err);
} else {
return url;
}
});
}
};
function onvariete(id_objet, next) {
var url = '/';
return Variete.findOne({id:id_objet}).exec(function (err, v) {
sails.log.info('URL Variety : '+ v.nom + ' / ' +id_objet + ' / ' + v.slug);
if (err) {
sails.log.error('Error : ' + v.nom + ' / ' + err);
// Do nothing.
return next(new Error('Variete error'), undefined);
} else if (!v) {
return next(new Error('Variete not found'), undefined);
} else if (!v.slug) {
// variete doesn't have a slug field
// we redirect to /v/:id
url += 'v/' + v.id;
return next (null, url);
} else {
// Ok variete has got a slug field
sails.log.info('GOT A SLUG! ' + v.slug);
url += 'variete/' + v.slug;
return next (null, url);
}
});
}
I made a static object that embeds my geturl service, and then inside a Jade template:
a(href="#{s.geturl.variete(ann.variete.id)}" title="#{ann.variete.name}") #{ann.variete.name}
And I can get something like:
<a title="Tomate Coeur de Boeuf" href="undefined">Tomate Coeur de Boeuf</a>
Thank you by advance.
The solution vas to write a .url(bool) instance method. See how to write instance methods in Sails / Waterline.
This way I directly access this method from my template : a(href="#{ann.variete.url()}" title="#{ann.variete.name}") #{ann.variete.name}.
Done!