node.js + mongoose + HTTP PUT Not Working - node.js

I'm following scotch's tutorial, so I have this user schema:
var userSchema = mongoose.Schema({
local : {
email : String,
password : String,
},
facebook : {
id : String,
token : String,
email : String,
name : String
},
twitter : {
id : String,
token : String,
displayName : String,
username : String
},
google : {
id : String,
token : String,
email : String,
name : String
}
});
I'm trying to make a http put to update some data with x-www-form-urlencoded, but I'm not able to set the fields, this is what I have:
PUT /teacherup HTTP/1.1
Host: localhost:8080
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
email=example%40gmail.com&password=randompass
How can I make an appropriate http put and set those fields? I would also like to know how to do this with JSON.
-- Update with put
Here's the http put:
app.put('/teacherup', isLoggedIn, function(req, res) {
if(req.user.usertype == 1)
{
util.updateDocument(req.user, userschema, req.body);
req.user.save(function(err) {
if (err)
throw err;
});
res.send(200, {message : 'Teacher saved!'});
}
else
{
res.send(406, {message : 'Not a teacher!'});
}
});
-- Update methods for saving doc
I'm using these methods for updating a doc
exports.updateDocument = function(doc, SchemaTarget, data) {
for (var field in SchemaTarget.schema.paths) {
if ((field !== '_id') && (field !== '__v')) {
var newValue = getObjValue(field, data);
console.log('data[' + field + '] = ' + newValue);
if (newValue !== undefined) {
setObjValue(field, doc, newValue);
}
}
}
return doc;
};
function getObjValue(field, data) {
return _.reduce(field.split("."), function(obj, f) {
if(obj) return obj[f];
}, data);
}
function setObjValue(field, data, value) {
var fieldArr = field.split('.');
return _.reduce(fieldArr, function(o, f, i) {
if(i == fieldArr.length-1) {
o[f] = value;
} else {
if(!o[f]) o[f] = {};
}
return o[f];
}, data);
}

I am showing you the server side code for inserting a new document using 'express' and 'mongoose'. Hope this will helps you.
var app = require('express)(),
mongoose=require('mongoose'),
userModel=mongoose.model("user", userSchema);
app.put('/teacherup', update);
update=function(req,res){
new userModel
.save(object)//I have not created the object you have to create it according to yourschema
.exec(function(err, result) {
if (err) {
console.log(err);
} else {
console.log("Successfully inserted");
}
});
}

Related

Problem with Mongoose pre.save() generating specific codes on record creation

I have a Mongo collection with this properties:
{
"_id" : ObjectId("384f1f06f72cc1b566e32f98"),
"num" : 41,
"product" : ObjectId("5c8921d8f9f7be241c0b02cf"),
"data" : {
"phone" : "123123123",
"email" : "email#email.com",
"name" : "John",
"_id" : ObjectId("5ca34689ac024b579991fe26")
},
"generatedCode": "01-1FCS3";
}
Depending on the type of product (it's an object as you can see), I want to generate a different code every time a new element is created
ProductSchema.pre('save', function(next) {
if ((typeof this.generatedCode === 'undefined') || !this.generatedCode) {
ProductSchema.find({_id: this.product}, function(error, existingProduct) {
if (error) {
next(Error(error));
}
else {
if (existingProduct.length > 0) {
console.log("Type of product: "+ existingProduct[0].type);
generateCode(this,existingProduct[0].type).then(function(doc) {
console.log('Code generated:' + doc.generatedCode);
next();
}, function(err) {
console.error(err);
next(err);
});
}
else {
next(new Error("Error: Not found."));
}
}
});
} else {
next();
}
});
function generatedCode(doc,productType){
var deferred = q.defer();
try {
console.log("Product type = "+ invoiceType);
var ObjectId = require('mongoose').Types.ObjectId;
var generatedCode = '';
switch(productType) {
case 'TYPE1':
generatedCode = 'COD01-'+ random(5);
break;
case 'TYPE2':
generatedCode = 'COD02-'+ random(5);
break;
default: // other types
generatedCode = 'COD03-'+ random(5);
}
console.log("generatedCode = "+ generatedCode);
var InvoiceModel = mongoose.model('Invoice', InvoiceSchema);
// check that there are no records with that code
InvoiceModel.find({generatedCode: generatedCode}, function(err, existingInvoice) {
console.log('Find: '+ generatedCode);
if(err) {
console.log(err);
deferred.reject(err);
}
if(existingInvoice.length > 0) {
console.log('Alredy exists ' + generatedCode + ' in db');
generatedCode(doc,productType).then(function(doc) {
deferred.resolve(doc);
}, function(err) {
deferred.reject(err);
});
}
else {
console.log("generatedCode = "+ generatedCode);
console.log("Doc = "+ doc);
doc.generatedCode = generatedCode;
deferred.resolve(doc);
}
});
}
catch(exception) {
console.error(exception);
deferred.reject(new Error(exception.message));
}
return deferred.promise;
}
I do not know why the error is due, but in the line "doc.generatedCode = generatedCode;" returns null It seems as if doc does not exist and I do not understand what is failing...
I show you the debug:
Type of product: GENERIC
Product type = GENERIC
generatedCode = COD03-8KORD
Find: COD03-8KORD
generatedCode = COD03-8KORD
Doc = null
[ERROR] (node.js:496) -> uncaughtException: Cannot set property 'code' of null
The value of this changes when you are inside a callback function. Same is happening to your code here. When you do ProductSchema.find and try to access this inside it, the value is changed. You need to save a reference to actual value outside.
ProductSchema.pre('save', function(next) {
if ((typeof this.generatedCode === 'undefined') || !this.generatedCode) {
var that = this; // here create a reference to "this"
ProductSchema.find({_id: this.product}, function(error, existingProduct) {
..
..
// generateCode(this,existingProduct[0].type).then(function(doc) { // YOUR OLD CODE
generateCode(that,existingProduct[0].type).then(function(doc) { // here, change "this" to "that" which is the actual reference of your object
..
..

POST data with user details in mongo db using node.js

I am developing a web application using the MEAN stack with Angular 6. I have a form to submit data into MongoDB. Following is the save function and it works.
It saves the extruded value in the DB.
saveExtrudedHeightValue(extrudedHeight: NgForm) {
if (extrudedHeight.value != "" && extrudedHeight.value != null) {
this.extrudedHeightService.saveExtrudedHeight(extrudedHeight.value).subscribe(res => {
console.log(res);
}, (err) => {
console.log(err);
});
}
}
Here is the model
// Schema for extruded height panel
var extrudedHeightSchema = new mongoose.Schema({
userName: {
type: String
},
extrudedHeight: {
type: Number
},
});
module.exports = mongoose.model('extrudedHeightValue', extrudedHeightSchema);
Here is my post route
//post extrudedHeight values
router.post("/save", function(req, res) {
var mod = new extrudedHeight(req.body);
extrudedHeight.findOneAndUpdate({
userName: req.body.email,
extrudedHeight: req.body.extrudedHeight,
},
req.body, {
upsert: true,
new: true
},
function(err, data) {
if (err) {
console.log(err);
res.send(err);
} else {
res.send(mod);
}
}
);
});
Here is the service.
// service for save extruded height
saveExtrudedHeight(extrudedHeight): Observable < any > {
return this.http.post('/extrudedHeight/save', extrudedHeight, httpOptions)
.pipe(
catchError(this.handleError)
);
}
Now I want to save data in DB with the current user's userName. I can retrieve the current user's userName by this.
this.payload.user['email']
My problem is that I do not have an idea how to pass this userName to post route to save in db.
Here is where I get token.
this.authService.onTokenChange().subscribe(
(token: NbAuthJWTToken) => {
if (token.isValid()) {
this.user = token.getPayload().user;
this.payload = token.getPayload();
console.log(this.payload.user['email']);
}
}
)
You can first call this.authService.onTokenChange inside the saveExtrudedHeight method, and then use the flatMap operator to unwrap the internal Observable that would be returned by the http.post.
That would translate to code like this:
import { flatMap } from 'rxjs/operators';
import { throwError } from 'rxjs';
...
saveExtrudedHeight(extrudedHeight): Observable<any> {
const requestPayload = {
extrudedHeight
};
return this.authService.onTokenChange()
.pipe(flatMap(
(token: NbAuthJWTToken) => {
if (token.isValid()) {
this.user = token.getPayload().user;
this.payload = token.getPayload();
const email = this.payload.user['email'];
requestPayload.email = email;
// Make the changes here to send the email as the Request Payload.
return this.http.post('/extrudedHeight/save', requestPayload, httpOptions)
.pipe(
catchError(this.handleError)
);
} else {
throwError('Something went wrong!');
}
}
));
}
PS: I'm not really sure if this would work though as I haven't tested it out and I can't without a minimal working StackBlitz.

Can I perform mongoose update from post save middleware?

Is it possible to update a document from a post save mongoose middleware? Because it is not working for me.
I have tried in different ways.
Way 1:
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
this.quoteString = doc.quoteNumber + "";
this._doc.quoteString = doc.quoteNumber + "";
// update the record with quoteString
this.update({ _id: this.id }, this, { new: true }, function(err, result) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
Way 2: because this contains the saved object id so I tried directly.
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
this.quoteString = doc.quoteNumber + "";
this._doc.quoteString = doc.quoteNumber + "";
enter code here
// update the record with quoteString
this.update(function(err) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
Way 3:
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
var _quoteString = doc.quoteNumber+"";
this.update({ _id: doc._id }, { $set: { "quoteString": _quoteString } }, function(err) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
None of these ways works for me.
All I have to do is to update QuoteNumber field after the save. QuoteNumber is being generated from mongoose autoincrement which requires a number field. and I'm also saving a string version of quoteNumber in quoteString field so that in the UI, I can perform regex search in an autocomplete. As regular expression does not work with number type.
any suggestions will be helpful. Thanks.
Just make the autoincrementing field virtual and you don't have to worry about post save hook...
const QuoteSchema = new Schema(
{
quoteNumber: { type: Number },
quoteString: { type: String },
},
);
QuoteSchema.virtual('quote').set(function(value) {
this.quoteNumber = Number(value);
this.quoteString = String(value);
});
QuoteSchema.virtual('quote').get(function() {
return this.quoteNumber;
});
Setup:
QuoteSchema.plugin(autoIncrement.plugin, { model: 'Quote', field: 'quote' });

Push to second level array in mongodb with node/express

I am working on a chatroom where users can chat with each other filtered on the basis on projects. Users from the same project can talk to each other.
Here is my chat model where each document is based on project ref and has an array for the messages with user refference:
'use strict';
var mongoose = require('bluebird').promisifyAll(require('mongoose'));
var ChatSchema = new mongoose.Schema({
projectid: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Project'
},
messages: [{
userid: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
message: String,
date: {
type: Date,
default: Date.now
},
time: String
}]
});
export default mongoose.model('Chat', ChatSchema);
Now I am trying to update the messages array with new messages but I am unable to do so since past few hours. Here is what I have so far.
To get chat messages based on projects I am using:
routes:
router.get('/projectid/:id', controller.showByProject);
router.post('/projectid/:id', controller.insertMessageByProject);
controller:
// Gets the chat thread based on project id
export function showByProject(req, res) {
Chat.findAsync({projectid: req.params.id})
.then(handleEntityNotFound(res))
.then(respondWithResult(res))
.catch(handleError(res));
}
// Insert a new message in the chat based on projectid
export function insertMessageByProject(req, res) {
if (req.body._id) {
delete req.body._id;
}
Chat.findAsync({projectid: req.params.id})
.then(handleEntityNotFound(res))
.then(saveUpdates({$push: {messages: req.body}}))
.then(respondWithResult(res))
.catch(handleError(res));
}
Json Object I am sending from POSTMAN:
{
"messages":
{
"userid": "56d7967745ab81322a964927",
"message": "This is a meesage"
}
}
OR
{
"userid": "56d7967745ab81322a964927",
"message": "This is a meesage"
}
I am able to update the object if I have the object ID to the chat document itself but inside my application, I do not have the direct reference. I have tried few other ways as well but every time my application returns a 500 error.
Your help would be highly appreciated.
EDIT 1: here are the helping functions I am using generated by the angular full-stack plugin.
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
if (entity) {
res.status(statusCode).json(entity);
}
};
}
function saveUpdates(updates) {
return function(entity) {
var updated = _.merge(entity, updates);
return updated.saveAsync()
.spread(updated => {
return updated;
});
};
}
function removeEntity(res) {
return function(entity) {
if (entity) {
return entity.removeAsync()
.then(() => {
res.status(204).end();
});
}
};
}
function handleEntityNotFound(res) {
return function(entity) {
if (!entity) {
res.status(404).end();
return null;
}
return entity;
};
}
function handleError(res, statusCode) {
statusCode = statusCode || 500;
return function(err) {
res.status(statusCode).send(err);
};
}
EDIT 2: As I mentioned in the comments, the problem was with _.Merge function which was not merging the object right, although it should have been able to update the object.
So I wrote my own function for saveUpdates as follows:
function saveUpdatesForNewChat(updates) {
return function(entity) {
var temp = entity;
temp[0].messages.push(updates);
console.log('\ntemp:');
console.log(require('util').inspect(temp, { depth: null }));
console.log('\nend of ops\n\n');
var updated = _.merge(entity, temp);
console.log('out of merge');
console.log(require('util').inspect(updated, { depth: null }));
return updated.saveAsync()
.spread(updated => {
return updated;
});
};
}
ok so I have left the console logs inside and it's perfect object to save into the database but the server still returns a 500 errors on update.
OK! So I have found the answer myself.
The problem was that the object returned was a result set and I was calling save on whole result set. I fetched the first element out of the returned resultset, pushed new message to the element and called save on it and it started working.
Here is the code:
function saveUpdatesForNewChat(updates) {
return function(entity) {
var temp = entity[0];
temp.messages.push(updates);
var updated = temp;
return updated.saveAsync()
.spread(updated => {
return updated;
});
};
}

nodejs orm many to many

Hello Im trying to do associaton with many to many in node using node-orm2.
I have this tables:
project --> id, name
user --> id, name
user_project --> user_fk, project_fk (this is many to many association table)
The controller returns me a list of users and a list of projects, but i need to return in a both another property with projects of every user and users of every prject.
Models:
module.exports = function (orm, db) {
var Project = db.define('project', {
name : { type: 'text', required: true }
},
{
methods: {
serialize: function () {
return {
id : this.id,
name : this.name
};
}
}
});
};
module.exports = function (orm, db) {
var User = db.define('user', {
name : { type: 'text', required: true },
email : { type: 'text', required: true }
},
{
methods: {
serialize: function () {
return {
id : this.id,
name : this.name,
email : this.email
};
}
}
});
};
Controller:
module.exports = {
list: function (req, res, next) {
req.models.project.find().limit(4).order('-created').all(function (err, messages) {
if (err) return next(err);
var projects = messages.map(function (m) {
return m.serialize();
});
console.log(projects);
});
req.models.user.find().limit(4).order('-created').all(function (err, messages) {
if (err) return next(err);
var users = messages.map(function (m) {
return m.serialize();
});
console.log(users);
});
res.sendfile(settings.path + '/public/index2.html');
}
How can I do this I'm confused, I readed the documentation but i don't understand this.
Had some issues with node-orm also...
You can do it with light-orm or bookshelf.js.
In light-orm you can simply extend model with complex request like this:
var SiteCollection = new ORM.Collection({
connector: ORM.driver,
tableName: 'site',
modelExtension: {
getAds: function(callback) {
var query = "SELECT DISTINCT `ads`.* FROM `ads` INNER JOIN `siteads` " +
"ON `ads`.`id` = `siteads`.`ads_id` " +
"WHERE site_id = " + this.get('id');
ORM.Collections.AdsCollection.find(query, callback);
},
renderAds: function(callback) {
var that = this;
this.getAds(function(err, ads) {
var html = ejs.render(adsTemplate, {
content: new ORM.Collection(ads).toJSON()
});
fs.writeFileSync(writeDir + that.get('id') + ".html", html, {
encoding: 'utf8'
});
callback();
});
}
}
});
Try it: https://npmjs.org/package/light-orm

Resources