Node.js Express PUT Functionality - Saving data - node.js

I am setting up a server with Node and Express for the first time and am having trouble saving the response I am retrieving in my PUT call. This is a survey - I need to update the model with the "responded" object entered in the survey.
I do see the correct response outputting in the console but receive "Object [object Object],[object Object],[object Object],[object Object],[object Object] has no method 'findById'" from my "save" function.
Thank you in advance.
kitty-questions.json
[
{
"id": "favorite-food",
"number": "1",
"url": "favorite-food",
"name": "Favorite Food",
"question": "Which of the following best describes your kitty's palatte?",
"responded" : "default response",
"query": "Which of the following best describes your kitty's palatte?",
"answers": {
"Grumpy" : "Fresh Water Salmon, no bones, served on china",
"Hipster" : "Nothing - trying to fit into newer, tighter jeans",
"Pudge" : "Anything and everything my owner is eating",
"Bub" : "Mice",
"Meow" : "Roaches"
}
},
{
"id": "favorite-band",
"number": "2",
"url": "favorite-band",
"name": "Favorite Band",
"question": "Your kitty claws at you desperatly when it wants to listen to:",
"responded" : "default response",
"query": "Which of the following best describes your kitty's palatte?",
"answers": {
"Bub" : "Country",
"Grumpy" : "Mozart. Popular music is for the plebs.",
"Pudge" : "z100",
"Meow" : "Very heavy metal",
"Hipster" : "something long winded"
}
}
Server.js
var express = require('express'),
http = require('http'),
questions = require('./data/kitty-questions');
var app = express()
.use(express.bodyParser())
.use(express.static('public'));
app.get('/questions', function (req, res) {
res.json(questions);
});
app.post('/questions', function (req, res) {
var matches = questions.filter(function (question) {
return question.url === req.body.url;
});
if (matches.length > 0) {
res.json(409, {status: 'question already exists'});
} else {
req.body.id = req.body.url;
questions.push(req.body);
res.json(req.body);
}
});
app.put('/questions/:question_name', function (req, res) {
var matches = questions.filter(function (question) {
return question.url === req.params.question_name;
});
var catResponse = req.body.responded;
console.log(JSON.stringify(catResponse));
return questions.findById(req.params.question_name, function (err, question) {
question.catResponse = req.body.responded;
return question.save(function (err) {
if (!err) {
console.log("updated");
} else {
console.log(err);
}
return res.send(question);
});
});
});
app.get('/questions/:question_name', function (req, res) {
var matches = questions.filter(function (question) {
return question.url === req.params.question_name;
});
if (matches.length > 0) {
res.json(matches[0]);
} else {
res.json(404, {status: 'invalid survey question'});
}
});
app.delete('/questions/:question_name', function (req, res) {
var found = false;
items.forEach(function (question, index) {
if (question.url === req.params.question_name) {
found = index;
}
});
if (found) {
items.splice(found, 1);
res.json(200, {status: 'deleted'});
} else {
res.json(404, {status: 'invalid survey question deletion'});
}
});
app.get('/*', function (req, res) {
res.json(404, {status: 'not found'});
});
http.createServer(app).listen(3000, function () {
console.log("Server ready at http://localhost:3000");
});
STRING FROM THE TERMINAL AFTER MAKING PUT CALL:
Server ready at http://localhost:3000
TypeError: Object [{"id":"favorite-food","number":"1","url":"favorite-food","name":"Favorite Food","question":"Which of the following best describes your kitty's palatte?","responded":"default response","query":"Which of the following best describes your kitty's palatte?","answers":{"Grumpy":"Fresh Water Salmon, no bones, served on china","Hipster":"Nothing - trying to fit into newer, tighter jeans","Pudge":"Anything and everything my owner is eating","Bub":"Mice","Meow":"Roaches"}},{"id":"favorite-band","number":"2","url":"favorite-band","name":"Favorite Band","question":"Your kitty claws at you desperatly when it wants to listen to:","responded":"default response","query":"Which of the following best describes your kitty's palatte?","answers":{"Bub":"Country","Grumpy":"Mozart. Popular music is for the plebs.","Pudge":"z100","Meow":"Very heavy metal","Hipster":"something long winded"}},{"id":"favorite-hideout","number":"3","url":"favorite-hideout","name":"Favorite Hideout","question":"You are most likely to find your beast perched here:","responded":"","answers":{"Bub":"On your shoulder","Grumpy":"Alone. Anywhere, just alone.","Pudge":"In the fridge","Meow":"Herding other cats","Hipster":"Outside, smoking."}},{"id":"favorite-friends","number":"4","url":"favorite-friends","name":"Favorite Friends","question":"Your kitty generally gets along with:","responded":"","answers":{"Bub":"Other cats","Grumpy":"No one.","Pudge":"Humans, animals, whoever.","Meow":"Obedient animals","Hipster":"dogs"}},{"id":"favorite-celebrity","number":"5","url":"favorite-celebrity","name":"Favorite Celebrity","question":"Your feline cannot get enough of this red carpet walker:","responded":"","answers":{"Bub":"Meg Ryan","Grumpy":"Jack Nicholson","Pudge":"John Candy","Meow":"Does General McArthur count?","Hipster":"Zooey Deschanel"}}] has no method 'update'
3/19 UPDATE:
app.put('/questions/:question_name', function (req, res) {
var question = questions.filter(function (question) {
return question.url === req.params.question_name;
});
var defaultResponse = question[0].responded;
res.json(defaultResponse);
var catResponse = req.body.responded;
questions.update({id: req.params.question_name}, function (err, question) {
question.catResponse = catResponse;
question.save(function (err) {
if (!err) {
res.send(catResponse);
} else {
res.send(400); //or something
}
});
});
});

There are a lot of unnecessary returns going on here, and at the very least, they make the code confusing to read.
Removing some stuff and ignoring the matches variable, since that isn't used in the PUT itself, something like this may be more what you're looking for:
app.put('/questions/:question_name', function (req, res) {
var catResponse = req.body.responded;
questions.update({id: req.params.question_name}, function (err, question) {
question.catResponse = catResponse;
question.save(function (err) {
if (!err) {
res.send(question);
} else {
res.send(400); //or something
}
});
});
});
*EDIT*
I assumed that questions = require('./data/kitty-questions'); was your mongoose model. You need questions to be a mongoose model for update to work.
Like:
var mongoose = require('mongoose')
, Questions = mongoose.model('Question')
Then your questions model file probably looks like:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
QuestionSchema = new Schema({
//keys and stuff in here
});
mongoose.model('Question', QuestionSchema);

Related

How to Lint code if I need additional mongodb request

Today I have this code. I learning NodeJS and all the time I have same question: I have a route. This route doing something, thats why I need some mongodb requests. In different ways I can need additional options or not. For example please look on my code:
const express = require('express');
const router = express.Router();
const debug = require('debug')('hackit:posts');
const boom = require('boom');
const config = require('../../config');
const db = require('monk')(config.mdbConnect);
const DBpgs = db.get('pgs');
const DBposts = db.get('posts');
const DBcategories = db.get('categories');
router.route( "/edit-:id" )
.get((req,res,next) => {
const ops ={
h1: "Edit post",
title: "Admin edit post",
description: "Admin page",
specialscript: "/javascripts/editpost.js"
};
DBposts.findOne({_id: req.params.id}, (err, saved) => {
if(saved.h1.length) debug('Edit «'+saved.h1+'»')
else debug('Create new post')
if(err) next(boom.badImplementation(err))
DBcategories.find({}, (err, categories) => {
if(err) next(boom.badImplementation(err))
let group = {}
if(saved.parent&&saved.parent.length){
DBpgs.findOne({parent: saved.parent}, (err, group) => {
if(err) next(boom.badImplementation(err))
res.render('admin/post', {ops, saved, categories, group})
})
}else{
res.render('admin/post', {ops, saved, categories, group})
}
})
})
})
module.exports = router;
There is only one route. The problem is if I got saved.parent I need to do additional request to db, to pick group options. I solved it just by if and ... its looks rude. Next two lines i type res.render('admin/post', {ops, saved, categories, group}) witch is the same line of code. I want to be a good programmer. Lint my code and say how I must fill my code please. Thanks!
The promise-based implementation would look something like this (I just modified your code directly in the answer so it probably has bugs, but it gives you the gist). Note that you get out of duplicating both the response as well as the error conditions.
router.route( "/edit-:id" )
.get((req,res,next) => {
const ops ={
h1: "Edit post",
title: "Admin edit post",
description: "Admin page",
specialscript: "/javascripts/editpost.js"
};
DBposts.findOne({_id: req.params.id})
.then(saved => {
if (saved.h1.length) debug('Edit «'+saved.h1+'»');
else debug('Create new post');
return DBcategories.find({})
.then(categories => {
if (saved.parent && saved.parent.length) {
return DBpgs.findOne({parent: saved.parent});
}
return {};
})
.then(group => {
res.render('admin/post', {ops, saved, categories, group})
})
.catch(error => {
next(boom.badImplementation(err));
});
});
});

Alexa Test Response does not contain outputSpeech

I'm new to Alexa, and have followed the airportinfo tutorial and I have copied the code from github https://github.com/bignerdranch/alexa-airportinfo and when i test it using npm and input an airport code e.g. SFO, Theres no "outputSpeech:" and i tried making a similar skill with the same issue, I'm not sure what I'm doing wrong. I have both index.js and FAADataInfo.js Thanks in advance for your help.
This is the index.js file
'use strict';
module.change_code = 1;
var _ = require('lodash');
var Alexa = require('alexa-app');
var skill = new Alexa.app('airportinfo');
var FAADataHelper = require('./faa_data_helper');
skill.launch(function(req, res) {
var prompt = 'For delay information, tell me an Airport code.';
res.say(prompt).reprompt(prompt).shouldEndSession(false);
});
skill.intent('airportInfoIntent', {
'slots': {
'AIRPORTCODE': 'FAACODES'
},
'utterances': [
'{|flight|airport} {|delay|status} {|info} {|for} {-|AIRPORTCODE}'
]
},
function(req, res) {
var airportCode = req.slot('AIRPORTCODE');
var reprompt = 'Tell me an airport code to get delay information.';
if (_.isEmpty(airportCode)) {
var prompt = 'I didn\'t hear an airport code. Tell me an airport code.';
res.say(prompt).reprompt(reprompt).shouldEndSession(false);
return true;
} else {
var faaHelper = new FAADataHelper();
console.log(airportCode);
faaHelper.getAirportStatus(airportCode).then(function(airportStatus) {
console.log(airportStatus);
res.say(faaHelper.formatAirportStatus(airportStatus)).send();
}).catch(function(err) {
console.log(err.statusCode);
var prompt = 'I didn\'t have data for an airport code of ' +
airportCode;
res.say(prompt).reprompt(reprompt).shouldEndSession(false).send();
});
return false;
}
}
);
module.exports = skill;
and heres FAADataInfo.js
'use strict';
var _ = require('lodash');
var requestPromise = require('request-promise');
var ENDPOINT = 'http://services.faa.gov/airport/status/';
function FAADataHelper() {
}
FAADataHelper.prototype.getAirportStatus = function(airportCode) {
var options = {
method: 'GET',
uri: ENDPOINT + airportCode,
json: true
};
return requestPromise(options);
};
FAADataHelper.prototype.formatAirportStatus = function(aiportStatusObject) {
if (aiportStatusObject.delay === 'true') {
var template = _.template('There is currently a delay for ${airport}. ' +
'The average delay time is ${delay_time}.');
return template({
airport: aiportStatusObject.name,
delay_time: aiportStatusObject.status.avgDelay
});
} else {
//no delay
var template =_.template('There is currently no delay at ${airport}.');
return template({
airport: aiportStatusObject.name
});
}
};
module.exports = FAADataHelper;
This is the response that I get
{
"version": "1.0",
"response": {
"directives": [],
"shouldEndSession": true
},
"sessionAttributes": {},
"dummy": "text"
}
The alexa-app version that the tutorial is using is out of date. When using the latest alexa-app npm version (4.0.0), the return value for the .intent() function should be a Promise and not a boolean if you are running asynchronous functions.
In your index.js, add:
return faaHelper.getAirportStatus(....) {}.catch(){}
and remove the return false; after the catch.
Here's the full skill.intent() code
skill.intent('airportInfoIntent', {
'slots': {
'AIRPORTCODE': 'FAACODES'
},
'utterances': [
'{|flight|airport} {|delay|status} {|info} {|for} {-|AIRPORTCODE}'
]
},
function(req, res) {
var airportCode = req.slot('AIRPORTCODE');
var reprompt = 'Tell me an airport code to get delay information.';
if (_.isEmpty(airportCode)) {
var prompt = 'I didn\'t hear an airport code. Tell me an airport code.';
res.say(prompt).reprompt(reprompt).shouldEndSession(false);
return true;
} else {
var faaHelper = new FAADataHelper();
console.log(airportCode);
return faaHelper.getAirportStatus(airportCode).then(function(airportStatus) {
console.log(airportStatus);
res.say(faaHelper.formatAirportStatus(airportStatus)).send();
}).catch(function(err) {
console.log(err.statusCode);
var prompt = 'I didn\'t have data for an airport code of ' +
airportCode;
res.say(prompt).reprompt(reprompt).shouldEndSession(false).send();
});
//return false;
}
}
);

Combined search in sails js

I have game collection:
{
"name": "Play RoadRash",
"version": "1.0.0",
"icon": "image-md-two-thirds.png",
"id": "6dc41c3fa0e7"
}
and platform collection:
{
"name": "PlayStation",
"version": "7",
"icon": "playstation.jpg",
"id": "55eaf322f1a16"
}
I'm trying to create a search query who searches in both collection based on name parameters. Does anyone have any idea how to search on multiple collection in sails waterline?
We've wrote a controller with full-text search within all models. All what it does is search within all models and their attributes by q parameter from request. Here is the full code of this controller:
var _ = require('lodash');
var Promise = require('bluebird');
module.exports = {
index: function (req, res) {
var models = [];
if (!req.param('q')) {
return res.badRequest(null, null, 'You should specify a "q" parameter!');
}
var q = req.param('q');
if (req.param('model')) {
var modelStr = req.param('model').toString().toLowerCase();
if (!(modelStr in sails.models)) {
return res.badRequest(null, null, 'Cannot find model: ' + modelStr);
}
models.push({name: modelStr, model: sails.models[modelStr]});
} else {
_.forEach(sails.models, function (model, modelStr) {
models.push({name: modelStr, model: model});
});
}
Promise.map(models, function (modelObj) {
var model = modelObj.model;
var modelStr = modelObj.name;
var where = _.transform(model.definition, function (result, val, key) {
result.or.push(_.set({}, key, {contains: q}));
}, {or: []});
return model
.find(where)
.then(function (queryRes) {
var resObj = {};
resObj[modelStr] = queryRes;
return Promise.resolve(resObj)
});
})
.then(function (searchRes) {
return _.transform(searchRes, function (result, val) {
result = _.merge(result, val);
}, {});
})
.then(res.ok)
.catch(res.serverError)
}
};
You can just copy-paste it in your api/controllers/SearchController.js and that's it. It still need to refactor this code, but it works.

RESTful CRUD for Angular and Express $resource

I have my JSON querying and creating correctly. I'm a bit stuck on how to remove items from the server. They are being removed in angular but I can't seem to get the connection right for removing them on the server.
My server.js:
var hcController = require('./server/controllers/services-controller.js')
//REST API
app.get('/api/hc', hcController.list);
app.post('/api/hc', hcController.create);
app.delete('/api/hc:_id', hcController.delete);
My server-side model
var mongoose = require('mongoose');
module.exports = mongoose.model('HealingCenterData',{
title: String,
shortname: String,
summary: String,
description: String
});
My server-side controller
var Hc = require('../models/healingcenter-model.js')
module.exports.create = function (req, res) {
var hc = new Hc(req.body);
hc.save(function (err, result){
res.json(result);
});
}
module.exports.list = function (req,res) {
Hc.find({}, function (err, results){
res.json(results);
});
}
module.exports.delete = function (req, res) {
???????
});
}
My angular service:
app.factory("HC", ["$resource", function($resource) {
return {
API: $resource('/api/hc/:id')
}
}]);
My angular controller:
app.controller('servicesController', ['$scope', 'HC','$resource', function ($scope, HC, $resource) {
HC.API.query(function(results) {
$scope.services = results;
});
$scope.createService = function() {
var service = new HC.API();
service.title = $scope.serviceTitle;
service.shortname = $scope.serviceShortname;
service.summary = $scope.serviceSummary;
service.description = $scope.serviceDescription;
service.$save(function(result){
$scope.services.push(result);
$scope.serviceTitle = '';
$scope.serviceShortname = '';
$scope.serviceSummary = '';
$scope.serviceDescription = '';
});
}
$scope.removeItem = function(index){
$scope.services.splice(index, 1);
}
}]);
My JSON structure
{ "_id" : ObjectId("53bea9366a03a66c2dad68bb"), "title" : "Auto Clinic", "shortname" : "auto_clinic", "summary" : "Volunteers evaluate car problems and make minor repairs. Labor is free, and the car owner pays for any needed parts. Oil changes are performed at a reduced cost. All services are performed on Saturdays.", "description" : "No additional information yet.", "__v" : 0 }
On the server side try (I'm assuming you are using moongose) :
exports.delete = function(req,res){
if(req.params.id !==null || req.params.id!==undefined){
Hc.remove({_id:req.params.id},function(err){
res.send(200);
});
}
};
on the client side:
angular controller:
var endPoint = $resource('/api/hc/:id', {id:'#tId'});
$scope.removeItem = function(id){
var ep = new endPoint({tId:id});
ep.$delete(function(res){
//TODO: update local array in scope
});
};
EDIT:
you can just use the resource directly in the controller or just the service as you have done in your case, that's totally fine.

Mongoose Query Data in Express.js Node.js ,get the wrong result

The Problem is .. sometimes it shows up the wrong user on the screen (someone gets session of another one). but it's hardly happen and i think it only happen when there're some concurrent.
if anything in this code can make this behaviour happen , please suggest.
app.js -- this file has a schema and initiation of model and routes component
var userSchema = mongoose.Schema({
"_id": mongoose.Schema.Types.ObjectId,
"name": String,
"username":String,
"etc":String
});
userMongooseModel = mongoose.model('users',userSchema);
var sessionSchema = mongoose.Schema({
"userID": mongoose.Schema.Types.ObjectId,
"sessionID": String,
"expire":Number
});
sessionMongooseModel = mongoose.model('sessions',sessionSchema);
var UserModel = require(basePath+'/models/UserModel.js').UserModel;
userModel = new UserModel();
var user = require(basePath+'/routes/user');
routes/user.js -- this file is the detail about each route.
exports.editProfilePage = function(req,res){
var httpRes = res;
userModel.checkSession(req.cookies.session,function(res){
if(res.status=='success' && res.type=='photographer')
{
userModel.getByID(res.userID,{},function(resp){
httpRes.render(basePath+'/views/photographer-edit.html',{currentUser:res.user,user:resp.user,etc:'etc'});
});
}
else
{
//if not login or state != 0
httpRes.redirect(baseURL+'/photographerRedirect');
}
});
}
usermodel.js -- this file is to retrieve data from database
var mongoose = require('mongoose');
var ObjectId = mongoose.Types.ObjectId;
var request = require('request');
UserModel.prototype.checkSession = function(sessionID,callback){
sessionMongooseModel.findOne({sessionID:sessionID},function (err, user) {
if(err)
{
callback({status:'fail',errorMsg:'notFound'});
return;
}
if(user==null)
{
callback({status:'fail',errorMsg:'notFound'});
}
else
{
if(user.expire > Date.now())
{
userMongooseModel.findOne({_id:user.userID},{studioName:1,state:1,etc:1},function (err, user) {
if(err || user==null)
{
callback({status:'fail',errorMsg:'notFound'});
return;
}
if(user.type=='photographer' && user.state==0)
{
callback({status:'fail',errorMsg:'wrongUserState',userID:user._id,user:user,etc:1});
}
else
callback({status:'success',userID:user._id,user:user,type:user.type,etc:1});
});
}
else
{
callback({status:'fail',errorMsg:'notFound'});
}
}
});
}
UserModel.prototype.getByIDs = function(userIDs,options,callback){
userMongooseModel.find({_id:{$in:userIDs}},options,function (err, users) {
if(err){
callback({status:'fail',errorMsg:'UserModel.find'});
return;
}
callback({status:'success',users:users});
});
}
Thanks a lot !
(not sure if this is causing the problem, but it seems worth mentioning anyway)
Here, you're passing req.cookies.session, which is an object:
userModel.checkSession(req.cookies.session, ...);
But in checkSession, you're assuming it's an id:
UserModel.prototype.checkSession = function(sessionID, callback) {
sessionMongooseModel.findOne({sessionID:sessionID}, ...);
Just out of curiosity: any reason why you're not using one of the existing MongoDB-backed session stores for Express (like this one)?
Also, Mongoose has it's own way of adding class methods to models, using statics.
The answer is ... cache
Someone in the middle cache the content ,and it also send the wrong content to the wrong person so it seems like session was mixed
the way out is .. make server send flag no cache to the client
res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');

Resources