Create a post route that adds references to its relations - express

I've got the basic MERN Stack app running I can, GET, POST and Delete Topics and Post separately, they have relationship in Schema but I don't understand how to route it properly to incorporate the relations when POSTing.
const express = require('express');
const router = express.Router();
const Post = require("../../models/Post");
const Topic = require('../../models/Topic');
router.post("/", (req,res) => {
//gets topic id from param
const {topic} = req.params;
//creating a new post
const newPost = new Post({
post: req.body.post,
description: req.body.description,
topic_id: req.body.topic_id
});
//get topic by id
const topic_obj = Topic.findById(topic);
//add posts to topic_object
topic_obj.posts.push(newPost);
//and save
topic_obj.save()
.then(newPost.save()
.then(post => res.json(post)).catch(err => console.log(err)));
});
module.exports = router;
I Don't know if you need more information
I pushed it to GitHub for some helpL https://github.com/wolffles/bloccit-node/blob/react/routes/api/posts.js

Accoarding to TopicSchema:
const TopicSchema = new Schema({
topic: {
type: String,
required: true
},
description: {
type: String,
required: true
},
posts: [
{
type: Schema.Types.ObjectId,
ref: 'post'
}
],
date: {
type: Date,
default: Date.now
}});
To incorporate relation between Topic and Post you need to add _id property of newly created Post object to posts field in Topic object that this newly created Post represents.
topic_obj.posts.push(newPost._id);
EDIT:
In your code there are some issues that an cause errors.
const {topic} = req.params; You should validate whether topic is a valid ObjectId
You should also validate request body and check if description and post has required format.
You dupicate Topic id by passing it in request body and also asa request param.
const topic_obj = Topic.findById(topic); This is an async operation, it returns Promise not result of query.
Code should look like (it is not tested):
const express = require("express");
const router = express.Router();
const Post = require("../../models/Post");
const Topic = require("../../models/Topic");
router.post("/:topicId", async (req, res) => {
const { topicId } = req.params;
//TODO: validate if topic is a valid ObjectID
const validTopicId = validateTopicId(topicId);
if (!validTopicId) return res.status(400).send("Invalid param.");
//TODO: validate req.body.description and req.body.post
const validBody = validateBody(req.body);
if (!validBody) return res.status(400).send("Invalid data.");
const newPost = new Post({
post: req.body.post,
description: req.body.description,
topic_id: topicId
});
const topic = await Topic.findById(topicId);
if (!topic) return res.status(400).send("Invalid param.");
topic.posts.push(newPost._id);
await topic.save();
await newPost.save();
res.send();
});
module.exports = router;

Related

Stripe Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?

So first what i want to say, is that none of the public questions on stackoverflow did not helped me with this error. I am running the Stripe CLI on my local machine like this : stripe listen --forward-to localhost:4242/webhook , but weirdly when i try to proccess all the events inside i get the error :
No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing
I have already tried using request.rawBody , but it didn't fix my issue.
I am posting all of the code, so maybe someone will see what i can't and help me fixing it
router.js :
let express = require('express');
let router = express.Router();
let bodyParser = require('body-parser')
let postMong = require('./post')
require("dotenv").config()
router.use(express.json());
const YOUR_DOMAIN = 'http://localhost:4242';
const stripe = require('stripe')(process.env.PUBLIC_KEY);
router.post('/checkout/create-order', async (req, res) => {
const price = req.body.order.stripe_price || undefined,
product = req.body.order.stripe_product || undefined
const session = await stripe.checkout.sessions.create({
shipping_address_collection: {
allowed_countries: ['US', 'CA'],
},
shipping_options: [
{
shipping_rate_data: {
type: 'fixed_amount',
fixed_amount: {
amount: 2499,
currency: 'usd',
},
display_name: 'International Shipping',
// Delivers between 5-7 business days
delivery_estimate: {
minimum: {
unit: 'week',
value: 2,
},
}
}
},
],
line_items: [
{
price: price,
quantity: 1,
},
],
payment_method_types: ["card", 'us_bank_account'],
mode: 'payment',
success_url: `${YOUR_DOMAIN}/success.html`,
cancel_url: `${YOUR_DOMAIN}/index.html`,
});
res.json({url: session.url})
});
router.post('/posts/add', async (req,res)=>{
try{
const {author, id, name, picture, pixels, price, size, stripe_price, stripe_product} = req.body
const pos = await postMong.create( {author, id, name, picture, pixels, price, size, stripe_price, stripe_product})
res.json(pos)
} catch(e){
res.status(500).json(e)
}
})
router.get('/ideas', async (req,res)=>{
try{
const posts = await postMong.find()
return res.json(posts);
} catch(e){
reject(res.status(500).json(e))
}
})
const endpointSecret = 'whsec_****';
const fulfillOrder = (session) => {
// TODO: fill me in
console.log("Fulfilling order", session);
}
router.use(bodyParser.json());
router.post('/webhook', (request, response) => {
const payload = request.body;
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.rawBody, sig, endpointSecret);
console.log(event)
} catch (err) {
console.log(err.message)
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
// Fulfill the purchase...
fulfillOrder(session);
}
response.status(200);
});
module.exports = router
server.js :
const router = require("./router");
const account = require("./routerAccount");
const express = require('express');
const mongoose = require("mongoose")
const app = express();
const cors = require('cors')
var session = require('express-session');
require("dotenv").config()
const db_url = process.env.MONGO_URL
app.use(session({
cookie: {
httpOnly: true
},
rolling: true,
resave: true,
saveUninitialized: true,
secret: '~~~~~'
}));
app.set('view engine','ejs');
app.use(express.static('public'));
//app.use(express.json());
app.use(cors())
app.use('/', router)
app.use('/', account)
async function startApp(){
try{
await mongoose.connect(db_url, {
useUnifiedTopology: true,
useNewUrlParser:true
})
app.listen(4242, () => {console.log("server is working")})
} catch(e) {
console.log("some error appearead" + e)
}
}
startApp()
Normally when you see this error, it means that, either the HTTP request body Stripe sent to your webhook handler has been altered in some way or You may not be using the correct webhook secret.
The most likely reason it is throwing an exception is because your router is parsing body as JSON with router.use(express.json()). constructEvent requires the raw, unparsed body you receive from the request to verify the signature. To verify you have the raw body you can print it out and see if you get something like <Buffer 28 72 10..>
You can tell your router to keep the request body raw by setting something like this on your route router.use('/webhook', express.raw({type: "*/*"}))
I found the solution for my problem.
What i added is
app.use( "/webhook",express.raw({ type: "*/*" }))
in my server.js file.

How to link documents on mongoose

I am new to express development and i am trying to build a blog. I have built two models, one for posts and one for uses. On users schema i have an attribute posts to save the post when a user creates a post. On the controller, before i create a post first i am taking the user's id from the req.params and after that i retrive the user by findbyid function and try to save the post on user's posts attribute, but with no succeful.
const mongoose = require("mongoose");
UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
posts: [{type: mongoose.Schema.Types.ObjectId, ref: "Post"}]
})
module.exports = mongoose.model("User", UserSchema);
const Post = require("../model/post");
const User = require("../model/user");
module.exports = {
new: (req, res) => {
res.render("new_post");
},
post_new: (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = new Post({
title: title,
article: article,
})
User.findById(id)
.then(user => {
user.posts.push(post);
})
//post.created_by.push(id);
post.save()
.then(result => {
console.log("Post has created");
res.redirect("/");
});
}
};
I see a few problems.
You user schema should not have array of posts. Instead, your post schema should have a field named user/userId to store user ID.
Example:
const PostSchema = new mongoose.Schema({
title: { type: String },
....,
userId: {type: mongoose.Schema.Types.ObjectId, ref: "User"}
});
Now your post_new function should be like this.
post_new: async (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = await Post.create({
title: title,
article: article,
userId: id
});
console.log("Post has created");
res.redirect("/");
}
If you want to stick with your way then the create_new function should be like this.
post_new: async (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = new Post({
title: title,
article: article,
});
const {_id} = await post.save();
const user = await User.findById(id);
user.posts.push(_id);
await user.save();
console.log("Post has created");
res.redirect("/");
}

profile validation failed: handle: Path `handle` is required

My goal is to build an app that will connect different professionals from different background. I'm also using mongoose as my database.
I created a profile.js that will create and update profiles. But when I test with postman, I get the following error:
"PROFILE VALIDATION FAILED: HANDLE: PATH HANDLE IS REQUIRED."
What can I possibly do to solve this issue?
Your help will be grateful.
const express = require('express'); // require express modules
const router = express.Router(); // to use express router
const auth = require('../../middleware/auth');
const { check, validationResult } = require('express-validator');
const Profile = require('../../models/Profile');
const User = require('../../models/User');
//#route GET api/profile/me
//#desc Get current users profile
//#access Private
router.get('/me', auth, async (req,res) => {
try{
const profile = await Profile.findOne({user: req.user.id}).populate(
'user',
['name', 'avatar']);
if(!profile){
return res.status(400).json({ msg:'No profile exists for this user'});
}
res.json(profile);
} catch(err){
console.error(err.message);
res.status(500).send('Server error');
}
}); //to create a route
//#route POST api/profile
//#desc Create or update users profile
//#access Private
router.post('/',
[
auth,
[
check('status', 'Status is required')
.not()
.isEmpty(),
check('skills', 'Skills is required')
.not()
.isEmpty()
]
] ,
async (req, res) =>{
const errors = validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({errors: errors.array()})
}
const {
company,
website,
location,
bio,
status,
githubusername,
skills,
youtube,
facebook,
twitter,
instagram,
linkedin
} =req.body;
//to build profile object
const profileFields = {};
profileFields.user = req.user.id
if(company) profileFields.company = company;
if(website) profileFields.website = website;
if(location) profileFields.location = location;
if(bio) profileFields.bio = bio;
if(status) profileFields.status = status;
if(githubusername) profileFields.githubusername = githubusername;
if(skills){
profileFields.skills = skills.split(',').map(skills => skills.trim());
}
//for the social object
profileFields.social = {}
if(youtube) profileFields.social.youtube = youtube;
if(facebook) profileFields.social.facebook = facebook;
if(twitter) profileFields.social.twitter = twitter;
if(instagram) profileFields.social.instagram = instagram;
if(linkedin) profileFields.social.linkedin = linkedin;
try{
let profile = await Profile.findOne({ user: req.user.id });
if(profile){ //if there is a profile, we will update it
profile = await Profile.findOneAndUpdate(
{ user: req.user.id},
{$set: profileFields },
{new: true}
);
return res.json(profile);
}
//this will create profiles
profile = new Profile(profileFields);
await profile.save();
res.json(profile);
} catch(err){
console.error(err.message);
res.status(500).send('Server Error');
}
}
);
module.exports = router;
same code, same issue. In the profile Schema there is a handle field that is set to required. i commented it out and it is working fine now.
the same code you made i made it,you will found handle is required in profile model delete it and your code will working
handle: {
type: String,
required: true,
max: 40
}
Change this code in your models/Profile.js to
handle: {
type: String,
required: false,
max: 40
},
I had the very same Issue . However , it happened because in your "Profile" Schema you probably you made the 'handle' attribute required . So , you must have to give it otherwise just make a change and make the require value to false . Such as (require: false) and hopefully your issue will go .
This is happening because your Profile schema has a handle field(attribute) in which you have a property required: true.
Go to the profile schema file and remove the handle attribute(field) or remove the required: true< from the handle attribute
Example
Profile.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create Schema
const Profile = new Schema({
...,
handle: {
type: ...,
required: true //remove this line
}
...
});
use
let const profileFields = {};
profileFields.user = req.user.id
that's
let profileFields = {};
profileFields.user = req.user.id
or you can use
const profileFields = {};
profileFields.user = req.user.id;

Adding mongoose schema model to express results in error 500

I have react app that stores data via axios to a mongoose server. It worked perfect until I wanted to add an extra schema for different data. My schema models are separated so I thought to just add another one called PartyList.js.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// Define collection and schema for Items
var PartyList = new Schema({
name: {
type: String
},
song_id: {
type: String
},
port: {
type: Number
}
},{
collection: 'party'
});
module.exports = mongoose.model('PartyList', PartyList);
This is the code in my app.js.
const config = require('./database/DB');
const ServerPortRouter = require('./routes/ServerPortRoutes');
mongoose.connect(config.DB).then(
() => {console.log('Database is connected') },
err => { console.log('Can not connect to the database' +err)
});
app.use('/serverport', ServerPortRouter);
This is how I import it and try to run it (called ServerPortRoutes.js). After running a part of my application that uses this route I get a 500 (Internal Server Error). My server tells me ReferenceError: PartyList is not defined which is defined 3 lines above.
const ServerPort = require('../models/ServerPort');
const PartyList = require('../models/PartyList');
ServerPortRouter.route('/add-party').post(function (req, res) {
const PartyList = new PartyList(req.body);
PartyList.save()
.then(PartyList => {
res.json('Server added successfully');
})
.catch(err => {
res.status(500).send("unable to save to database");
});
});
The problem looks to be you are redefining a const . In your route change to const partyList = new PartyList(req.body); Then use partyList as your variable

Mongoose Model#save not being called in Promise.all() handling

I am testing an express route handler which makes use of mongoose.
My route handler code is the following.
// Require models.
var Comment = require('../models/commentModel');
var User = require('../models/userModel');
var Post = require('../models/postModel');
// Require mongoose and set bluebird to handle its promises.
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
// Creates a comment.
exports.create_comment = function(req, res) {
var comment = new Comment();
return comment.save().then(function(createdComment) {
res.json(createdComment);
var promises = [
User.findById(userId).exec(),
Post.findById(postId).exec()
];
// Should resolve to [{ user: {/* */} }, { post: {/* */} }]
var promisedDocs = Promise.all(promises);
// Function provided to Array.map().
function pushAndSave(obj) {
// Extract the doc from { doc: {/* */} }
var doc = obj[Object.keys(obj)[0];
doc.comments.push(createdComment);
return doc.save();
}
// Return promise and process the docs returned by resolved promise.
return promisedDocs.then(function(results) {
results.map(pushAndSave);
});
})
.catch(function(err) {
res.json(err);
});
}
The logic I am trying to test is that when everything goes right, the calls to the appropriate functions are made. Basically, I am expecting the following:
comment.save(), User.findById().exec(), Post.findById().exec(), user.save(), and post.save() to be called.
To test this, I am using mocha, chai, sinon, sinon-mongoose, sinon-stub-promise, and node-mocks-http.
This is my test (I am obviating the setup).
it('Makes all the appropriate calls when everyting goes right', function() {
// Mock through sinon-mongoose
var userMock = sinon.mock(User);
var postMock = sinon.mock(Post);
userMock
.expects('findById')
.chain('exec')
.resolves({
user: {/**/}
});
postMock
.expects('findById')
.chain('exec')
.resolves({
post: {/**/}
});
// Stubbing calls to Model#save.
var saveComment = sinon.stub(Comment.prototype, 'save');
var saveUser = sinon.stub(User.prototype, 'save');
var savePost = sinon.stub(Post.prototype, 'save');
saveComment.returnsPromise().resolves({
comment: {/**/}
});
saveUser.returnsPromise().resolves({
user: {/**/}
});
savePost.returnsPromise().resolves({
post: {/**/}
});
// Mocking req and res with node-mocks-http
req = mockHttp.createRequest({
method: 'POST',
url: '/comments',
user: {/**/},
body: {/**/}
});
res = mockHttp.createResponse();
// Call the handler.
commentsController.create_comment(req, res);
expect(saveComment.called).to.equal(true); // Pass
userMock.verify(); // Pass
postMock.verify(); // Pass
expect(saveUser.called).to.equal(true); // Fail
expect(savePost.called).to.equal(true); // Fail
});
As you can see, the calls to user.save() and post.save() are not made. This might be a problem with my Promise.all() setup and subsequent handling or my test itself, but I am out of ideas.
Can you spot my error?
Thanks in advance, guys.
It took me longer to recreate the missing parts of your example, than to actually find the problem. Below you can find a fixed version of your test scenario.
user.js
var mongoose = require('mongoose');
module.exports = mongoose.model('User', {
name: String,
comments: Array
});
post.js
var mongoose = require('mongoose');
module.exports = mongoose.model('Post', {
name: String,
comments: Array
});
comment.js
var mongoose = require('mongoose');
module.exports = mongoose.model('Comment', {
text: String
});
controller.js
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
var Comment = require('./comment');
var User = require('./user');
var Post = require('./post');
// Creates a comment.
exports.create_comment = function (req, res) {
var comment = new Comment();
const userId = req.user.id;
const postId = req.body.id;
return comment.save().then(function (createdComment) {
res.json(createdComment);
var promises = [
User.findById(userId).exec(),
Post.findById(postId).exec()
];
// Should resolve to [{ user: {/* */} }, { post: {/* */} }]
var promisedDocs = Promise.all(promises);
// Function provided to Array.map().
function pushAndSave (doc) {
doc.comments.push(createdComment);
return doc.save();
}
// Return promise and process the docs returned by resolved promise.
return promisedDocs.then(function (results) {
results.map(pushAndSave);
});
})
.catch(function (err) {
console.error('foo', err);
res.json(err);
});
};
test.js
'use strict';
const chai = require('chai');
const sinon = require('sinon');
const SinonChai = require('sinon-chai');
var sinonStubPromise = require('sinon-stub-promise');
sinonStubPromise(sinon);
require('sinon-mongoose');
chai.use(SinonChai);
const expect = chai.expect;
var mockHttp = require('node-mocks-http');
const commentsController = require('./controller');
var Comment = require('./comment');
var User = require('./user');
var Post = require('./post');
describe.only('Test', () => {
it('Makes all the appropriate calls when everyting goes right',
function (done) {
// Mock through sinon-mongoose
var userMock = sinon.mock(User);
var postMock = sinon.mock(Post);
userMock
.expects('findById')
.chain('exec')
.resolves(new User());
postMock
.expects('findById')
.chain('exec')
.resolves(new Post());
// Stubbing calls to Model#save.
var saveComment = sinon.stub(Comment.prototype, 'save');
var saveUser = sinon.stub(User.prototype, 'save');
var savePost = sinon.stub(Post.prototype, 'save');
saveComment.resolves({
comment: { /**/}
});
saveUser.resolves({
user: { /**/}
});
savePost.resolves({
post: { /**/}
});
// Mocking req and res with node-mocks-http
const req = mockHttp.createRequest({
method: 'POST',
url: '/comments',
user: {id: '123'},
body: {id: 'xxx'}
});
const res = mockHttp.createResponse();
// Call the handler.
commentsController.create_comment(req, res).then(() => {
expect(saveComment.called).to.equal(true); // Pass
userMock.verify(); // Pass
postMock.verify(); // Pass
expect(saveUser.called).to.equal(true); // Fail
expect(savePost.called).to.equal(true); // Fail
done();
});
});
});
Overall, the assertion logic was ok in theory. The problems that I found were the following:
Your userMock and postMock returned a plain empty object instead of a Mongoose model instance. Therefore, there was an exception thrown within the pushAndSave function that was never caught, since the .save() method was undefined. I simplified the pushAndSave function so that you can see what I'm talking about
You tested an async method in sync mode and this would definitely cause issues with the assertions. I've switched the test case into async and the assertions are executed as soon as the method completes