I try to make a notification server in node.js which get the notifications from a database, edit their payload, send them via Firebase Cloud Messaging, and then edit their status in database.
Javascript is not my main language, so I hope their is not a lot of misconceptions in this code.
To do this, I use some Promises and a Promise.all.
Currently, the problem is when I call firebase.admin.messaging().sendToDevice
, my app never ends its execution.
Here is the code :
main.js :
'use strict';
global.basePath = __dirname + '/';
const conf = require('./config/config'),
db = require('./lib/database'),
builder = require('./notify/builder');
const gender = conf.genderId[process.argv[2]],
type = conf.typeId[process.argv[3]],
confSql = conf.inc.prod ? conf.mysql.prod : conf.mysql.dev,
database = new db.database(confSql);
const notify = new Promise(
(resolve, reject) => {
if (typeof(gender) !== 'number' && typeof(type) !== 'number') {
return reject('Error: gender and type are mandatory');
}
resolve();
}
);
function main () {
notify
//Get the notifications from DB - They are already group by user
.then( () => { return database.getNotifications(gender, type); })
//Set the payload, send to Firebase, and update the status in DB
// <-- Inside it is the call to Firebase
.then( rows => { return Promise.all(rows.map(builder.handleNotification)); }
, err => {
return database.close().then( () => {
return Promise.reject(err)
} );
}
)
.then(() => {
console.log('Success ! The DB and the app must close.');
database.close();
})
.catch(console.log.bind(console))
;
}
main();
builder.js :
'use strict';
const conf = require('./../config/config'),
sender = require('./sender'),
database = require('./../lib/database');
//This is called inside an array.map
//It is a chain of Promises that are resolved or rejected in a Promise.all
function handleNotification( notification){
let notif = notification;
return Promise.resolve(setGroupPayload(notification))
.then(sender.send)
.then(console.log)
.catch(error => {
return Promise.reject(error);
});
}
function setGroupPayload (notification){
//Do some change on notifications
// (...)
return notification;
}
module.exports = {
handleNotification: handleNotification
};
database.js :
const mysql = require( 'mysql' );
function Database(config) {
this.connection = mysql.createConnection( config );
}
Database.prototype.query = function query( sql, args ) {
return new Promise( ( resolve, reject ) => {
this.connection.query( sql, args, ( err, rows ) => {
if ( err )
return reject( err );
resolve( rows );
} );
} );
};
Database.prototype.ping = function ping(){
return new Promise( ( resolve, reject) => {
this.connection.ping( err => {
if ( err )
return reject( err );
resolve('Server responded to ping');
} );
} );
};
Database.prototype.close = function close() {
console.log('close connection');
return new Promise( ( resolve, reject ) => {
this.connection.end( err => {
if ( err )
return reject( err );
console.log('connection closed');
resolve();
} );
} );
};
Database.prototype.getNotifications = function getNotifications (gender, type) {
const query = `(...)`;
const params = [gender, type];
return this.query(query, params);
};
module.exports = {
database: Database
};
And finally, the sender.js :
'use strict';
const firebase = require('./../lib/firebase-admin');
/**
*
* @param notification
* @returns {Promise}
*/
function send (notification) {
if (notification.message === false) {
return Promise.reject(["payload is empty"]);
}
if (!(notification.token && notification.token.length > 0)) {
return Promise.reject(["target is empty."]);
}
const options = {
contentAvailable: true
};
//When this is called here, the app never ends
return firebase.admin.messaging().sendToDevice(notification.token, notification.message, options); /
}
module.exports = {
send: send
};
I have from firebase.admin.messaging().sendToDevice(notification.token, notification.message, options)
the following response, which is a Promise.resolve :
[ { error: { [Error: The provided registration token is not registered. A previously valid registration token can be unregistered for a variety of reasons. See the error documentation for more details. Remove this registration token and stop using it to send messages.] errorInfo: [Object], codePrefix: 'messaging' } } ]
This is right because the token is not valid. And I want to handle this response. But what I don't understand, is why my app never end ? It looks like their is a neverending promise in the Promise.all that prevent the app to end.
I tried also to handle the response from Firebase and send a Promise.reject
to the promise chain, but without success...
So... Where am I wrong ? Thanks a lot to anybody that can help me to resolve this bug.
Edit :
I have added a .then()
before the catch in the builder.js
as @JimWright asked.
And here is the result :
Here is the result:
{ results: [ { error: [Object] } ],
canonicalRegistrationTokenCount: 0,
failureCount: 1,
successCount: 0,
multicastId: 6057186106180078000 }
Success ! The DB and the app must close.
close connection
connection closed