Mocha Testing a Nested Model - express

Trying to write a test for a nested model but can't get it working:
Model:
const EmployeeSchema = new mongoose.Schema({
firstName: {type: String, required: true},
lastName: { type: String, required: true}
});
const CompanySchema = new mongoose.Schema({
name: { type: String, required: true },
streetAddress: { type: String, required: true },
country: { type: String, required: true },
employees:[EmployeeSchema]
}, { timestamps: true});
Controller:
function create(req, res, next) {
const company = new Company({
name: req.body.name,
streetAddress: req.body.streetAddress,
country: req.body.country
});
company.employees.push(req.employees);
company.save()
.then(savedCompany => res.json(savedCompany))
.catch(e => next(e));
}
Test:
describe('## Company APIs', () => {
let company = {
name: "Test Company",
streetAddress: "123 Fake Street",
country: "A Country"
};
company.employees.push({firstName: "Jane", lastName: "Doe"});
describe('# POST /api/company', () => {
it('should create a new company', (done) => {
request(app)
.post('/api/company')
.send(company)
.expect(httpStatus.OK)
.then((res) => {
expect(res.body.name).to.equal(company.name);
expect(res.body.streetAddress).to.equal(company.streetAddress);
expect(res.body.country).to.equal(company.country);
company = res.body;
done();
})
.catch(done);
});
});
The above gives: TypeError: Cannot read property 'push' of undefined
I've tried a few other things but this is the most promising result, for some reason I just can't seem to populate the embedded model as part of setting up the unit test.

I ended up resolving this, hopefully this helps someone in the future.
Test:
it('should associate an employee with the company', (done) => {
var employee = new Employee();
company.employees.push(employee);
request(app)
.put(`/api/company/${company._id}`)
.send(company)
.expect(httpStatus.OK)
.then((res) => {
expect(res.body.employees).to.be.an('array')
expect(res.body.employees).to.contain(employee.id)
done();
})
.catch(done);
});
Controller:
Adding this to handle multiple additions:
if (req.body.employees != null) {
req.body.employees.forEach(function(employee) {
company.employees.push(employee);
}, this);
}

Related

like/dislike button with api call not working using vue an mongoDB

I am learning vuejs and i am working on my first project which is a social network, and i want to implement a like button that call the api to add a like or remove it if the user has already liked it. It does work in my backend but i can't make it work in the front.
I need to send the userId and add or remove the like when i click on the button
This is the data
data() {
return {
post: {
file: "",
content: "",
likes: 0,
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
the last method i tried
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then(() => {
console.log("response", response);
this.user._id = response.data._id;
if(post.usersLiked == user._id) {
this.post.likes += 0
} else if (post.usersLiked != user._id) {
this.post.likes += 1
};
})
.catch((error) => console.log(error));
}
and this is the model
const postSchema = mongoose.Schema({
userId: { type: String, required: true, ref: "User" },
content: { type: String, required: true, trim: true },
imageUrl: { type: String, trim: true },
likes: { type: Number, default: 0 },
usersLiked: [{ type: String, ref: "User" }],
firstname: {type: String, required: true, trim: true },
lastname: {type: String, required: true, trim: true },
created_at: { type: Date},
updated_at: { type: Date }
});
Any idea what is wrong ? Thank you !
.then(() => { // you missed value response from Promise here
this.user._id = response.data._id;
if(post.usersLiked == user._id)
})
Do you mean this.post.usersLiked === user._id I suppose, so post within your data options should be
post: {
file: "",
content: "",
likes: 0,
usersLiked: false,
// something else reflect to your post schema
},
i want to implement a like button that call the api to add a like or remove it if the user has already liked it
By saying that you just need a simple boolean value to do this
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
// Just need to toggle state
this.$set(this.post, 'usersLiked', this.post.usersLiked !== response?.data?._id)
})
.catch((error) => console.log(error));
}
Found the answer, i changed the axios method to this
likePost(id) {
let userId = localStorage.getItem('userId');
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, { userId }, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
console.log(response.data);
this.getAllPost();
})
.catch((error) => console.log(error));
}
i also made a few changes to the data
data() {
return {
posts: [],
post: {
file: "",
content: "",
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
and i also made some changes on the controller
exports.ratePost = (req, res, next) => {
console.log(req.body.userId)
//using findOne function to find the post
Post.findOne({ _id: req.params.id }).then(post => {
if (!post.usersLiked.includes(req.body.userId)) {
// making a object with $inc and $push methods to add a like and to add the user's id
let toChange = {
$inc: { likes: +1 },
$push: { usersLiked: req.body.userId },
};
// we update the result for the like
Post.updateOne({ _id: req.params.id }, toChange)
// then we send the result and the message
.then(post =>
res
.status(200)
.json(
{ message: "Liked !", data: post }
)
)
.catch(error => res.status(400).json({ error }));
} else if (post.usersLiked.includes(req.body.userId)) {
// using the updateOne function to update the result
Post.updateOne(
{ _id: req.params.id },
// we use a pull method to take off a like
{ $pull: { usersLiked: req.body.userId }, $inc: { likes: -1 } }
)
.then(post => {
// then we send the result and the message
res
.status(200)
.json(
{ message: "Post unliked", data: post }
);
})
.catch(error => res.status(400).json({ error }));
}
});
};

user.setPassword is not a function

I'm trying to change the user's password with the setPassword() passport local mongoose instance method, but I'm getting an error that says "user.setPassword is not a function".
This is my code
const change_password = (req, res) => {
const password = req.body.password
console.log(password);
User.find({
_id: req.user._id
}).then((user) => {
user.setPassword(password, function() {
console.log('user password changed')
user.save()
res.status(200).json( { msg : 'The password has been changed' })
})
}).catch(e => {
console.log(e);
})
}
And this is my user Schema
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
// required: true
},
googleId: {
type: String,
},
photoUrl: {
type: String,
},
githubId: {
type: String,
},
twitterId: {
type: String,
},
facebookId: {
type: String,
},
bio: {
type: String,
},
email: {
type: String,
},
phoneNumber: {
type: String,
},
})
UserSchema.plugin(passportLocalMongoose, { usernameField: 'email' });
const User = mongoose.model('User', UserSchema);
module.exports = User;
User.find() will return an array of users matching the query, hence the error.
Because there should be (at most) one user in the database matching req.user._id, use findOne instead. Also note that findOne() can return null in case there were no users matching the query:
User.findOne({ _id: req.user._id }).then(user => {
if (! user) return res.sendStatus(404);
user.setPassword(…);
});

Sequelize Count and Group Nested Include

I have got a few sql tables as
export default (sequelize, Sequelize) => {
return sequelize.define('teacher', {
tagline: {
type: Sequelize.TEXT,
},
modeOfPayment: {
type: Sequelize.STRING,
},
modeOfSession: {
type: Sequelize.STRING,
},
preferredTimeZones: {
type: Sequelize.STRING,
},
titleForSessions: {
type: Sequelize.STRING,
},
availableForWork: {
type: Sequelize.BOOLEAN,
},
});
};
export default (sequelize, Sequelize) => {
return sequelize.define('skill', {
name: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
},
});
};
export default (sequelize, Sequelize) => {
return sequelize.define('category', {
name: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
},
});
};
Here is the model relation between them
Teacher.belongsToMany(Skill, {
through: 'skill_teacher',
});
Skill.belongsToMany(Teacher, {
through: 'skill_teacher',
});
Category.hasMany(Skill);
Skill.belongsTo(Category);
Earlier I needed to query count of teachers in each skill, here's my controller for that
// #desc Get skill count for each skill
// #route GET /api/skills/count
// #access Public
const getSkillCount = asyncHandler(async (req, res) => {
try {
const skills = await Skill.findAll({
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('teachers.id')), 'teacherCount'],
],
include: [{ attributes: [], model: Teacher }],
group: ['skill.id'],
});
res.json(skills);
} catch (err) {
console.log(err.message);
res.status(500);
throw new Error(err.message);
}
});
Now, the skills are grouped in categories. So I wanna query a list of skills having their own teacher count grouped in their own categories, which category also having a skillCount column. I tried this but it is not giving my desired results
// #desc Get category and their skills counts
// #route GET /api/categories/skills/count
// #access Public
const getCategorySkillCounts = asyncHandler(async (req, res) => {
try {
const categories = await Category.findAll({
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('skills.id')), 'skillCount'],
],
include: {
model: Skill,
include: [{ model: Teacher }],
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('teachers.id')), 'teacherCount'],
],
group: ['skill.id'],
},
group: ['category.id'],
});
res.json(categories);
} catch (err) {
console.log(err.message);
res.status(500);
throw new Error(err.message);
}
});

Transpiled GraphQL with Babel is throwing error "Cannot call class as function"

I am trying to get running GraphQL server. I have simple schema in GraphQL
import {
GraphQLObjectType,
GraphQLInt,
GraphQLString,
GraphQLList,
GraphQLSchema
} from 'graphql'
import db from './models'
const user = new GraphQLObjectType({
name: "user",
description: 'This represents a user',
fields: () => {
return {
id: {
type: GraphQLInt,
resolve(user) {
return user.id
}
},
firstName: {
type: GraphQLString,
resole(user) {
return user.firstName
}
},
lastName: {
type: GraphQLString,
resole(user) {
return user.lastName
}
},
email: {
type: GraphQLString,
resole(user) {
return user.email
}
},
createdAt: {
type: GraphQLString,
resole(user) {
return user.createdAt
}
},
updatedAt: {
type: GraphQLString,
resole(user) => {
return user.updatedAt
}
}
}
}
})
const Query = new GraphQLObjectType({
name: 'Query',
description: 'This is root Query',
fields: () => {
return {
users: {
type: GraphQLList(user),
args: {
id: {
type: GraphQLInt
},
email: {
type: GraphQLString
}
},
resolve(root, args) {
return db.user.findAll({where: args})
}
}
}
}
})
const Schema = new GraphQLSchema({
query: Query
})
export default Schema
I am transpile it with babel into ES5, but every time when I try run it with express
import GraphHTTP from 'express-graphql'
import Schema from './schema'
app.use('/grapql', GraphHTTP({
schema: Schema,
pretty: true,
graphiql: true
}))
I am getting this error
\node_modules\graphql\type\definition.js:41
function _classCallCheck(instance, Constructor) { if (!instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }                                                             
TypeError: Cannot call a class as a function
I check it again and again if i have some typing error but i didnt find enything.
instead of type: GraphQLList(user) use type: new GraphQLList(user)
GraphQLList is a class and you have to create it's instance and use, but you have called it as a function.
const Query = new GraphQLObjectType({
name: 'Query',
description: 'This is root Query',
fields: () => {
return {
users: {
type: new GraphQLList(user),
args: {
id: {
type: GraphQLInt
},
email: {
type: GraphQLString
}
},
resolve(root, args) {
return db.user.findAll({where: args})
}
}
}
}
})

Sequelize findAll where many to many criteria

I have 2 models User and Item with many to many relation, here is the definitions:
User = sequelize.define('User', {
name: {type: Sequelize.STRING}
})
Item = sequelize.define('Item', {
name: {
type: Sequelize.STRING,
allowNull: false
}
}
User.belongsToMany(models.Item, {
as: 'items',
through: 'UserItem'
})
Item.belongsToMany(models.User, {
as: 'owners',
through: 'UserItem'
})
And my request is :
Item.findAll({
include: [{
model: User,
through: {
where: {id: 2}
}
}]
}).then(items => {
log.debug(items)
}).catch(err => {
log.error(err)
})
Then I have : Error: User is not associated to Item!
I also try this :
Item.findAll({
where: {'owners.id': 2},
include: Item.assocations.owners
}).then(items => {
debug(items)
}).catch(err => {
log.error(err)
})
But now I have Error: SQLITE_ERROR: no such column: Item.owners.id
Any ideas ?
Here is my working solution :
'use strict'
const Sequelize = require('Sequelize')
const sequelize = new Sequelize({
host: 'localhost',
dialect: 'sqlite',
// SQLite only
storage: 'database.sqlite'
})
const User = sequelize.define('user', {
name: {
type: Sequelize.STRING
}
}, {
freezeTableName: true // Model tableName will be the same as the model name
})
const Item = sequelize.define('item', {
name: {
type: Sequelize.STRING
}
}, {
freezeTableName: true // Model tableName will be the same as the model name
})
const UserItem = sequelize.define('useritem', {})
User.belongsToMany(Item, {
as: 'items',
through: 'useritem'
})
Item.belongsToMany(User, {
as: 'owners',
through: 'useritem'
})
Promise.all([
User.sync({force: true}),
Item.sync({force: true}),
UserItem.sync({force: true})])
.then(() => {
return Promise.all([
User.create({name: 'test'}),
User.create({name: 'admin'}),
Item.create({name: 'item1'}),
Item.create({name: 'item2'})])
.then(results => {
return results[2].addOwner(results[0])
}).then(() => {
return Item.findAll({
include: {model: User, as: 'owners', where:{
'id': 1
}}
}).then(items => {
console.log(items)
})
})
}).catch(err => console.log(err))
as need to be on include clause and where should be added on include too