I am trying to validate the array of objects before inserting them into the mongodb.
what i am trying to do is, lets say i have an object like below
var data= { prodDetails:
[
{ measured: 'Liters',
name: 'A',
prodCode: '713',
status: true },
{ measured: 'Liters',
name: 'B',
prodCode: '713',
status: true },
{ measured: 'Liters',
name: 'C',
prodCode: '674',
status: true }
]
};
before making a bulk insert call i want check whether the given prodCode is valid DB Code or not and name duplicated or not
i am using node bluebird promises.
i tried the following code to validate prodCode
var bulkOperations = {
bulkProdInsert: function (body) {
return new Promise(function (reslv, rej) {
if (body.prodDetails.length > 0) {
common_lg.getValueById(body, "typesProd", body.prodDetails[0].prodCode).then(bulkOperations.successCallback(reslv, rej, body)).catch(bulkOperations.errCallback(reslv, rej, body));
};
reslv();
});
},
successCallback: function (reslv, rej, body) {
return function (res) {
if (res) {
body.prodDetails.splice(0, 1);
if (body.prodDetails.length > 0) {
common_lg.getValueById(body, "typesProd", body.prodDetails[0].prodCode).then(bulkOperations.successCallback(reslv, rej, body)).catch(bulkOperations.errCallback(reslv, rej, body));
}
};
};
},
errCallback: function (reslv, rej, body) {
return function (err) {
body.prodDetails.splice(0, 1);
if (body.prodDetails.length > 0) {
common_lg.getValueById(body, "typesProd", body.prodDetails[0].prodCode).then(bulkOperations.successCallback(reslv, rej, body)).catch(bulkOperations.errCallback(reslv, rej, body));
};
};
}
};
but i want to do is insert all the objects/documents into DB when name and prodCode of all the objects/documents is validated.
how to achieve this.
thanks
It sounds like you want to check the input object and then make DB calls. I would suggest the .map method of bluebird.
var promise = require('bluebird');
var checkValues = promise.method( function(prod){
if( isValid(prod.prodCode) ){
return prod;
}
//something went wrong!
throw new Error('prodCode ' + prod.prodCode + ' is invalid');
}
promise.map( data.prodDetails, checkValues )
.then(function(){
//it worked! You can call the DB now.
})
.catch(function(error){
//something went wrong, look at that error (or pass along)
})
So long as your checkValues method is a promise, you can run it against every value in your input array, and use the success of .then to know things worked and call your DB!
Related
I have a function that gets some data from DynamoDB using docClient.query(). I'm able to retrieve data and print it on console e.g., console.log(data)), but if I try to return data I always get undefined.
I thought that function (err,data){ ... } was a callback of the query() function and was hoping it would wait for the value to be available to before returning.
Clearly I'm new with AWS SDK and async functions, couldn't find documentation that used return in the way I needed.
I just need the aliasHasRole to return the isAuthorized JSON so I can use it elsewhere outside the function.
function aliasHasRole(an_alias, a_role) {
const params = {
TableName: 'xxxxxxx',
KeyConditionExpression: '#alias= :alias AND #Role= :Role',
ExpressionAttributeNames: {
'#alias': 'alias',
'#Role': 'Role'
},
ExpressionAttributeValues: {
':alias': an_alias,
':Role': a_role,
},
};
docClient.query(params, function (err, data) {
if (err) {
console.log("Error when attempting table query, see below:\n\n" +
JSON.stringify(err, null, 2));
return err;
} else {
var isAuthorized = data.Count === 1 && data.Items[0].alias === an_alias && data.Items[0].Role === a_role ? true : false;
console.log(1,'Access', isAuthorized ? 'Granted' : 'Denied', 'for alias "' + an_alias + '".\n');
return isAuthorized; //always returns undefined
}
})
}
console.log(aliasHasRole("fooAlias","barRole")) // returns undefined.
Surely that's the issue related to asynchronous code. If you are able to use docClient.query with async/await syntax, then you can wait for its execution and return value according to its result. You can also use Promise syntax if it's necessary to know the err value (if exists). Then you can also use resolve/reject in your function.
Solution with async/await syntax:
async function aliasHasRole(an_alias, a_role) {
const params = {
TableName: 'xxxxxxx',
KeyConditionExpression: '#alias= :alias AND #Role= :Role',
ExpressionAttributeNames: {
'#alias': 'alias',
'#Role': 'Role'
},
ExpressionAttributeValues: {
':alias': an_alias,
':Role': a_role,
}
};
const data = await docClient.query(params);
if (!data) return false;
const isAuthorized = data.Count === 1 && data.Items[0].alias === an_alias && data.Items[0].Role === a_role ? true : false;
return isAuthorized;
};
aliasHasRole("fooAlias", "barRole").then(console.log).catch(console.log);
UPDATE
What really helps is making your query a promise with .promise() . Then it can be easily executed and handled with then/catch syntax.
I got this error: Falsy value for recipient key 'registrationTokens' while working with gcm push notifications.
Below you can find my code:
Device.find({ user: { $in: users }}, function (err, devices) {
if (err) {
logger.error('500 ' + err)
return res.status(500).json({
code: config.errorCode.status500.code,
message: config.errorCode.status500.message
})
}
var androidRegTokens = []
var iOSReqTokens = []
for (var i = 0; i < devices.length; i++) {
if (devices[i].platform == 'Android') {
androidRegTokens.push(devices[i].deviceToken)
} else {
iOSReqTokens.push(devices[i].deviceToken)
}
}
if (androidRegTokens.length > 0) {
gcmPush('Notification is sent.', androidRegTokens)
}
if (iOSReqTokens.length > 0) {
apnsPush('Notification is sent.', iOSReqTokens)
}
return res.json({ msg: 'Operation succeed.'})
})
Body of the function gcmPush('Notification is sent.', androidRegTokens) is:
this.sender.send(message, { registrationTokens: deviceTokens }, function (err, response) {
if (err) {
console.error(err)
}else {
console.log(response)
}
})
Does anyone know what wrong is with my code? At first push notifications worked perfect, than this error occured each time I call the service.
EDIT:
I have solved this thanx to Luiz Fernando. The problem is the function:
gcmPush('Notification is sent.', androidRegTokens)
I have forgot to add title which is part of the constructor:
function GcmPush (title, message, deviceTokens) {
this.sender = new gcm.Sender(config.gcmSender)
this.sendPushNotification(title, message, deviceTokens)
}
The deviceTokens object is a falsy value, it can be: null, undefined, 0, '', NaN,false.
Maybe it happend because you are filling iOSReqTokens and androidRegTokens nested in an asynchronous operation (it seems Device.find is async). So, the callback of Device.find and the for-loop happens in different times. Probably, the for-loop is happening AFTER the response, so this response will be undefined (and really is).
So, you need to use some async control flow library, such Promise or Async and ensure the correct order of operations.
Also, where you use the pushed registrations?
While my database server is not available and any function of my node-express rest service like hiExpress is called, Nodejs crashes the node server and console of node reports
sql server connection closed
I do not want this to happen because either it should go to err function or at least it must be cautht by catch block. What could i do to avoid the crash of nodejs when database server is not available I am using following code which is absolutely fine as long as database server is available.
var sqlServer = require('seriate');
app.get('/hiExpress',function(req, res)
{
var sr = {error:'',message:''};
var sql= 'select * from table1 where id=? and name=?';
var params = {id: 5, name:'sami'};
exeDB(res,sr,sql, params);//sent only 4 parameters (not 6)
});
function exeDB(res, sr, sql, params, callback, multiple) {
try {
var obj = {};
for (p in params) {
if (params.hasOwnProperty(p)) {
obj[p] = {
type: sqlServer.VARCHAR,
val: params[p]
};
}
};
var exeOptions = {
query: sql,
params: obj
};
if (multiple) {
exeOptions.multiple = true;
}
sqlServer.execute(sqlServerConfigObject, exeOptions).then(function (results) {
sr.data = results;
if (callback)
callback(sr);
else
res.json(sr); //produces result when success
}, function (err) {
//sr.message = sql;
console.log(11);
sr.error = err.message;
res.json(sr);
});
}
catch (ex) {
console.log(21);
sr.error = ex.message;
res.json(sr);
}
}
Why I preferred to use seriate
I had not been much comfortable with node-SQL, especially when when it came to
multiple queries option even not using a transaction. It facilitates easy go to parameterized queries.
You can use transaction without seriate but with async like below
async.series([
function(callback) {db.run('begin transaction', callback)},
function(callback) {db.run( ..., callback)},
function(callback) {db.run( ..., callback)},
function(callback) {db.run( ..., callback)},
function(callback) {db.run('commit transaction', callback)},
], function(err, results){
if (err) {
db.run('rollback transaction');
return console.log(err);
}
// if some queries return rows then results[query-no] contains them
})
The code is very dirty. Pass req and res params to db-layer is not a good idea.
Try change exeDB. I'm not sure, but probably you don't set error catcher to promise
function exeDB(res, sr, sql, params, callback, multiple) {
// It will execute with no error, no doubt
var obj = {};
for (p in params) {
if (params.hasOwnProperty(p)) {
obj[p] = {
type: sqlServer.VARCHAR,
val: params[p]
};
}
};
var exeOptions = {
query: sql,
params: obj
};
if (multiple) {
exeOptions.multiple = true;
}
// Potential problem is here.
// Catch is useless because code below is asynchronous.
sqlServer.execute(sqlServerConfigObject, exeOptions).then(function (results) {
sr.data = results;
if (callback)
callback(sr);
else
res.json(sr); //produces result when success
}).error(function(err){ // !!! You must provide on-error
console.log(err);
};
}
I created a function to:
take an array of 'labels' and look for whether they have a record in the db already
create those which don't exist,
and update those which do exist
return a json array reporting on each item, whether they were updated/created, or resulted in an error
I managed to make it work but I feel like I just made some ugly dogs' dinner!
var models = require("../models");
var Promise = models.Sequelize.Promise;
module.exports = {
addBeans: function (req, callback) {
Promise.map(req.body.beansArr, function (bean) {
return models.Portfolio.findOrCreate({where: {label: bean}}, {label: bean});
}).then(function (results) { // Array of 'instance' and 'created' for each bean "findOrCreate(where, [defaults], [options]) -> Promise<Instance>"
var promisesArr = [];
results.forEach(function (result) {
if (result[1]) { // result[1] = wasCreated
promisesArr.push(Promise.resolve([result[0].dataValues.label, "created"]));
} else {
promisesArr.push(
models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).then(function () {
return Promise.resolve([result[0].dataValues.label, "updated"])
})
);
}
});
return promisesArr;
// When it's all done create a JSON response
}).then(function (results) {
var resultObj = {items: []}; // JSON to return at the end
Promise.settle(results).then(function (promiseinstances) {
for (var i = 0; i < promiseInstances.length; i++) {
if (promiseInstances[i].isFulfilled()) {
resultObj.items.push({
item: {
label: promiseInstances[i].value()[0],
result: promiseInstances[i].value()[1],
error: ''
}
});
}
else if (promiseInstances[i].isRejected()){
resultObj.items.push({
label: promiseInstances[i].value()[0],
result: 'error',
error: promiseInstances[i].reason()
});
}
}
// Send the response back to caller
}).then(function () {
return callback(null, resultObj);
}, function (e) {
return callback(e, resultObj);
});
});
}
};
Question:
Is there an easier or more obvious way to create/update values with Sequelize?
Is my use of Promise.settle() appropriate for this case? I have the feeling I made this more complicated than it needs to be.
I am new to Sequelize and using Promises, I'd appreciate if someone could advise on this.
I feel like this would work better on CodeReview.SE but I can see a few issues.
Is there an easier or more obvious way to create/update values with Sequelize?
Well, for one thing:
.then(function(array){
var newArr = [];
array.forEach(function(elem){
newArr.push(fn(elem);
}
return newArr;
});
Is just
.map(fn)
Additionally, promises assimilate so you can return val; from a .then you don't have to return Promise.resolve(val);.
So:
).then(function (results) { // Array of 'instance' and 'created' for each bean "findOrCreate(where, [defaults], [options]) -> Promise<Instance>"
var promisesArr = [];
results.forEach(function (result) {
if (result[1]) { // result[1] = wasCreated
promisesArr.push(Promise.resolve([result[0].dataValues.label, "created"]));
} else {
promisesArr.push(
models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).then(function () {
return Promise.resolve([result[0].dataValues.label, "updated"])
})
);
}
});
return promisesArr;
})
Is just
.map(function(result){
if(result[1]) return [result[0].dataValues.label, "created"];
return models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).
return([result[0].dataValues.label, "updated"]);
});
However, since you want it to work regardless of it being resolved, you'd have to do:
.then(function(results){
return results.map(function(result){
if(result[1]) return [result[0].dataValues.label, "created"];
return models.Portfolio.update({label: result[0].dataValues.label},
{where: {label: result[0].dataValues.label}}).
return([result[0].dataValues.label, "updated"]);
});
});
Which means it'll resolve regardless, then you'd call .settle():
.settle().then(function(results){
// your settle logic here
});
Note that the last:
}).then(function () {
return callback(null, resultObj);
}, function (e) {
return callback(e, resultObj);
});
Is simply:
.nodeify(callback);
However, I recommend sticking to promises.
I use Promise.settle for sequelize.update, and can get affect rows number by _settledValueField .
promise.push(...update...)
db.sequelize.Promise.settle(promise).then(function (allresult) {
var affectcnt = 0
allresult.forEach(function (singlecnt) {
if (undefined !== singlecnt._settledValueField[1]) {
affectcnt += parseInt(singlecnt._settledValueField[1])
}
})
unfortunately, it's only work for update.
You can insert array in database using sequelize. You want to change in model like below. I am trying to add multiple languages in database through array.
language: { type: DataTypes.STRING,
allowNull: false,
get()
{
return this.getDataValue('language').split(';')
},
set(val)
{
this.setDataValue('language',Array.isArray(val) ? val.join(','):val);
}
}
Suppose I have four distinct asynchronous operations that need to be run, and they can all be run independently. But there's one remaining function that needs to use all the data those asynchronous calls collect, so it can only be done once all of them are finished.
A simple way to do this is to make the asynchronous calls call each other one right after the other, and then finally call the final function, like so:
myObj.async1(function () {
myObj.async2(function () {
myObj.async3(function () {
myObj.async4(function () {
...
finalFunction();
But this is a poor way to do it, since node is built around asynchronous functionality for a reason. So instead, let's say we want to do:
myObj.async1(async1Callback);
myObj.async2(async2Callback);
myObj.async3(async3Callback);
myObj.async4(async4Callback);
if( //Some logic here to determine when all four functions have completed
finalFunction();
What's the best way to determine that logic? I considered having each function set a boolean variable to indicate whether it has completed, and then having a time-based emitter that constantly checks if all four variables are set to true and then calls finalFunction if they are, but that can get messy with having all those variables lying around.
Any thoughts on what's the best way to do this?
I would make use of the async library for this, e.g.
async.parallel([
myObj.async1,
myObj.async2,
myObj.async3,
myObj.async4
], function(err) {
if (err) throw err;
// Run final function now that all prerequisites are finished
finalFunction();
});
This assumes that each myObj.async* function takes a callback function as its only parameter and that callback's first parameter is an err param. For more info see the docs for async#parallel().
As #jabclab recommended, take a look at async as it manages much of the complexity for you. However, if you want to do something like this yourself here are a couple of alternatives.
Starting with a myObj that looks like:
var myObj = {
async1: function async1(cb) { setTimeout(function() {
console.log('async1');
cb(null, {name: 'async1'});
}, 1000)},
async2: function async2(cb) { setTimeout(function() {
console.log('async2');
cb(null, {name: 'async2'});
}, 500)},
async3: function async3(cb) { setTimeout(function() {
console.log('async3');
cb(null, {name: 'async3'});
}, 1001)},
async4: function async4(cb) { setTimeout(function() {
console.log('async4');
cb(null, {name: 'async4'});
}, 200)}
}
This version is hardcoded to call four specific functions and callback when the results are complete. The results passed back in an array ordered by completion. Each result object contains the name of the function as well as any error or success result.
function doFourSpecificThings(callback) {
var results = [];
var storeResults = function(fnName, err, resp) {
results.push( { fnName: fnName, err: err, resp: resp } );
if(results.length === 4 && callback) {
callback(results);
}
}
// Bind the callback to myObj and pass the name of the called function
// as the first argument
myObj.async1(storeResults.bind(myObj, 'async1'));
myObj.async2(storeResults.bind(myObj, 'async2'));
myObj.async3(storeResults.bind(myObj, 'async3'));
myObj.async4(storeResults.bind(myObj, 'async4'));
}
doFourSpecificThings(function(results) {
console.log(results);
});
Output:
async4
async2
async1
async3
Results:
[ { fnName: 'async4', err: null, resp: { name: 'async4' } },
{ fnName: 'async2', err: null, resp: { name: 'async2' } },
{ fnName: 'async1', err: null, resp: { name: 'async1' } },
{ fnName: 'async3', err: null, resp: { name: 'async3' } } ]
This version is a bit more flexible. The tasks are passed in as an array with the results being stored in the same order in the resulting array:
function doABunchOfStuff(tasks, callback) {
var results = [];
var expected = tasks.length;
var storeResults = function(idx, err, resp) {
results[idx] = { err: err, resp: resp };
--expected;
if((expected === 0) && callback) {
callback(results);
}
}
// Using bind here to pass the current index to the storeResults()
// callback as the first parameter
for(var i = 0; i < tasks.length; ++i) {
tasks[i](storeResults.bind(tasks[i], i));
}
}
doABunchOfStuff([
myObj.async1.bind(myObj),
myObj.async2.bind(myObj),
myObj.async3.bind(myObj),
myObj.async4.bind(myObj)],
function(results) {
console.log('\nResults:');
console.log(results);
});
Output:
async4
async2
async1
async3
Results:
[ { err: null, resp: { name: 'async1' } },
{ err: null, resp: { name: 'async2' } },
{ err: null, resp: { name: 'async3' } },
{ err: null, resp: { name: 'async4' } } ]