Amer
Amer

Reputation: 71

Function returns a promise rather than the object it should return

I was writing a MEAN stack application when I came across a rather peculiar error that I don't quite understand.

One of the functions I wrote should return a regular JSON object with certain parameters that will be set in the execution of the function. However, that is not what happens. It instead returns a promise object.

I created a user model, and then created some methods/functions for it. The function in question that returns a promise is the validate function.

What this function does is simply that it makes sure that the data the user entered is in check! You can tell from the code in user.js that it merely checks the length of the input data as well as match it to some predefined regular expressions to see if the data is within acceptable limits (in order not to cause problems later).

I call this function when a user registers which happens in the register function in registerController.js which also should find if the user already exists (has created an account before) or if the username he chose is taken (username exists) after that it sends them a confirmation email containing a link that is their temporaryToken. The route that leads the user to register is in registerRoutes.js. I tried logging the value of the objects received from the functions checkData and validate. checkData returns a normal object while validate returns a promise even though it shouldn't.

Here is the user file user.js

const mongoose = require('mongoose');

var userSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true,
        unique: true
    },
    email:{
        type: String,
        required: true,
        unique: true
    },
    password:{
        type: String,
        required: true
    },
    firstName:{
        type: String,
        required: true
    },
    lastName:{
        type: String,
        required: true
    },
    confirmed:{
        type: Boolean,
        default:false,
        required: true
    },
    temporaryToken:{
        type: String,
        default: "NO_TOKEN",
        required: true
    }
});

userSchema.method({
    checkData: function() {
        let checkData = {};
        checkData.missing = [];
        checkData.wrongType = [];
        checkData.success = true;
        if(!this.username)
        {
            checkData.success = false;
            checkData.missing.push("username");
        }
        if(!this.email)
        {
            checkData.success = false;
            checkData.missing.push("email");
        }
        if(!this.password)
        {
            checkData.success = false;
            checkData.missing.push("password");
        }
        if(!this.firstName)
        {
            checkData.success = false;
            checkData.missing.push("firstName");
        }
        if(!this.lastName)
        {
            checkData.success = false;
            checkData.missing.push("lastName");
        }
        return checkData;
    },
    validate: function() {
        let validation = {};
        validation.errors = [];
        validation.success = true;
        if(this.username.length < 2 || this.username.length > 35)
        {
            validation.success = false;
            validation.errors.push({
                "field": "username",
                "message": "Invalid length of username. Username must be between 2 and 35 characters long."
            });
        }
        if(this.email.length < 6 || this.username.length > 256)
        {
            validation.success = false;
            validation.errors.push({
                "field": "email",
                "message": "Invalid length of email. Email must be between 6 and 256 characters long."
            });
        }
        if(this.password.length < 8 || this.password.length > 50)
        {
            validation.success = false;
            validation.errors.push({
                "field": "password",
                "message": "Invalid length of password. Password must be between 6 and 256 characters long."
            });
        }
        if(this.firstName.length < 2 || this.firstName.length > 35)
        {
            validation.success = false;
            validation.errors.push({
                "field": "firstName",
                "message": "Invalid length of first name. First name must be between 2 and 35 characters long."
            });
        }
        if(this.lastName.length < 2 || this.lastName.length > 35)
        {
            validation.success = false;
            validation.errors.push({
                "field": "lastName",
                "message": "Invalid length of last name. Last name must be between 2 and 35 characters long."
            });
        }
        let usernameRegex = /^[a-zA-Z0-9$#@%`'"\.]+$/
        if(!usernameRegex.test(this.username))
        {
            validation.success = false;
            validation.errors.push({
                "field": "username",
                "message": "Invalid format of username. Username can only contain Alphanumeric characters and $ # @ % ` ' \" and .."
            });
        }
        let emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
        if(!emailRegex.test(this.email))
        {
            validation.success = false;
            validation.errors.push({
                "field": "email",
                "message": "Invalid format and email. Email has to be in the form [email protected]."
            })
        }
        let passwordRegex = /^[A-Za-z0-9$#@%`'"\.]+$/;
        if(!passwordRegex.test(this.password))
        {
            validation.success = false;
            validation.errors.push({
                "field": "email",
                "message": "Invalid format of password. Password an only contain Alphanumeric characters and $ # @ % ` ' \" and .."
            });
        }
        let nameRegex = /^[A-Z][a-z-]+$/;
        if(!nameRegex.test(this.firstName))
        {
            validation.success = false;
            validation.errors.push({
                "field": "firstName",
                "message": "Invalid format of first name. First Name can only contain English letters and hyphens (-)."
            });
        }
        if(!nameRegex.test(this.middleName))
        {
            validation.success = false;
            validation.errors.push({
                "field": "middleName",
                "message": "Invalid format of middle name. Middle Name can only contain English letters and hyphens (-)."
            });
        }
        if(!nameRegex.test(this.lastName))
        {
            validation.success = false;
            validation.errors.push({
                "field": "lastName",
                "message": "Invalid format of last name. Last Name can only contain English letters and hyphens (-)."
            });
        }
        return validation;
    },
    capitalizeNames: function() {
        this.firstName = this.firstName.charAt(0).toUpperCase() + this.firstName.slice(1);
        this.lastName = this.lastName.charAt(0).toUpperCase() + this.lastName.slice(1);
    }
});

const UserModel = mongoose.model("user", userSchema);

module.exports = UserModel;

Here is the register controller file registerController.js

const User = require("../model/user.js");
const system = require("../middleware/system.js");
const mails = require("../../config/mails.js");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const nodemailer = require('nodemailer');

let registerController = {
    register: function(req, res, next) {
        let newUser = new User ({
            "username": req.body.username,
            "email": req.body.email,
            "password": req.body.password,
            "firstName": req.body.firstName,
            "lastName": req.body.lastName,
        });
        let check = newUser.checkData();
        if(!check.success)
        {
            res.status(400).json(check);
            next();
            return;
        }
        newUser.capitalizeNames();
        let validity = newUser.validate();
        console.log(validity); // "Promise { <pending> }"
        if(!validity.success)
        {
            res.status(400).json(validity);
            next();
            return;
        }
        newUser.findOne({"username": newUser.username}, function(err, foundUser1) {
            if(err)
            {
                system.handleServerError(res);
                next();
                return;
            }
            if(foundUser1)
            {
                res.status(403).json({
                    "success": false,
                    "message": "The user with the name " + newUser.username + " already exists. Please choose another name."
                });
                next();
                return;
            }
            newUser.findOne({"email": newUser.email}, function(err, foundUser2) {
                if(err)
                {
                    system.handleServerError(res);
                    next();
                    return;
                }
                if(foundUser2)
                {
                    res.status(403).json({
                        "success": false,
                        "message": "The user with the email " + newUser.email + " already exists. If you already have an account, please log in."
                    });
                    next();
                    return;
                }
                bcrypt.hash(newUser.password, saltRounds, function(err, hash) {
                    newUser.password = hash;
                    newUser.temporaryToken = jwt.sign({
                        "email": newUser.email
                    }, "confirm", {
                        expiresIn: 60*60*24*365
                    });
                    newUser.save(function(err, product, numAffected) {
                        if(err)
                        {
                            system.handleServerError(res);
                            next();
                            return;
                        }
                        // SEND EMAIL TO USER
                        res.status(200).json({
                            "success": true,
                            "message": "Your registration has been completed successfully. A confirmation link has been sent to your email. Please use that link to actvate your account and login."
                        });
                        next();
                        return;
                    });
                });
            });
        });
    }
};

module.exports = registerController;

Here is the routes file registerRoutes.js

const express = require("express");
const router = express.Router();
const registerController = require("../controllers/registerController.js")

router.post("/api/register", registerController.register);

module.exports = router;

Please tell me if there is anything other information that I can provide or clarify. And thank you all for your time. :)

Upvotes: 2

Views: 65

Answers (1)

Amer
Amer

Reputation: 71

The error is in the naming of the function.

validate() is already defined in the mongoose module. Calling it on an instance of the user model called the native mongoose function that expected a callback, and therefore returned a promise.

Thankfully, changing validate() to validator() solved the problem.

Upvotes: 2

Related Questions