I develop a restful API with nodeJS.
exports.postCreature = function (req, res) {
var creature = new Creature({
name: req.body.name, id_user: req.user._id
});
creature.save(function (err) {
if (err)
res.status(400).send(Error.setError('impossible to save the your creature', err));
else
res.status(201).send();
});
};
//CODE DUPLICATE
exports.createCreature = function(user, callback) {
console.log('Creature created');
var creature = new Creature({
name: user.username, id_user: user._id
});
creature.save(function (err) {
if (err)
callback(err, null);
else
callback(null, creature);
});
}
The two functions execute the same code but not with the same parameters.
I would like to avoid duplication in my code.
How can do in order to avoid duplication of my code ?
I would create another function to handle the redundancies:
function createCreature (creatureName, user, callback) {
console.log('Creature created');
var creature = new Creature({
name: creatureName, id_user: user._id
});
creature.save(function (err, creature) {
if (err)
callback(err, null);
else
callback(null, creature);
});
}
And then in your other functions:
exports.postCreature = function (req, res) {
createCreature(req.body.name, req.user, function (err, creature) {
if (err)
res.status(400).send(Error.setError('impossible to save the your creature', err));
else
res.status(201).send();
};
};
exports.createCreature = function(user, callback) {
console.log('Creature created');
createCreature (user.username, user, callback);
}
Related
So I come to this, I want to write into a DB and do other operations to work with my program logic, this is the guide that I'm following Node.js Class Creation:
//## This is my mysql_test.js file
function MySQL(){
var mysql = require('mysql');
var con = mysql.createConnection({
//data omitted
});
function AppendRecordset (req, res){
con.connect(function(err) {
if (err) throw err;
console.log("Connected!");
con.query(req, function (err, res) {
if (err) throw err;
console.log("1 record inserted");
});
});
}
function UpdateRecordset (req, res) {
con.connect(function(err) {
if (err) throw err;
con.query(req, function (err, res) {
if (err) throw err;
console.log(result.affectedRows + " record(s) updated");
});
});
}
function DeleteRecordset (req, res){
con.connect(function(err) {
if (err) throw err;
con.query(req, function (err, res) {
if (err) throw err;
console.log("Number of records deleted: " + result.affectedRows);
});
});
}
function GetRecordset (req, res) {
con.connect(function(err) {
if (err) throw err;
con.query(req, function (err, res, fields) {
if (err) throw err;
console.log(result);
});
});
}
}
I then have in a separate file(s) my app logic, and want to use what of the above as an object/class so I wrote this accordingly to that guide:
//##this is inside my main app file
//declare the sql processor
require('./mysql_test.js');
var DB = MySQL();
DB.AppendRecordset(sql_string, res); //sql_string contains a valid SQL statement
But when I try to acces it using `` I get this error message: ReferenceError: MySQL is not defined what am I doing wrong?
I think these functions handle your routes, so I didn't change them. Because I don't know how your router is desined.
Create a file dbHangler.js and write this single function:
const mysql = require('mysql');
let con;
exports.execQuery = (query) => {
return new Promise((resolve, reject) => {
if(!con) {
con = mysql.createConnection({
//data omitted
});
}
con.connect(function(err) {
if(err) {
reject(err);
}
else {
console.log("Connected!");
con.query(query, function (err, res) {
if (err) {
reject(err);
}
else {
resolve(res);
}
});
}
});
});
};
In your dedicated.js file, now you can write:
const dbObject = require('path/to/dbHandler');
function AppendRecordset (req, res){
dbObject.execQuery(req)
.then(result => {
console.log(result.affectedRows + " record(s) updated");
})
.catch(error => {
// handle error
});
}
function AppendRecordset (req, res){
dbObject.execQuery(req)
.then(result => {
console.log("Number of records deleted: " + result.affectedRows);
})
.catch(error => {
// handle error
});
}
function AppendRecordset (req, res){
dbObject.execQuery(req)
.then(result => {
console.log(result);
})
.catch(error => {
// handle error
});
}
UPDATE
I hope this one helps you.
DbHandler.js
const mysql = require('mysql');
class DbHandler {
constructor(config) {
let self = this;
self.dbConfig = config;
self.connection = mysql.createConnection({
//data omitted
});
}
queryExecuter(query) {
let self = this;
return new Promise((resolve, reject) => {
self.connection.connect(function (err) {
if (err) {
reject(err);
}
else {
console.log("Connected!");
self.connection.query(query, function (err, res) {
if (err) {
reject(err);
}
else {
resolve(res);
}
});
}
});
});
}
AppendRecordset(query) {
let self = this;
return self.queryExecuter(query)
.then(result => {
console.log("1 record inserted");
return result;
})
.catch(error => {
// handle error
throw error;
});
}
UpdateRecordset(query) {
let self = this;
return self.queryExecuter(query)
.then(result => {
console.log(result.affectedRows + " record(s) updated");
return result;
})
.catch(error => {
// handle error
throw error;
});
}
// and other functions
}
module.exports = DbHandler;
And use it like below:
let DB = require('/path/to/DbHandler');
let myDb = new DB(/* db config */);
db.UpdateRecordset('your query')
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
I am attempting to do what I've previously done in c# and asp.net webforms and execute a number of insert stored procedures, some of which are in loops, in a transaction. I am now trying to do so in my angular application through node.js with the mssql package. What I have so far is as follows.
//Insert Change Record
router.post('/insertChange', (req, res) => {
const transaction = new sql.Transaction(conn);
transaction.begin(err => {
let rolledBack = false;
transaction.on('rollback', aborted => {
rolledBack = true;
})
const request = new sql.Request(transaction);
request.input('ChangeTitle', req.body.changeTitle);
request.input('TypeId', req.body.typeId);
request.input('DateSubmitted', req.body.dateSubmitted);
request.input('TargetDate', req.body.targetProductionDate);
request.input('ChangeSponsor', req.body.changeSponsor);
request.input('UATDate', req.body.dateReadyUAT);
request.input('ChangeSponsorEmail', req.body.changeSponsorEmail);
request.input('ClarityId', req.body.clarityId);
request.input('ChangeDescription', req.body.changeDescription);
request.input('ComponentName', req.body.componentName);
request.input('ComponentDescription', req.body.componentDescription);
request.input('ReasonForChange', req.body.reasonForChange);
request.input('ComponentREplacing', req.body.componentReplacing);
request.input('DependentChange', req.body.dependentChange);
request.input('InstallOption', req.body.installOption);
request.input('RebootRequired', req.body.rebootRequired);
request.input('UserIntervention', req.body.userIntervention);
request.input('Activation', req.body.activation);
request.input('ContingencyPlan', req.body.contingencyPlan);
request.input('AdditionalInformation', req.body.additionalInformation);
request.input('PerformanceImpact', req.body.applicationPerformance);
request.input('IsPCoERequired', req.body.pcoeTesting);
request.input('IsCanadianRetailBranch', req.body.pbsTesting);
request.input('BusinessAppImpact', req.body.businessApplication);
request.input('NetworkImpact', req.body.networkPerformance);
request.input('NewInfrastructure', req.body.newInfrastructure);
request.input('LogonTime', req.body.logonTime);
request.input('AdditionalTechnicalInformation', req.body.additionalAssessmentInfo);
request.input('IsProdIssue', req.body.isProdIssue);
request.input('ProdIssue', req.body.productionIssue);
request.input('ProdIncidentNum', req.body.incidentNumbers);
request.input('IsMajorChange', req.body.isMajorChange);
request.input('HasRequiredTesting', req.body.hasRequiredTesting);
request.input('HasPackageSubmit', req.body.hasPackageSubmit);
request.input('SignOffETA', req.body.signoffETA);
request.input('SpecificTesting', req.body.specificTesting);
request.input('SpecificPilot', req.body.specificPilot);
request.input('PilotInfo', req.body.pilotTransits);
request.input('UserChanges', req.body.userDifferences);
request.input('ServiceDeskProcedure', req.body.procedureSupport);
request.input('SupportCallFlowId', req.body.supportCallFlow);
request.input('OverallChangeStatus', 1);
let changeId = request.output('changeId', sql.Int);
request.execute('dbo.InsertChange').then(function (result) {
console.dir(result);
}).catch(function (err) {
console.dir(err);
});
async.each(
req.body.changeType
,function iterator(item, next) {
const requestCT = new sql.Request(transaction);
requestCT.input('ChangeId', changeId);
requestCT.input('TypeOfChangeId', item.typeOfChangeId);
requestCT.input('Description', item.description);
requestCT.execute('dbo.InsertTypeOfChange').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
async.each(
req.body.serviceImpacted
,function iterator(item, next) {
const requestSI = new sql.Request(transaction);
requestSI.input('ChangeId', changeId);
requestSI.input('ServicesImpactedId', item.serviceImpactedId);
requestSI.execute('dbo.InsertImpactedService').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
async.each(
req.body.businessImpacted
,function iterator(item, next) {
const requestBI = new sql.Request(transaction);
requestBI.input('ChangeId', changeId);
requestBI.input('BusinessImpactedId', item.businessImpactedId);
requestBI.execute('dbo.InsertImpactedBusiness').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
async.each(
req.body.criticalApp
,function iterator(item, next) {
const requestCA = new sql.Request(transaction);
requestCA.input('ChangeId', changeId);
requestCA.input('CriticalBankingId', item.criticalId);
requestCA.input('Description', item.description);
requestCA.execute('dbo.InsertCriticalBankingApp').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
async.each(
req.body.testStages
,function iterator(item, next) {
const requestTS = new sql.Request(transaction);
requestTS.input('ChangeId', changeId);
requestTS.input('TestStageId', item.testStageId);
requestTS.input('TestDate', item.testDate);
requestTS.input('Description', item.description);
requestTS.execute('dbo.InsertChangeTest').then(function (result) {
console.dir(result);
next();
}).catch(function (err) {
console.dir(err);
});
})
if (err) {
if (!rolledBack) {
transaction.rollback(err => {
console.dir(err);
// ... error checks
})
}
} else {
transaction.commit(err => {
console.dir(err);
// ... error checks
})
}
})
});
There really isn't a heck of a lot of material online anywhere for processing transactions with stored procedures, and even less for big transactions like this. But the error I'm getting is the following multiple times in my terminal log.
{ TransactionError: Can't acquire connection for the request. There is another request in progress.
at Transaction.acquire (C:\Users\Redirection\meecd26\Documents\GitRepo\td-angular-starter\node_modules\mssql\lib\base.js:740:30)
at Immediate._query.err [as _onImmediate] (C:\Users\Redirection\meecd26\Documents\GitRepo\td-angular-starter\node_modules\mssql\lib\msnodesqlv8.js:417:19)
at runCallback (timers.js:781:20)
at tryOnImmediate (timers.js:743:5)
at processImmediate [as _immediateCallback] (timers.js:714:5) code: 'EREQINPROG', name: 'TransactionError' }
This certainly suggests that I'm performing the transaction improperly.
Would someone be able to help guide me as to how I can improve and fix this?
Edit: Made changes to my code to reflect what I found here, as well as a few changes to my first insert. While my first insert does work, I'm still getting the repeated errors above, so now I know my problems are with how I'm implementing the inserts inside of loops. Is there a way I can close the connection upon each insert? Would that be the approach I should try and take?
I ended up getting it working, and although my understanding of how everything works admittedly hasn't improved much at all, I can at least post what I have in hopes of others hopefully finding it helpful.
router.post('/insertChange', (req, res) => {
console.log("Beginning of POST, before initializing transaction");
let changeId;
let mainInsert = false;
beginTransaction(function (err, rollback, transaction) {
if (err) {
// return cb(err);
}
let request = new sql.Request(transaction);
// request.verbose = true;
request.input('ChangeTitle', req.body.ChangeTitle);
request.input('TypeId', req.body.TypeId);
request.input('DateSubmitted', req.body.DateSubmitted);
request.input('TargetDate', req.body.TargetDate);
request.input('ChangeSponsor', req.body.ChangeSponsor);
request.input('UATDate', req.body.UATDate);
request.input('ChangeSponsorEmail', req.body.ChangeSponsorEmail);
request.input('ClarityId', req.body.ClarityId);
request.input('ChangeDescription', req.body.ChangeDescription);
request.input('ComponentName', req.body.ComponentName);
request.input('ComponentDescription', req.body.ComponentDescription);
request.input('ReasonForChange', req.body.ReasonForChange);
request.input('ComponentReplacing', req.body.ComponentReplacing);
request.input('DependentChange', req.body.DependentChange);
request.input('InstallOption', req.body.InstallOption);
request.input('RebootRequired', req.body.RebootRequired);
request.input('UserIntervention', req.body.UserIntervention);
request.input('Activation', req.body.Activation);
request.input('ContingencyPlan', req.body.ContingencyPlan);
request.input('AdditionalInformation', req.body.AdditionalInformation);
request.input('PerformanceImpact', req.body.PerformanceImpact);
request.input('IsPCoERequired', req.body.IsPCoERequired);
request.input('IsCanadianRetailBranch', req.body.IsCanadianRetailBranch);
request.input('BusinessAppImpact', req.body.BusinessAppImpact);
request.input('NetworkImpact', req.body.NetworkImpact);
request.input('NewInfrastructure', req.body.NewInfrastructure);
request.input('LogonTime', req.body.LogonTime);
request.input('AdditionalTechnicalInformation', req.body.AdditionalTechnicalInformation);
request.input('IsProdIssue', req.body.IsProdIssue);
request.input('ProdIssue', req.body.ProdIssue);
request.input('ProdIncidentNum', req.body.ProdIncidentNum);
request.input('IsMajorChange', req.body.IsMajorChange);
request.input('HasRequiredTesting', req.body.HasRequiredTesting);
request.input('HasPackageSubmit', req.body.HasPackageSubmit);
request.input('SignOffETA', req.body.SignOffETA);
request.input('SpecificTesting', req.body.SpecificTesting);
request.input('SpecificPilot', req.body.SpecificPilot);
request.input('PilotInfo', req.body.PilotInfo);
request.input('UserChanges', req.body.UserChanges);
request.input('ServiceDeskProcedure', req.body.ServiceDeskProcedure);
request.input('SupportCallFlowId', req.body.SupportCallFlowId);
request.input('OverallChangeStatus', 1);
request.output('changeId', sql.Int);
request.execute('dbo.InsertChange', function (err, callback) {
if (err) {
console.dir(err);
rollback(err);
res.status(400).send("Failed to submit change form.");
} else {
mainInsert = true;
}
if (callback) {
changeId = +callback.returnValue;
}
if (mainInsert) {
async.each(
req.body.changeType, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('TypeOfChangeId', item.typeOfChangeId);
request.input('Description', item.description);
request.execute('dbo.InsertTypeOfChange', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
})
async.each(
req.body.serviceImpacted, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('ServicesImpactedId', item.serviceImpactedId);
request.execute('dbo.InsertImpactedService', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
})
async.each(
req.body.businessImpacted, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('BusinessImpactedId', item.businessImpactedId);
request.execute('dbo.InsertImpactedBusiness', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
})
async.each(
req.body.criticalApp, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('CriticalBankingId', item.criticalId);
request.input('Description', item.description);
request.execute('dbo.InsertCriticalBankingApp', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
})
async.each(
req.body.testStages, function iterator(item, next) {
request = new sql.Request(transaction);
request.input('ChangeId', changeId);
request.input('TestStageId', item.testStageId);
request.input('TestDate', item.testDate);
request.input('Description', item.description);
request.execute('dbo.InsertChangeTest', function (err, callback) {
if (err) return next(err);
console.dir(callback);
next();
})
}, function fin(err) {
if (err) {
rollback(err);
return; //done
}
else {
transaction.commit(function (err) {
if (err) {
console.error('Transaction commit error: ', err);
res.status(400).send("Failed to submit change form.");
} else {
console.error('Transaction commit success');
res.send({ "Response": "Success", "Message": "Change form successfully submitted." });
}
});
}
})
}
});
})
});
[TypeError: Cannot read property 'rid' of undefined]
Is the error that I get when I try to execute this controller on my post route.
I've tested it out with Postman.
I've tried to console.log(result) but I get undefined.
My query gets executed and my row is inserted into my table. I've checked it. Password is also hashed.
The problem is that I don't get any out binds that should be returned.
Problematic code (IMO) is
...
.then(function(result) {
console.log(result);
cb(null, {
id: result.outBinds.rid[0],
email: result.outBinds.remail[0],
role: result.outBinds.rrole[0]
});
})
...
oracle-NodeDB Wrapper
var oracledb = require('oracledb');
module.exports.OBJECT = oracledb.OBJECT;
function executeSQL(config ,sql, bindParams , options) {
return new Promise(function(resolve, reject) {
oracledb.getConnection(
config,
function(err, connection) {
if (err) {
return reject(err);
}
connection.execute(
sql,
bindParams,
options,
function(err, result) {
if (err) {
doRelease(connection);
return reject(err);
}
resolve(result);
doRelease(connection);
});
});
});
}
function doRelease(connection) {
connection.release(
function(err) {
if (err) {
console.log(err.message);
}
}
);
}
module.exports.executeSQL = executeSQL;
Controller
var database = require('../database/oracledbWrapper');
var dbconfig = require('../database/dbconfig').dbconfig;
var jwt = require('jsonwebtoken');
var bcrypt = require('bcrypt');
exports.createUser = function(req, res, next) {
var user = {
email: req.body.email
};
var unhashedPassword = req.body.password;
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(unhashedPassword, salt, function(err, hash) {
if (err) {
return next(err);
}
user.hashedPassword = hash;
insertUser(user, function(err, user) {
var payload;
if (err) {
return next(err);
}
payload = {
sub: user.email,
role: user.role
};
res.status(200).json({
user: user,
token: jwt.sign(payload, config.jwtSecretKey, {expiresInMinutes: 60})
});
});
});
});
}
function insertUser(user, cb) {
var bindParams = {
email: user.email.toLowerCase(),
password: user.hashedPassword,
rid: {
type: database.NUMBER,
dir: database.BIND_OUT
},
remail: {
type: database.STRING,
dir: database.BIND_OUT
},
rrole: {
type: database.STRING,
dir: database.BIND_OUT
}
};
database.executeSQL(
dbconfig,
'insert into express_users (email, password, role ) values ( :email, :password, \'BASE\' ) returning id, email, role into :rid , :remail, :rrole',
bindParams,
{}
)
.then(function(result) {
console.log(result);
cb(null, {
id: result.outBinds.rid[0],
email: result.outBinds.remail[0],
role: result.outBinds.rrole[0]
});
})
.catch(function(err) {
console.log(err);
next(err);
});
}
Route
var RESTfulAPICon = require('../controllers/RESTfulAPI');
var indexCon = require('../controllers/index');
var views = require('express').Router();
views.route('/users').post(RESTfulAPICon.createUser);
exports.views = views;
The problem was in my wrapper , mainly here
module.exports.OBJECT = oracledb.OBJECT;
I export only the OBJECT property , but I try to access BIND_OUT properties later on. And they are non existent.
If I do the full export like this
module.exports.OBJECT = oracledb;
Then I can access BIND_OUT properties.
I can use the following to save a new document to my mongodb database.
User.prototype.save = function (fn) {
var user = new userModel({
user: this.user,
pass: this.pass
});
console.log('user: ' +user);
this.hashPassword (user.pass, function (err, salt, hash) {
if (err) return fn (err);
this.pass = hash;
user.salt = salt;
user.pass = hash;
user.save (function (err, product, numberAffected) {
if (err) return fn (err);
return fn(undefined);
});
});
};
Now I'm trying to check if the user already exists before saving it
User.prototype.save = function (fn) {
// See if the username exists
userModel.findOne ({ 'user': this.user }, function (err, user) {
if (err) return fn (err);
if (!user) {
user = new userModel({
user: this.user,
pass: this.pass
});
console.log('user: ' +user);
this.hashPassword (user.pass, function (err, salt, hash) {
if (err) return fn (err);
this.pass = hash;
user.salt = salt;
user.pass = hash;
user.save (function (err, product, numberAffected) {
if (err) return fn (err);
return fn(undefined);
});
});
} else {
// TODO: update all the user fields
console.log ('user already exists');
}
});
}
this.hashPassword is no longer found TypeError: Object #<Promise> has no method 'hashPassword' and the fields of user are now undefined. How can I use this within these callbacks?
EDIT 1:
Taking a closer look I also notice that this.pass = hash; in the first snippet also does nothing to the object I care about.
on line 2
var self = this
then,
self.hashPassword()
In full:
User.prototype.save = function (fn) {
var self = this;
// See if the username exists
userModel.findOne ({ 'user': self.user }, function (err, user) {
if (err) return fn (err);
if (!user) {
user = new userModel({
user: self.user,
pass: self.pass
});
console.log('user: ' +user);
self.hashPassword (user.pass, function (err, salt, hash) {
if (err) return fn (err);
self.pass = hash;
user.salt = salt;
user.pass = hash;
user.save (function (err, product, numberAffected) {
if (err) return fn (err);
return fn(undefined);
});
});
} else {
// TODO: update all the user fields
console.log ('user already exists');
}
});
}
I have the following code:
var Company = function(app) {
this.crypto = require('ezcrypto').Crypto;
var Company = require('../models/company.js');
this.company = new Company(app);
}
// Create the company
Company.prototype.create = function (name, contact, email, password, callback) {
this.hashPassword(password, function(err, result) {
if (err) throw err;
console.log(this.company); // Undefined
this.company.create(name, contact, email, result.password, function(err, result) {
if (err) {
return callback(err);
}
return callback(null, result);
});
});
}
// Get company with just their email address
Company.prototype.hashPassword = function (password, callback) {
if(typeof password !== 'string') {
var err = 'Not a string.'
} else {
var result = {
password: this.crypto.SHA256(password)
};
}
if (err) {
return callback(err);
}
return callback(null, result);
}
module.exports = Company;
The problem is that this.company is undefined on line 11 of that code block.
I know this is not what I think, but I'm not sure how to refactor to get access to the correct this.
so theres 2 solution's to this
first the dirty one
Company.prototype.create = function (name, contact, email, password, callback) {
var that = this; // just capture this in the clojure <-
this.hashPassword(password, function(err, result) {
if (err) throw err;
console.log(that.company); // Undefined
that.company.create(name, contact, email, result.password, function(err, result) {
if (err) {
return callback(err);
}
return callback(null, result);
});
});
}
and the clean one using bind https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Company.prototype.create = function (name, contact, email, password, callback) {
this.hashPassword(password, (function(err, result) {
if (err) throw err;
console.log(this.company); // Undefined
this.company.create(name, contact, email, result.password, function(err, result) {
if (err) {
return callback(err);
}
return callback(null, result);
});
}).bind(this));
}
You can reference this through another variable by declaring it in the Company.create scope, like this:
// Create the company
Company.prototype.create = function (name, contact, email, password, callback) {
var me = this;
this.hashPassword(password, function(err, result) {
if (err) throw err;
console.log(me.company); // Undefined - not anymore
me.company.create(name, contact, email, result.password, function(err, result) {
if (err) {
return callback(err);
}
return callback(null, result);
});
});
}
Untested, but it should work like this.