I really need some help! I am developing a simple app in NodeJS using express and the Onion architecture. I have an infrastructure, application, domain and interface layer.
The problem occurs when I want to initialize my domain object in the application layer and pass it to the Repository. I am passing to the domain object the req.body
{
minTransactionValue: '1000000000000000000',
maxTransactionValue: '40000000000000000000',
author: 'Konstantina'
}
directly, but what I get in the console is RuleDomain {}
The CreateRule.js
const serialize = require('serialize-javascript');
const RuleDomain = require('../../domain/Rule');
class CreateRule extends EventEmitter {
constructor({ rulesRepository }) {
super();
this.rulesRepository = rulesRepository;
}
execute(ruleData) {
const rule = new RuleDomain(ruleData);
console.log(rule);
this.rulesRepository
.add(rule)
.then((newRule) => {
this.emit('SUCCESS', newRule);
})
.catch((error) => {
if (error.message === 'ValidationError') {
return this.emit('VALIDATION_ERROR', error);
}
this.emit('ERROR', error);
});
}
}
module.exports = CreateRule;
The domain constructed using structure package
const { attributes } = require('structure');
const RuleDomain = attributes({
id: Number,
uuid: {
type: String,
required: true,
},
minTransactionValue: {
type: String,
required: true,
},
maxTransactionValue: {
type: String,
required: true,
},
from: { type: String },
to: { type: String },
minConfirmations: {
type: Number,
},
author: {
type: String,
required: true,
},
createdAt: {
type: Date,
required: true,
},
})(
class RuleDomain {
isLegal() {
return true;
}
}
);
module.exports = RuleDomain;
The controller function where I call CreateRule and pass the req.body
create(req, res) {
const rule = {
minTransactionValue: req.body.minTransactionValue,
maxTransactionValue: req.body.maxTransactionValue,
author: req.body.author,
minConfirmations: req.body.confirmations,
};
rulesRepository = new rulesRepo(RuleModel);
const createRule = new CreateRule({ rulesRepository });
createRule
.on('SUCCESS', (rule) => {
res.status(201).json(rule);
})
.on('VALIDATION_ERROR', (error) => {
res.status(400).json({
type: 'ValidationError',
details: error.details,
});
})
.on('ERROR', (error) => {
res.sendStatus(500);
});
createRule.execute(req.body);
},
Please tell me what is wrong with the req.body and why isnt the object initialized correctly
Related
I've been going back and forth on this code for sometime now and I'm trying to have a totalQty value in the cart object that returns total number of items in the cart and I want to use that value in the views of course right next to the cart icon in the navigation. Here is my code for the user model and routes:
User model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
role: {
type: String,
default: 'BASIC'
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
address: {
type: String
},
apartment: {
type: String
},
country: {
type: String
},
state: {
type: String
},
city: {
type: String
},
zip: {
type: String
},
phone: {
type: String
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
resetToken: String,
resetTokenExpiration: Date,
cart: {
items: [
{
productId: {
type: Schema.Types.ObjectId,
ref: 'Product',
required: true
},
quantity: { type: Number, required: true }
},
],
totalQty: {
type: Number,
default: 0
}
}
}, { timestamps: true });
userSchema.methods.addToCart = function (product) {
const cartProductIndex = this.cart.items.findIndex(cp => {
return cp.productId.toString() === product._id.toString();
});
let newQuantity = 1;
// let newTotalQty = 1;
const updatedCartItems = [...this.cart.items];
if (cartProductIndex >= 0) {
newQuantity = this.cart.items[cartProductIndex].quantity + 1;
updatedCartItems[cartProductIndex].quantity = newQuantity;
newTotalQty = this.cart.totalQty + 1;
updatedTotalQty = newTotalQty;
} else {
updatedCartItems.push({
productId: product._id,
quantity: newQuantity
});
}
const updatedCart = {
items: updatedCartItems,
totalQty: updatedTotalQty
};
this.cart = updatedCart;
return this.save();
};
userSchema.methods.removeFromCart = function (productId) {
const updatedCartItems = this.cart.items.filter(item => {
return item.productId.toString() !== productId.toString();
});
this.cart.items = updatedCartItems;
return this.save();
};
userSchema.methods.clearCart = function () {
this.cart = { items: [] };
return this.save();
};
module.exports = mongoose.model('User', userSchema);
User routes:
exports.getCart = (req, res, next) => {
// populate req user
req.user
.populate('cart.items.productId')
.execPopulate()
.then(user => {
const products = user.cart.items;
// render cart view
res.render('shop/cart', {
path: '/cart',
pageTitle: 'Cart - Hashing365.com',
products: products
});
})
.catch(err => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
};
exports.postCart = (req, res, next) => {
// extract prod ID
const prodId = req.body.productId;
// run DB find with prod ID
Product.findById(prodId)
.then(product => {
// return true && add to cart
return req.user.addToCart(product);
})
.then(result => {
// re-render same page
res.redirect('back');
})
.catch(err => {
const error = new Error(err);
error.httpStatusCode = 500;
return next(error);
});
};
Would really appreciate if someone could help me with a way to do that. Thanks!
You can look into Array reducer function. It should look like this
cart.totalQty = cart.items.reduce((sum, item)=>{
return sum + item.quantity;
},0);
This is my Profile Schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
// Special field type because
// it will be associated to different user
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
},
company: {
type: String,
},
website: {
type: String,
},
location: {
type: String,
},
status: {
type: String,
required: true,
},
skills: {
type: [String],
required: true,
},
bio: {
type: String,
},
githubusername: {
type: String,
},
experience: [
{
title: {
type: String,
required: true,
},
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
school: {
type: String,
required: true,
},
degree: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedin: {
type: String,
},
instagram: {
type: String,
},
},
date: {
type: Date,
default: Date.now,
},
});
module.exports = Profile = mongoose.model('profile', ProfileSchema);
This is my view api. It doesn't work. it only return Cast to ObjectId failed for value { 'experience._id': '5edcb6933c0bb75b3c90a263' } at path _id for model profile
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findById({
'experience._id': req.params.viewexp_id,
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
How can I fix this? I tried looking at the stackoverflow of the same errors. still it doesn't seem to work.
and this is what I am trying to hit
The problem is that you have to convert your string _id to mongoose object id using this function mongoose.Types.ObjectId and my suggestion is to use findOne function instead of findById,
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
let id = mongoose.Types.ObjectId(req.params.viewexp_id);
const exp = await Profile.findOne(
{ "experience._id": req.params.viewexp_id },
// This will show your sub record only and exclude parent _id
{ "experience.$": 1, "_id": 0 }
);
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
var mongoose = require('mongoose');
router.get('/experience/viewing/:viewexp_id', auth, async (req, res) => {
try {
const exp = await Profile.findOne({
'experience._id': mongoose.Types.ObjectId(req.params.viewexp_id),
});
if (!exp) {
return res.status(404).json({ msg: 'Experience not found' });
}
res.json(exp);
} catch (err) {
console.error(err.message);
res.status(500).send(err.message);
}
});
You are saving object id . but your param id is string. convert it in ObjectId. Please check my solution.
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,
githubuername,
skills,
youtube,
facebook,
twitter,
instagram,
linkedin,
} = req.body;
const profileFileds = {};
profileFileds.user = req.user.id;
if (company) profileFileds.company = company;
if (website) profileFileds.website = website;
if (location) profileFileds.location = location;
if (bio) profileFileds.bio = bio;
if (status) profileFileds.status = status;
if (githubuername) profileFileds.githubuername = githubuername;
if (skills) {
profileFileds.skills = skills.split(",").map((skill) => skill.trim());
}
//Build profile object
profileFileds.social = {};
if (youtube) profileFileds.social.youtube = youtube;
if (twitter) profileFileds.social.twitter = twitter;
if (facebook) profileFileds.social.facebook = facebook;
if (linkedin) profileFileds.social.linkedin = linkedin;
if (instagram) profileFileds.social.instagram = instagram;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//update
profile = await Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFileds },
{ new: true }
);
return res.json(profile);
}
//Create profile
profile = new Profile(profileFileds);
await profile.save();
res.json(profile);
} catch (err) {
console.error(err.message);
res.status(500).send("server Error");
}
}
);
I have created number of function in the user model which are both instance and class methods. But when i am calling class method findMyMobile() from a controller it is giving 'not a function' error. I tried display it inside the controller but it seems it is undefined there.
model/user.js
const { Sequelize, sequelize } = require('../db/sequelize');
const jwt = require('jsonwebtoken');
const Model = Sequelize.Model;
class User extends Model {}
User.init({
id:{
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING,
allowNull: false
},
mobile_number:{
field:'mobile_number',
type: Sequelize.BIGINT(10),
unique:true,
allowNull: false,
is:/^[1-9]\d{9}$/g,
},
type:{
type: Sequelize.ENUM('0','1','2'),
allowNull: false,
defaultValue: '1',
},
otp:{
type: Sequelize.STRING,
},
createdAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW
},
updatedAt: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW
}
},{ sequelize,
modelName:'user',
classMethods:{
findByMobile: function(){
var User = this;
return User.findOne({'mobile_number':data['mobile_number']}).then(user => {
return new Promise((resolve,reject)=>{
if(user)
resolve(user);
else
reject(new Error('No user found'));
});
}).catch(err=>{
return Promise.reject(new Error('Database error'));
})
}
}
})
User.sync();
module.exports = {
User
}
controller/login.js
const { User } = require('../model/user');
const _ = require('lodash');
exports.login = (req, res) => {
const mobile = _.pick(req.body, ['mobile_number']);
console.log(typeof User.findByMobile);
User.findByMobile(mobile).then((user) => {
console.log(user);
}).catch(err => {
var response = {
status: 'failure',
message: err.message
}
res.send(response);
});
};
ERROR:
TypeError: User.findByMobile is not a function
Since sequelize v4 classMethods and instanceMethod are removed from the configuration options : https://sequelize.org/v4/manual/tutorial/upgrade-to-v4.html#config-options
You have two ways to define them
class User extends Model {
//Class Method
static findByMobile() {}
//Instance Method
findByMobile() {}
}
OR
class User extends Model { }
//Class Method
User.findByMobile = function() {}
//Instance Method
User.prototype.findByMobile = function() {}
I think you have the export wrong. See this example for reference.
module.exports = {
getName: () => {
return 'Jim';
},
getLocation: () => {
return 'Munich';
},
dob: '12.01.1982',
};
Then on the import file:
const { getName, dob } = require('./user');
console.log(
`${getName()} was born on ${dob}.`
);
What I do suggest is export the function itself. See link below for ref:
What is the purpose of Node.js module.exports and how do you use it?
So I've got a classes Model which contains an array of people who will attend the class, I am trying to remove people from the classes.
So this is the Model:
const mongoose = require('mongoose');
const classMembersSchema = mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
}
})
const classSchema = mongoose.Schema({
location: {
type: String,
required: true
},
type: {
type: String,
required: true
},
name: {
type: String,
required: true
},
time: {
type: String,
required: true
},
classMembers: [classMembersSchema]
});
module.exports = mongoose.model('createClass', classSchema);
The classMembers Array is the one I mentioned that I am trying to remove members from. classMembers: [classMembersSchema].
This is the axios.delete:
deleteClassHandler = () => {
axios.delete('/api/classes/' + this.props.id + '/user/' + this.props.userId)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
}
This is the route:
router.delete('/:id/user/:userId', ClassesController.deleteUser);
This is the controller:
exports.deleteUser = (req, res) => {
GymClass.findById({
_id: req.params.id
}, 'classMembers', (err) => {
if (err) {
res.status(401).json({
message: "Error Occured!"
})
} else {
GymClass.findByIdAndDelete({
"classMembers.userId" : mongoose.Types.ObjectId(req.params.userId)
}, (err) => {
if(err) {
console.log('Keeps hitting here!');
res.status(401).json({
message: "Error Occured!"
})
} else {
res.status(200).json({
message: "Success!"
})
}
});
}
})
}
Everything works fine until it hits the console.log('Keeps hitting here!');
At the start of the function the req.params.id which is the class Id of which class we want to modify and the req.params.userId which is the user we want to remove from the Array inside the Model do have the right values but when it gets to that step it gives me the Error.
I'm thinking it could be that it is not finding this:
GymClass.findByIdAndDelete({
"classMembers.userId" : mongoose.Types.ObjectId(req.params.userId)
Since it's in an Array within the classMembers. Any idea or advice to get this to work? Many thanks.
I think I'm coming up against this problem https://github.com/Automattic/mongoose/issues/1844 .
I can see how that would happen - one request comes in, and tests is being updated, and at the same time another request comes, causing another update for tests.
I have schema that look like this
const User = new mongoose.Schema({
_id: { type: String, default: uuid.v1 },
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
tests: [Test],
});
const Test = new mongoose.Schema(
{
_id: { type: String, default: uuid.v1 },
responses: [Response]
},
{
timestamps: true
}
);
const Response = new mongoose.Schema({
_id: { type: String, default: uuid.v1 },
answer: {
type: String,
enum: [
"StronglyAgree",
"Agree",
"SomewhatAgree",
"Neutral",
"SomewhatDisagree",
"Disagree",
"StronglyDisagree"
]
},
question: { type: String, ref: "Question" }
});
const Question = new mongoose.Schema({
_id: { type: String, default: uuid.v1 },
description: String,
});
I have a class, UserModel that uses mongoose's models.
It does this
async createTest(userId) {
try {
const test = await this.testModel.create();
try {
const user = await this.model.findOne({ userId });
if (user) {
user.tests.push(test);
return await user.save();
} else {
throw new Error("Non existent UserId");
}
} catch (e) {
throw e;
}
} catch (e) {
throw e;
}
}
and this is what create looks like.
async create() {
if (!this._model) {
await this._getModel();
}
try {
const questions = await this.questionModel.getAllQuestions();
const test = new this.model();
questions.forEach(question => {
const response = this.responseModel.create(question.id);
test.responses.push(response);
});
await this.model.populate(test, {
path: "responses.question",
model: "Question"
});
return test;
} catch (e) {
throw e;
}
}
I'm not sure how to re-write this to avoid the versioning problem (and I'd rather not skip versioning). The schema also makes sense to me as I don't want to carry duplicate descriptions of Questions (I might have to change the descriptions in future).
How can I do this?
The only way is that you disable the versioning by putting
{
"versionKey":false
}
at end of your schema.