Mongoose Error when using findOneAndUpdate - node.js

I have received an error at my server console during updating my Note collection, the update is successful but I receive this error in my server console, felt something wrong. Thanks in advance
app.put("/todos/:id", async(req,res) => {
try {
// console.log(req.body);
const { id } = req.params;
const { title, content } = req.body.edit;
const edit = await Note.findOneAndUpdate({_id:id}, {
title: title,
content: content
},
function (err, docs) {
if(!err){
console.log("Successfully edit item:" + docs);
const response = res.json(docs);
}else{
console.error(err);
}
})
// Example: Update name to Gourav
// User.findByIdAndUpdate(user_id, {
// name: 'Gourav'
// },
// function (err, docs) {
// if (err) {
// console.log(err)
// } else {
// console.log("Updated User : ", docs);
// }
// });
} catch (error) {
console.error(error);
}
});
Error msg:
MongooseError: Query was already executed: Note.findOneAndUpdate({
_id: new ObjectId("61580e469338c1fc3...
at model.Query._wrappedThunk [as _findOneAndUpdate] (C:\Users\xx\Desktop\MernToDoV3\server\node_modules\mongoose\lib\helpers\query\wrapThunk.js:21:19)
at C:\Users\xx\Desktop\MernToDoV3\server\node_modules\kareem\index.js:370:33
at processTicksAndRejections (internal/process/task_queues.js:77:11) { originalStack: 'Error\n' +
' at model.Query._wrappedThunk [as _findOneAndUpdate] (C:\Users\xx\Desktop\MernToDoV3\server\node_modules\mongoose\lib\helpers\query\wrapThunk.js:25:28)\n'
+
' at C:\Users\xx\Desktop\MernToDoV3\server\node_modules\kareem\index.js:370:33\n'
+
' at processTicksAndRejections (internal/process/task_queues.js:77:11)' }

I had similar issue with this query query.limit(resPerPage).skip(skip).
I chained clone() and it worked
query.limit(resPerPage).skip(skip).clone()

You can do something like this. You don't need to use the callbacks instead use async/await or then/catch.
When you apply try/catch to the async code, it doesn't go well. They are typically used to catch the errors when the code is synchronous by using different means. Promises provided a way to catch the error using catch operator.
app.post("/todos", async (req,res)=>{
try{
// const small = new Tank({ size: 'small' });
// small.save(function (err) {
// if (err) return handleError(err);
// // saved!
// });
console.log(req.body); //should use console to see
const { title, content} = req.body.newNote,
noteInput = new Note({title, content });
const result = await noteInput.save();
if (result) {
// check if the result exist else do something
// if you don't find anything
}
}catch(err){
// any errors in the save will be catch here automatically
console.error(err.message);
}
});
Note: When you make a function async, you need to use the await keyword to wait for the results for the async portion in the function.

Related

Error: DPI-1002: invalid dpiConn handle: Nodejs, Oracledb

I have the following API, the API is inserting into a table based on user selection from the client. User can select different material belonging to same experiment. In my payload, I have materials as array, experiment as string. I tried several ways to resolve my error. Following was the last try:
app.post("/insertMaterials", (req, res) => {
for (let mat of req.body["material"]) {
try {
oracledb.getConnection(
{
user: "some_user",
password: "some_pw",
connectString: "someConnStr",
},
function (err, connection) {
if (err) {
console.error("1" + err);
return;
}
connection.execute(
"INSERT INTO MATERIALS (ID, MAT_NAME, EXPR) VALUES((SELECT max(ID) + 1 FROM MATERIALS), :1, :2)",
[mat, req.body["experiment"]],
(err, result) => {
if (err) {
console.error("log " + err);
}
connection.commit();
connection.close();
}
);
}
);
} catch (error) {
console.log(error);
}
}
return res.status(200).json({
title: "SUCCESS: Materials Inserted",
});
});
I always get:
triggerUncaughtException(err, true / fromPromise /);
^
[Error: DPI-1002: invalid dpiConn handle] { errorNum: 0, offset: 0 }
Before I had a separate function of the block inside the for loop and I also tried with execeuteMany. Still same error. After trying lot other ways and reading in internet, I couldn't solve the issue. Except for finally catching uncaughtException and logging the error:
process.on('uncaughtException', (error, next) => {
let date = new Date()
errorLogStream.write(`Date: ${date}. Err: ${error.stack} \n`)
return
})
By catching this exception, my program does not break anymore and data is always inserted. But it would be great to know how and when this is raised and how this can be resolved or where if I am doing a mistake.
UPDATE
Payload example: {'material': ['F99999.7', 'J84845.4'], 'experiment': 'NA32R'}
Function:
async function addMatToExpr(exp, mat) {
let connection;
try {
connection = await oracledb.getConnection(
{
user: "some_user",
password: "some_pw",
connectString: "someConnStr",
});
result = await connection.execute("INSERT INTO MATERIALS (ID,
MAT_NAME, EXPR) VALUES((SELECT max(ID) + 1 FROM MATERIALS), :1, :2)",
[exp, mat], { autoCommit: true })
} catch (error) {
return res.status(404).json({
title: error,
});
} finally {
if (connection) {
try {
await connection.close()
} catch(error) {
console.log(error)
}
}
}
}
API:
app.post("/insertMaterials", (req, res) => {
for (let mat of req.body["materials"]) {
addMatToExpr(req.body["experiment"], mat)
}
});
Added the async/await function and the api that calls the function.
You need to 'await' the Oracle function calls so each completes before continuing. Currently the connection is being closed before the statement is executed.
See all the node-oracledb documentation and examples.
E.g.
async function run() {
let connection;
try {
connection = await oracledb.getConnection(dbConfig);
result = await connection.execute(sql, binds, options);
console.dir(result, { depth: null });
} catch (err) {
console.error(err);
} finally {
if (connection) {
try {
await connection.close();
} catch (err) {
console.error(err);
}
}
}
}

Refactoring RESTful API into smaller functions

Background
I have a NodeJS app that is meant to be used as a RESTful API. It is connected with a MongoDB database in the backend using Mongoose. The app is built upon the idea of nested documents. It stores wikis, sections and notes with the following schema:
const noteSchema = new mongoose.Schema({ title: String, content: String });
const sectionSchema = new mongoose.Schema({ title: String, notes: [noteSchema] });
const wikiSchema = new mongoose.Schema({ title: String, sections: [sectionSchema] });
All of which are accessed via a single model of the wiki:
const wikiModel = mongoose.model("Wiki", wikiSchema);
A user can do GET, POST, PUT, DELETE requests on each of the endpoints to manipulate the data inside. If someone wants to ping the Notes endpoint (the furthest down in the hierarchy), it must first check the wiki and then the section endpoint, to ensure that each of them exists.
Here's an example:
app.get('/:wikiTitle/:sectionTitle/:noteTitle', function(req, res) {
wikiModel.findOne({ title: req.params.wikiTitle }, function(err, wiki) {
if (err) {
res.send('\nAn unkown error has occured');
console.error(err);
} else if (wiki) {
const sectionTitle = req.params.sectionTitle;
wikiModel.findOne({ 'sections.title': sectionTitle }, function(err, section) {
if (err) {
res.send('\nAn unkown error has occured');
console.error(err);
} else if (section) {
const noteTitle = req.params.noteTitle;
wikiModel.findOne({ 'sections.notes.title': noteTitle }, function(err, n) {
if (err) {
res.send('\nAn unkown error has occured');
console.error(err);
} else if (n) {
const section = n.sections.find((s) => { return s.title === sectionTitle; });
const note = section.notes.find((n) => { return n.title === noteTitle; });
if (note.content) {
res.send('\n' + note.title + '\n\n' + note.content);
} else {
res.send('\n' + note.title + '\n\n[ No content to show ]');
}
} else {
res.send('\nNo such note exists');
}
});
} else {
res.send('\nNo such section exists');
}
});
} else {
res.send('\nNo such wiki exists');
}
});
});
This is a very lengthy method and the first two queries are actually frequently throughout the app. I also understand a MongoDB query is an asynchronous operation and thus, why I put each consequent MongoDB query within it's parent (the one I wish to finish before that one begins).
Question
Is there a way to split each MongoDB query into its own method or introduce promises in a way that would shorten the code? I would rather prefer advice that ultimately causes the splitting of my code into individual methods as what you see above is one of many endpoints which all use the same queries.
So in the end result I would like to have something close to the likes of:
app.get('/:wikiTitle/:sectionTitle/:noteTitle', function(req, res) {
if (getWiki(req.params.wikiTitle)) {
// Continue with second query
if (getSection(req.params.sectionTitle)) {
// Continue with third query...
}
}
});
function getWiki(wikiTitle) {
wikiModel.findOne({ title: wikiTitle }, function(err, wiki) {
if (err) {
console.error(err);
res.send('An unknown error occured.');
} else if (wiki) {
// Send OK result to continue to next query
return wiki
} else {
res.send('No wiki found');
return null;
}
});
}
function getSection(sectionTitle) {
wikiModel.findOne({ 'sections.title': sectionTitle }, function(err, section) {
if (err) {
console.error(err);
res.send('An unknown error occured.');
} else if (section) {
// Send OK result to continue to next query
return section
} else {
res.send('No section found');
return null;
}
});
}
I am hoping this will significantly cut the length of code and also utilise re-usability of code. Any advice on how I could come close to achieving something like this is welcome.
You can definitely use callbacks in the same way as the ones call your model. For example:
app.get('/:wikiTitle/:sectionTitle/:noteTitle', function(req, res) {
getWiki(req.params.wikiTitle, function (err, title) {
if (err) {
return res.send(err);
}
getSection(req.params.sectionTitle, function (err, section) {
if (err) {
return res.send(err);
}
// Todo: use title and section, etc...
});
});
});
function getWiki(wikiTitle, cb) {
wikiModel.findOne({ title: wikiTitle }, function(err, wiki) {
if (err) {
console.error(err);
return cb('An unknown error occured.');
} else if (wiki) {
// Send OK result to continue to next query
return cb(null, wiki);
} else {
return cb('No wiki found');
}
});
}
function getSection(sectionTitle, cb) {
wikiModel.findOne({ 'sections.title': sectionTitle }, function(err, section) {
if (err) {
console.error(err);
return cb('An unknown error occured.');
} else if (section) {
// Send OK result to continue to next query
return cb(null, section);
} else {
return cb('No section found');
}
});
}
This is a standard way of using async functions in node. By convention, the first parameter is always an error parameter.
If you want your code to be cleaner, you can try to use guard clauses / early outs to exit error cases early. This will cut down on your need for if / else conditional statements.
You can also look into libraries like async for cleaner chaining of asynchronous calls.
When you are comfortable, you can also look into using promises and the 'async' javascript keyword (different from the async library above, confusing, I know) which will also allow you to cut down on the lines of code you have to write to get nice async code.
You should use async functions (Promises) like
app.get('somePath', async (req, res, next) => {
try {
const doc = await model.find({ someField: 'some value' }).exec(); // exec returns promise
res.send({ document: doc });
} catch (error) {
// here you can handle all errors or/and call next for the error middleware
next(error);
}
});

model.update() not updating

I'm trying to add a new element to an array.
This is the code:
var findEditThenSave = function(personId, done) {
var foodToAdd = 'hamburger';
var foodArray = [];
Person.findById(personId, function (err, data) {
if (err) return console.log(err);
done(null, data);
foodArray = data.favoriteFoods;
console.log("foodArray inside findById: ", foodArray);
foodArray.push(foodToAdd);
var updateObj = {favoriteFoods: foodArray};
console.log(updateObj)
Person.update({_id: personId}, updateObj, function(err, raw) {
if (err) {
console.log("There was an error");
}
console.log("Updated successfully");
console.log("foodArray inside update function:", foodArray);
});
});
};
This is the whole code on Glitch: https://glitch.com/edit/#!/holly-maroon-pony?path=myApp.js%3A226%3A0
This is the console log for a POST request:
POST
foodArray inside findById: ["spaghetti"]
{ favoriteFoods: ["spaghetti","hamburger"] }
(node:8943) DeprecationWarning: collection.update is deprecated. Use updateOne, updateMany, or bulkWrite instead.
Updated successfully
foodArray inside update function: ["spaghetti","hamburger"]
You can use async and await while making these updates.
var findEditThenSave = async function(personId, done){
var foodToAdd = 'hamburger';
var foodArray = [];
var updateObj;
try{
var data=await Person.findById(personId);
done(null, data);
foodArray = data.favoriteFoods;
console.log("foodArray inside findById: ", foodArray);
foodArray.push(foodToAdd);
updateObj = {favoriteFoods: foodArray};
console.log(updateObj)
}catch(err){
console.log(err);
}
try{
await Person.update({_id: personId}, updateObj);
console.log("Updated successfully");
console.log("foodArray inside update function:", foodArray);
}catch(err){
console.log(err);
}
};
As you can see in your console:
(node:8943) DeprecationWarning: collection.update is
deprecated. Use updateOne, updateMany, or bulkWrite instead.
Updated successfully
So you can get through this using Person.updateOne instead of Person.update
Hope it helps
If you are just planning to update a document and return the updated document you can do something like this
function update(personId) {
var foodToAdd = "hamburger";
const person = Person.findByIdAndUpdate(
personId,
{ $push: { favoriteFoods: foodToAdd } },
{ new: true },
function (err, result) {
if (err) console.log(err);
else console.log(result);
}
);
};

I cant fetch data from odoo calendar using intent of dialogflow and webhook

I want to list the event of odoo calendar in my chatbot created in dialogflow and using webhook.
This is my code :
function GetEventOdoo(agent) {
let username =agent.parameters.username;
var odooResult;
var odoo = new Odoo({
url: 'xxxxxxxxx',
port: 'xxxxxxxxxx',
db: 'xxxxxxxxxxxx',
username: 'xxxxxxxx#example.com',
password: 'xxxxxxxxxxxxx'
});
odooResult = JSON.stringify(odoo);
console.log('odooResult!!!:' + odooResult );
odoo.connect(function (err) {
if (err) {
return console.log('connection error: '+ JSON.stringify(err)); }
console.log('Connected to Odoo server.');
var inParams = [];
inParams.push([]); //where conditions
inParams.push(['name']); //fields
inParams.push(0); //offset
inParams.push(5); //limit
var params = [];
params.push(inParams);
odoo.execute_kw('calendar.event', 'search_read', params, function (err, value){
if (err) { return console.log(err); }
console.log(value);
});
})
return odooResult += JSON.stringify(value)
// eslint-disable-next-line promise/always-return
.then(res => {
const launchData = res.data;
agent.add(`Odoo event is: ${launchData[0].ID} `);
console.log(`Odoo event is: ${launchData[0].ID} `);
});
}
I can't connect with odoo to list data, i try out of dialogflow and works good, but here it's not working , maybe i have to correct the function to list this data from webhook to dialogflow.
Webhook call failed. Error: DEADLINE_EXCEEDED
Error: No handler for requested intent
at WebhookClient.handleRequest (/srv/node_modules/dialogflow-fulfillment/src/dialogflow-fulfillment.js:327:29)
at exports.dialogflowFirebaseFulfillment.functions.https.onRequest (/srv/index.js:118:9)
at cloudFunction (/srv/node_modules/firebase-functions/lib/providers/https.js:49:9)
at /worker/worker.js:783:7
at /worker/worker.js:766:11
at _combinedTickCallback (internal/process/next_tick.js:132:7)
at process._tickDomainCallback (internal/process/next_tick.js:219:9)
Edited:
I just uploaded my code like this*
function findeventlist(username) {
return new Promise((resolve, reject) => {
const odoo = new Odoo({
url: 'http://sssss',
port: '80',
db: 'ssss',
username: 'sssssssss',
password: 'sssssssss'
});
//odooResult = JSON.stringify(odoo);
odoo.connect(function (err) {
if (err) {
return console.log(err);
}
console.log('findeventlist Connected to Odoo server.');
const params = [];
params.push([]);
params.push(['name']);
params.push(0);
params.push(5);
params.push([params]);
odoo.execute_kw('calendar.event', 'search_read', params, function (err, value) {
if (err) {
return console.log(err);
}
console.log('Result: ', value);
resolve.send(JSON.stringify(value));
})
.catch(error => {
console.log(error);
reject(error);
});
});
});
}
function GetEventOdoo(agent) {
let username = agent.parameters.username;
console.log("GetEventOdoo");
return findeventlist().then(() => {
agent.add(`Event: ${username}, 😊`);
}).catch(() => {
agent.add(` 😔 `);
});
}
Error is : Error: memory limit exceeded. Function invocation was interrupted.
You are mixing up how you use Promises with other methods. This is certainly not returning what you expect, likely causing the response to not be sent back, and could be causing other issues such as memory leaks.
For example, in a few places in your code, inside the Promise, you have
if (err) {
return console.log(err);
}
All this does is quit out of the Promise without calling either resolve() or reject(), but it has returned that Promise to the Intent dispatcher, which is waiting for it to either resolve or reject, which never happens.
To fix this, instead of returning the console log (?), you should explicitly call reject() in these cases. So your code might look something like
if (err) {
reject( err );
}
This is only this complicated because you are wrapping calls to odoo in the Promise yourself. I don't know which library you're using, but you may wish to use one that works with Promises natively. For example, the odoo-api library lets you write things such as
function findeventlist(){
return odoo
.connect({
database: 'unicorn',
username: 'foo',
password: 'bar'
})
.then(client => {
return client.searchRead('product.product', [['list_price', '>', '50']], {limit: 1});
})
.then(products => {
console.log(products);
//=> [{list_price: 52, name: 'Unicorn'}]
agent.add( `I found ${products.length} results` );
});
};
without having to create or manage the Promise yourself. Using this library would even let you use async/await in the more recent versions of node, so this could even be written as something like (untested)
async function findeventlist(){
const client = await odoo.connect( connectionParameters );
const products = await client.searchRead('product.product', [['list_price', '>', '50']], {limit: 1});
console.log( products );
agent.add( `I found ${products.length} results` );
}

Error: Callback was already called in loopback

I have the following code:
"use strict";
const Raven = require("raven");
Raven.config(
"test"
).install();
module.exports = function(Reservation) {
function dateValidator(err) {
if (this.startDate >= this.endDate) {
err();
}
}
function sendEmail(campground) {
return new Promise((resolve, reject) => {
Reservation.app.models.Email.send(formEmailObject(campground),
function(
err,
mail
) {
if (err) {
console.log(err);
Raven.captureException(err);
reject(err);
} else {
console.log(mail);
console.log("email sent!");
resolve(mail);
}
});
});
}
function formEmailObject(campground) {
return {
to: "loopbackintern#yopmail.com",
from: "noreply#optis.be",
subject: "Thank you for your reservation at " + campground.name,
html:
"<p>We confirm your reservation for <strong>" +
campground.name +
"</strong></p>"
};
}
Reservation.validate("startDate", dateValidator, {
message: "endDate should be after startDate"
});
Reservation.observe("after save", async function(ctx, next) {
try {
const campground = await Reservation.app.models.Campground.findById(
ctx.instance.campgroundId
);
const mail = await sendEmail(campground);
next();
} catch (e) {
Raven.captureException(e);
next(e);
}
});
};
Sorry for the poor formatting. When the flow is done I get this error:
(node:3907) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Callback was already called.
I am calling the next() callback in two places, one in the try code and one in the catch code. I assume that when it all goes right, next callback is called only once, and the same when it goes wrong. But it seems that it is called twice and I don't know why.
I also tried to call next outside the try/catch code but it results in the same error. If I left only the next that is called inside the catch code it doesn't throw the error.
Any idea? Thanks!
if you are using async function you shouldn't explicitly call next, it gets automatically called.
check out this github issue for loopback async/await
so your hook can be like the following.
Reservation.observe("after save", async ctx => {
try {
const campground = await Reservation.app.models.Campground.findById(
ctx.instance.campgroundId
);
const mail = await sendEmail(campground);
} catch (e) {
Raven.captureException(e);
throw e;
}
});
NB: you don't need to wrap it in try catch unless you want to modify/work with the error.
You should declare your sendEmail method as async as it returns a promise.
async function sendEmail(campground) {
...
}
After reading this article, I created a await-handler.js file which include following code.
module.exports = (promise) =>
promise
.then(data => ({
ok: true,
data
}))
.catch(error =>
Promise.resolve({
ok: false,
error
})
);
Then in MyModel.js file, I created a async function to get a value from database as follow.
const awaitHandler = require("./../await-handler.js")
const getMaxNumber = async (MyModel) => {
let result = await awaitHandler(MyModel.find());
if (result.ok) {
if (result.data.length) {
return result.data.reduce((max, b) => Math.max(max, b.propertyName), result.data[0] && result.data[0].propertyName);
} else {
return 0;
}
} else {
return result.error;
}
}
As per #Mehari's answer, I've commented call to next() method as follow:-
module.exports = function(MyModel) {
MyModel.observe('before save', async(ctx, next) => {
const maxNumber = await getMaxNumber (MyModel);
if(ctx.instance) {
...
set the required property using ctx.instance.*
like createdAt, createdBy properties
...
// return next();
} else {
...
code for patch
...
// return next();
}
})
}
This solves the warning issue whenever saving endpoint is triggered.
But the warning issue still appear when I run the endpoint to load the resource.Like
http://localhost:3000/api/MyModel
Previously, the issue appear only when the before save operation hook gets triggered.
After encountering this issue, I checked adding access and loaded operation hooks and I found that the the warnings are issued after loaded operation hook.
MyModel.observe('access', (ctx, next) => {
return next();
})
MyModel.observe('loaded', (ctx, next) => {
return next();
})
What could have caused this issue and how can it gets resolved?

Resources