Node/Bluebird/MySQL transactions - mysql

I've been struggling with the Bluebird promise library and MySQL for the past week. I'm constantly finding that what doco there is assumes I'm totally immersed in their terminology and often only gives half the answer in it's examples, or none at all. Leaving me hanging trying to figure out how to use it.
Currently I'm trying to execute a series of SQL commands for creating a database record inside a transaction. I'm using this code:
var Promise = require('bluebird');
var mysql = require('mysql');
Promise.promisifyAll(mysql);
Promise.promisifyAll(require('mysql/lib/Connection').prototype);
Promise.promisifyAll(require('mysql/lib/Pool').prototype);
function getConnection() {
return pool.getConnectionAsync().disposer(function (connection) {
connection.release();
});
}
function getTransaction(connection) {
return connection.beginTransactionAsync().disposer(function (tx, promise) {
if (promise.isFulfilled()) {
tx.commitAsync();
} else {
tx.rollbackAsync();
}
});
}
Database.prototype.addStory = function (projectId, title, text) {
return Promise.using(getConnection(), function (connection) {
return Promise.using(getTransaction(connection), function () {
return connection.queryAsync('INSERT INTO story SELECT ?, MAX(storyNumber) + 1, ?, ?, 0 FROM story WHERE projectID = ?',
[projectId, title, text, projectId])
.then(connection.queryAsync('select LAST_INSERT_ID()'))
.then(function (rows) {
debug("Returning story for %s", rows[0]);
return getStory(connection, rows[0]);
});
});
});
}
And currently I'm getting this error:
TypeError: tx.rollbackAsync is not a function
From what I've read my code should work. Does anyone know what's wrong with it?

Thanks for the answers guys.
I've now found the Knex API which solves all my problems with less code.

I've found the initial error and used this approach in my code:
function getTransaction(connection) {
return connection.beginTransactionAsync().disposer(function (tx, promise) {
if (promise.isFulfilled()) {
connection.commitAsync();
} else {
connection.rollbackAsync();
}
});
}

Related

How do I use promises in a Chrome extension?

What I am trying to do is create a chrome extension that creates new, nested, bookmark folders, using promises.
The function to do this is chrome.bookmarks.create(). However I cannot just
loop this function, because chrome.bookmarks.create is asynchronous. I need to wait until the folder is created, and get its new ID, before going on to its children.
Promises seem to be the way to go. Unfortunately I cannot find a minimal working example using an asynchronous call with its own callback like chrome.bookmarks.create.
I have read some tutorials 1, 2, 3, 4. I have searched stackOverflow but all the questions do not seem to be about plain vanilla promises with the chrome extension library.
I do not want to use a plugin or library: no node.js or jquery or Q or whatever.
I have tried following the examples in the tutorials but many things do not make sense. For example, the tutorial states:
The promise constructor takes one argument—a callback with two
parameters: resolve and reject.
But then I see examples like this:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
How this works is a mystery to me.
Also, how can you call resolve() when its never been defined? No example in the tutorials seem to match real life code. Another example is:
function isUserTooYoung(id) {
return openDatabase() // returns a promise
.then(function(col) {return find(col, {'id': id});})
How do I pass in col, or get any results!
So if anyone can give me a minimal working example of promises with an asynchronous function with its own callback, it would be greatly appreciated.
SO wants code, so here is my non-working attempt:
//loop through all
function createBookmarks(nodes, parentid){
var jlen = nodes.length;
var i;
var node;
for(var i = 0; i < nodes.length; i++){
var node = nodes[i];
createBookmark(node, parentid);
}
}
//singular create
function createBookmark(node, parentid){
var bookmark = {
parentId : parentid,
index : node['index'],
title : node['title'],
url : node['url']
}
var callback = function(result){
console.log("creation callback happened.");
return result.id; //pass ID to the callback, too
}
var promise = new Promise(function(resolve, reject) {
var newid = chrome.bookmarks.create(bookmark, callback)
if (newid){
console.log("Creating children with new id: " + newid);
resolve( createBookmarks(bookmark.children, newid));
}
});
}
//allnodes already exists
createBookmarks(allnodes[0],"0");
Just doesn't work. The result from the callback is always undefined, which it should be, and I do not see how a promise object changes anything. I am equally mystified when I try to use promise.then().
var newid = promise.then( //wait for a response?
function(result){
return chrome.bookmarks.create(bookmark, callback);
}
).catch(function(error){
console.log("error " + error);
});
if (node.children) createBookmarks(node.children, newid);
Again, newid is always undefined, because of course bookmarks.create() is asynchronous.
Thank you for any help you can offer.
Honestly, you should just use the web extension polyfill. Manually promisifying the chrome APIs is a waste of time and error prone.
If you're absolutely insistent, this is an example of how you'd promisify chrome.bookmarks.create. For other chrome.* APIs, you also have to reject the callback's error argument.
function createBookmark(bookmark) {
return new Promise(function(resolve, reject) {
try {
chrome.bookmarks.create(bookmark, function (result) {
if (chrome.runtime.lastError) reject(chrome.runtime.lastError)
else resolve(result)
})
} catch (error) {
reject(error)
}
})
}
createBookmark({})
.then(function (result) {
console.log(result)
}).catch(function (error) {
console.log(error)
})
To create multiple bookmarks, you could then:
function createBookmarks(bookmarks) {
return Promise.all(
bookmarks.map(function (bookmark) {
return createBookmark(bookmark)
})
)
}
createBookmarks([{}, {}, {}, {}])
.catch(function (error) {
console.log(error)
})
Take the advantage of the convention that the callback function always be the last argument, I use a simple helper function to promisify the chrome API:
function toPromise(api) {
return (...args) => {
return new Promise((resolve) => {
api(...args, resolve);
});
};
}
and use it like:
toPromise(chrome.bookmarks.create)(bookmark).then(...);
In my use case, it just works most of the time.

Asynchronously working of for loop in protractor

when i execute the following code using protractor it works. I am passing nested json to for loop. Because of asynchronously working of for loop it print all values of variable i and reaches to last value because of this it always access last pair of username and password. How can i solve this issue?
var data = require('.../testdata.json');
describe('homepage Test', function() {
it('candidate login', function() {
browser.driver.get('https://abcxyz.com');
for (i in data.testdata) {
element(by.id('tool_btn3')).click();
console.log(i);
browser.getTitle().then(function(title) {
console.log("Title: " + title)
if (title === "<page title>") {
browser.driver.sleep(3000);
element(by.id('email_input')).sendKeys(data.testdata[i].username);
element(by.id('pwd_input')).sendKeys(data.testdata[i].password);
element(by.xpath('//*[#id="signIn_btn"]/div[2]')).click();
browser.sleep(3000);
element(by.id('setting_img')).click();
browser.sleep(2000);
element(by.id('logout_div')).click().then(function() {
console.log('success');
});
} else {
console.log("problem");
}
});
}
});
});
You need to keep in mind that you can't use a for-loop with promises. All is async so in the end it will bite you in the ass, meaning that the it is ready but the test isn't.
Based on you example it would suggest to make a method called for example logon (place it in a Page Object or something). It will do the logon and stuff for you. Add an empty promise-container (array) and push the promises in there.
When the for-loop is done you can resolve the complete promise-container at once and it will execute all the promises 1 after each other. It will look something like this.
var data = require('.../testdata.json');
describe('homepage Test', function() {
it('candidate login', function() {
var promises = [];
browser.driver.get('https://abcxyz.com');
for (i in data.testdata) {
promises.push(expect(logon(data.testdata[i].username, data.testdata[i].password)).to.equal(true));
promises.push(console.log(i));
}
Promise.all(promises);
});
});
/**
* Logon
* #params {string} username
* #params {string} password
* #return {boolean}
*/
function logon(username, password) {
element(by.id('tool_btn3')).click();\
return browser.getTitle().then(function(title) {
console.log("Title: " + title)
if (title === "<page title>") {
browser.driver.sleep(3000);
element(by.id('email_input')).sendKeys(username);
element(by.id('pwd_input')).sendKeys(password);
element(by.xpath('//*[#id="signIn_btn"]/div[2]')).click();
browser.sleep(3000);
element(by.id('setting_img')).click();
browser.sleep(2000);
return element(by.id('logout_div')).click()
.then(function() {
return Promise.resolve(true);
});
} else {
return Promise.resolve(false);
}
});
}
If you are using for example Node 7 you can use async/await, or use Babel to transpile the code. If you can write TypeScript you also get the async/await

Return a value from sequelize query

I'm new to sequelize. I'm using mysql as my database. I've a sequelize query which finds the email-id from the database. By using the result of this query, I got the ID of that row. Now I need to return this value. Can someone please help me how to do that.
Here is my code.
var userId = getUserId(email_address);
function getUserId(email_address) {
models.users.findOne({
where: {
email: email_address
}
}).then(function(result) {
if (result) {
var applicantId = result.id;
return applicantId; // This is what I need to do
} else {
console.log("Could not find Email");
}
});
}
Here I need to return the variable applicantId to the calling function.
The Sequelize call to the database is asynchronous so you need to alter your code a bit to work with promises. Something like this:
function getUserId(email_address) {
return models.users.findOne({
where: {
email: email_address
}
});
}
getUserId("some#email.com").then(function(result){
console.log(result.id);
});
check out this fiddle that mocks your scenario

Async and Knex: how they work ?

I parse an json object and for each element, i need to execute many queries.
In first, a "select" query and depending on the result, i execute an insert or an update.
I would like use async.js and knex.js
The issue it's the order of execution is not the searched order
async.each(newContent,function(e){
//var e=JSON.stringify(element),
var z=-1,
devicepresenceId = e.device_presence_id;
//console.log(e);
async.waterfall([
function(cb) {
knex('jos_joomrh_event_employee_hours_presence')
.whereRaw('device_presence_id=?', devicepresenceId)
.select('id', 'applied_at', 'applied_at_end')
.debug()
.then(function (rows) {
console.log(rows);
z = _.keys(rows).length;
console.log('rows0', z);
cb(null,z);
})
.catch(function (e) {
console.log(e)
reject(e)
})
cb(null,z);
},
function(z,cb){
console.log('z',z);
if (parseInt(z)==0)
{
console.log('insertHoursPresence');
//insertHoursPresence(e)
}
else{
console.log('updateHoursPresence');
//updateHoursPresence(e)
}
cb(null,'two')
}
],
function(err,z){
if(err)console.log(err);
console.log(z);
}
)}
)}
In fact; it executed the second function and and the cb function and after the first function with knex.:
Thanks for your help
Mdouke
In the knex part, you have a call to "cb(null,z)", you have to "move" that call inside catch function (but replacing reject(e) part). Your problem is that you're calling cb() function outside knex, of course it is called immediately independent of knex result)

How to pass errors conditions

In web app development I would like a consistent way to catch and report error conditions. For example, a database update routine may detect a variety of error conditions and ideally I would like the application to capture them and report gracefully. The code below din't work because retdiag is undefined when error is thrown...
function saveData(app,e) {
var db ;
var retdiag = "";
var lock = LockService.getPublicLock();
lock.waitLock(30000);
try {
// e.parameters has all the data fields from form
// re obtain the data to be updated
db = databaseLib.getDb();
var result = db.query({table: 'serviceUser',"idx":e.parameter.id});
if (result.getSize() !== 1) {
throw ("DB error - service user " + e.parameter.id);
}
//should be one & only one
retdiag = 'Save Data Finished Ok';
}
catch (ex) {
retdiag= ex.message // undefined!
}
finally {
lock.releaseLock();
return retdiag;
}
}
Is there a good or best practice for this is GAS?
To have a full error object, with message and stacktrace you have to build one, and not just throw a string. e.g. throw new Error("DB error ...");
Now, a more "consistent" way I usually implement is to wrap all my client-side calls into a function that will treat any errors for me. e.g.
function wrapper_(f,args) {
try {
return f.apply(this,args);
} catch(err) {
;//log error here or send an email to yourself, etc
throw err.message || err; //re-throw the message, so the client-side knows an error happend
}
}
//real client side called functions get wrapped like this (just examples)
function fileSelected(file,type) { return wrapper_(fileSelected_,[file,type]); }
function loadSettings(id) { return wrapper_(loadSettings_,[id]); }
function fileSelected_(file,type) {
; //do your thing
}
function loadSettings_(id) {
; //just examples
throw new Error("DB error ...");
}