I am learning Node JS and I am learning how to create OOP structure in node. I have a simple class where I am using functions to check and create user in database. I am getting TypeError when I am calling class function inside a function. My codes of class,
var method = AccessRegisterAction.prototype;
function AccessRegisterAction(parameters) {
this._bcrypt = require('bcrypt-nodejs');
this._cassandra = require('cassandra-driver');
this._async = require('async');
this._uname = parameters.uname;
this._email = parameters.email;
this._passwd = parameters.passwd;
//Connect to the cluster
this._client = new this._cassandra.Client({contactPoints: ['127.0.0.1'], keyspace: 'testdb'});
//this._bcrypt.hashSync("bacon");
}
/*
* This method "createUser" is used to create new users
*
* First it will check for user's email is in database or not.
*
* If email is registered we will process accordingly,
* else we will process accordingly.
*
* */
method.createUser = function () {
var selectUserFromDBQuery = "SELECT uid from testdb.users WHERE email = '?' ALLOW FILTERING";
this._client.execute(selectUserFromDBQuery, this._email, function (err, result) {
//if there is not any error while fetching data
if (!err) {
//if there is result, it means we have email already registered
if (result.rows.length > 0) {
//so now we need to show user is already registered error
//to the user
this.errorWhileCreatingAccount(2);
} else {
//here we are checking user's username because
//we have not found user's email in database and we
//are good to go to register user
this.userNameCheck();
}
}
else {
this.errorWhileCreatingAccount(1);
}
});
};
/*
* This method will check for username in database.
*
* If there is username registered in database than we will
* show error that username is taken otherwise
* we will process to register user.
* */
method.userNameCheck = function () {
var checkUserNameQuery = "SELECT uid from testdb.users WHERE uname = '?' ALLOW FILTERING";
this._client.execute(checkUserNameQuery, this._uname, function (err, result) {
//if there is not any error while fetching data
if (!err) {
//if there is result, it means we have email already registered
if (result.rows.length > 0) {
//so username is taken and we need to tell user to
//use different username
this.errorWhileCreatingAccount(3);
} else {
//here we are registering user and adding information into database
this.newUserCreate();
}
}
else {
this.errorWhileCreatingAccount(1);
}
});
};
/*
* This method will create new user into database
*
* Simple as that
* */
method.newUserCreate = function () {
};
/*
* This function will throw an error which was occurred during the account creation process
* */
method.errorWhileCreatingAccount = function (errorCode) {
var _error = {error: "", msg: ""};
switch (errorCode) {
case 1:
_error.error = true;
_error.msg = "There was error while checking your information. Please try again.";
break;
case 2:
_error.error = true;
_error.msg = "You have already created account with this email. " +
"Please use forgot password link if you don't remember your login details.";
break;
case 3:
_error.error = true;
_error.msg = "Username that you chose is already taken. Please try different username.";
break;
default:
_error.error = true;
_error.msg = "There was error an error. Please try again.";
break
}
};
// export the class
module.exports = AccessRegisterAction;
And the error message I am getting is,
events.js:160
throw er; // Unhandled 'error' event
^
TypeError: this.userNameCheck is not a function
at /Users/user/Desktop/Projects/nodejs/testproject/application/classes/UserAccessAction/AccessRegisterAction.js:55:22
at next (/Users/user/node_modules/cassandra-driver/lib/utils.js:616:14)
at readCallback (/Users/user/node_modules/cassandra-driver/lib/request-handler.js:202:5)
at Connection.invokeCallback (/Users/user/node_modules/cassandra-driver/lib/connection.js:584:5)
at Connection.handleResult (/Users/user/node_modules/cassandra-driver/lib/connection.js:522:8)
at emitThree (events.js:116:13)
at ResultEmitter.emit (events.js:194:7)
at ResultEmitter.each (/Users/user/node_modules/cassandra-driver/lib/streams.js:482:17)
at ResultEmitter._write (/Users/user/node_modules/cassandra-driver/lib/streams.js:466:10)
at doWrite (_stream_writable.js:307:12)
and my codes where I calling this function is,
var param = {uname: "testname", email: "fake#test.com", passwd: "test123"};
var AccessRegisterAction = require('../application/classes/UserAccessAction/AccessRegisterAction');
var register = new AccessRegisterAction(param);
register.createUser();
so can anyone tell me where my mistake is? How can I use oop with nodejs?
Thanks
Since it looks like you're using ES5 style, I'm not going to recommend arrow functions. However, as was brought out in the comments, the value of this changes inside each function statement. So, you need to save the context you'd like to use before entering the inner functions. For example:
method.createUser = function () {
var context = this;
var selectUserFromDBQuery = "SELECT uid from testdb.users WHERE email = '?' ALLOW FILTERING";
this._client.execute(selectUserFromDBQuery, this._email, function (err, result) {
//if there is not any error while fetching data
if (!err) {
//if there is result, it means we have email already registered
if (result.rows.length > 0) {
//so now we need to show user is already registered error
//to the user
context.errorWhileCreatingAccount(2);
} else {
//here we are checking user's username because
//we have not found user's email in database and we
//are good to go to register user
context.userNameCheck();
}
}
else {
context.errorWhileCreatingAccount(1);
}
});
};
Do this in each method which attempts to call this from within a new function.
You should be able to use prototypes easily enough in Node
You need to use an object first eg new AccessRegisterAction(), you'd be able to use the methods as usual.
It's hard to be sure as you've not posted the code you are using to access the method.
Related
I've been trying for a project I'm working on to develop a function for a Food chatbot. What I'm currently working on is to perform a method for a user to make a purchase of an order that is stored in firebase realtime database.
The method is set as the method for an actionMap and the actionMap is linked to an intent for knowing when to call the method and for retrieving the parameters.
My current method uses a simple check for a user's existence and status within the database before identifying the existence of the order they're trying to make a purchase for by its id by going through the user's reference path and doing a .forEach to check every order found and look at its parent folder name to check if it matches the user's order id. My code is as follows:
const MakePurchaseACTION = 'Make Purchase';
function makePurchase(app){
let email = parameter.email;
let orderId = parameter.orderId;
var currDate = currDateGenerator();
var name = email.split(".com");
//Check if User exists first in database
var userRef = database.ref().child('user/' + name);
return userRef.once('value').then(function(snapshot) {
if (snapshot.exists()) {
let statusRetrieved = snapshot.child('Status').val();
//Check if user's status in database is signed in.
if (statusRetrieved == "Signed In") {
var orderRef = database.ref().child('order/' + name);
//Check the order table for the user.
return orderRef.once('value').then(function(orderSnapshot){
let orderVal = orderSnapshot.val();
console.log(orderVal);
//Check through every child for the matching id.
orderSnapshot.forEach(function(childSnapshot) {
let orderIdFound = childSnapshot.key;
//let cost = childSnapshot.child('Cost').val();
console.log(orderIdFound);
if(orderId == orderIdFound) {
let eateryName = childSnapshot.child('Eatery').val();
let eateryLocation = childSnapshot.child('EateryLocation').val();
let deliveryAddress = childSnapshot.child('DeliveryAddress').val();
let orderItem = childSnapshot.child('OrderItem').val();
let quantity = childSnapshot.child('Quantity').val();
let cost = childSnapshot.child('Cost').val();
var purchaseRef = database.ref().child('purchase/' + name + "/" + currDate + "/" + orderId);
purchaseRef.set({
"Eatery" : eateryName,
"EateryLocation" : eateryLocation,
"DeliveryAddress": deliveryAddress,
"OrderItem" : orderItem,
"Quantity": quantity,
"Cost": cost,
"DateCreated": currDate
});
app.add("You have successfully purchased Order " + orderId);
} else {
app.add("There is no order with that id.");
}
});
});
} else {
app.add("You need to be signed in before you can order!");
}
}
else {
app.add("Sorry pal you don't exist in the database.");
}
});
}
actionMap.set(MakePurchaseACTION, makePurchase);
After checking through some firebase logs
Firebase Logs screenshot here
Firebase Realtime Database Order Table Sample
I found that the method actually completes Purchase table sample but my dialogflow returns with the stated error of:
Error: No responses defined for platform: undefined and displays "Not Available" back to the user. My question is how do I go about resolving this error?
I have defined an array Account[] family in my model.cto file and I want to access it from my logic.js. In particular I want to perform the transaction only if the receiver is in the family array of the sender.
My model.cto:
namespace org.digitalpayment
asset Account identified by accountId {
o String accountId
--> Customer owner
o Double balance
}
participant Customer identified by customerId {
o String customerId
o String firstname
o String lastname
--> Account[] family optional
}
transaction AccountTransfer {
--> Account from
--> Account to
o Double amount
}
My logic.js:
/**
* Account transaction
* #param {org.digitalpayment.AccountTransfer} accountTransfer
* #transaction
*/
async function accountTransfer(accountTransfer) {
if (accountTransfer.from.balance < accountTransfer.amount) {
throw new Error("Insufficient funds");
}
if (/*TODO check if the family array contains the receiver account*/) {
// perform transaction
accountTransfer.from.balance -= accountTransfer.amount;
accountTransfer.to.balance += accountTransfer.amount;
let assetRegistry = await getAssetRegistry('org.digitalpayment.Account');
await assetRegistry.update(accountTransfer.from);
await assetRegistry.update(accountTransfer.to);
} else {
throw new Error("Receiver is not part of the family");
}
}
Ok so basically you want to first get all the accounts of a Family asset, and then check if the Customer participant is included in it? Correct me if I am wrong.
A logical set of steps would be -
Retrieve the Account based on the to and from inputs
Retrieve each Customer for each Account using the owner variable
Get the family variable from each Customer
/**
* Account transaction
* #param {org.digitalpayment.AccountTransfer} accountTransfer
* #transaction
*/
async function accountTransfer(accountTransfer) {
if (accountTransfer.from.balance < accountTransfer.amount) {
throw new Error("Insufficient funds");
};
var from = accountTransfer.from;
var to = accountTransfer.to;
var fromCustomer = from.owner;
var toCustomer = to.owner;
var fromCustomerFamily = fromCustomer.family;
if (fromCustomerFamily && fromCustomerFamily.includes(to)) {
// perform transaction
accountTransfer.from.balance -= accountTransfer.amount;
accountTransfer.to.balance += accountTransfer.amount;
let assetRegistry = await getAssetRegistry('org.digitalpayment.Account');
await assetRegistry.update(accountTransfer.from);
await assetRegistry.update(accountTransfer.to);
} else {
throw new Error("Receiver is not part of the family");
}
}
Due to the syntax changes in the last few Composer versions may not work depending on the version you use in your project. If this does not work and you are using an older version, let me know and I will update the answer accordingly.
I'm having some issues referencing some dynamic variables which equate to some mongoose model based on user session location value. I have two scripts.
location.js & reporting.js
location.js
module.exports = function(req) {
// Setting some variables to do the following.
// Assign var to value of the client remote address. This should return the ip address.
// The remoteAddress object returns an IPV6 format. Because of this, I setup a new var to the value of the client IP stripped of the ":::ffff" portion.
// I then split the string by the period character to get an array of the sections of string.
var clientIP = req.connection.remoteAddress;
var strippedIP = clientIP.replace(/^.*:/, '');
var splitIP = clientIP.split('.')
// Determining if the site session value based on second octet of ip response.
if (splitIP[1] == '28') {
req.session.site = 'shk';
}
else if (splitIP[1] == '29') {
req.session.site = 'ftm';
}
else if (splitIP[1] == '31') {
req.session.site = 'tpe';
}
else {
req.session.site = 'ftm';
}
// Using case statement to determine the machinery model to use as well as passdowns.
switch(req.session.site) {
// Shakopee Variables
case 'shk':
console.log("You're located in Shakopee.");
var Machinery = require('../models/machinery_shk');
var Loggings = require('../models/passdowns_shk');
break;
// Fort Mill Variables
case 'ftm':
console.log("You're located in Fort Mill.");
var Machinery = require('../models/machinery_ftm');
var Loggings = require('../models/passdowns_ftm');
break;
// Tempe Variables
case 'tpe':
console.log("You're located in Tempe.");
var Machinery = require('../models/machinery_tpe');
var Loggings = require('../models/passdowns_tpe');
break;
// Default values to use if no case is matched.
default:
console.log("You're located in Default");
var Machinery = require('../models/machinery_ftm');
var Loggings = require('../models/passdowns_ftm');
break;
}
};
reporting.js - route
reportingRouter.route('/')
.get((req, res, next) => {
Location(req);
if (req.session.loggedIn === false || req.session.loggedIn === undefined || !req.session.loggedIn) {
res.redirect('/reporting/login')
}
else if (req.session.loggedIn === true) {
Loggings.find({}, (err, loggings) => {
if (err) {
throw err;
}
else {
Machinery.find({}, (err, machinery) => {
if (err) {
throw err;
}
else {
// console.log(machinery)
Shifts.find({}, (err, shifts) => {
if (err) {
throw err;
}
else {
res.render('reporting', { pageTitle: 'Reporting', loggings: loggings, machinery: machinery, shifts: shifts, ldapFullName: req.session.fullName })
// console.log(loggings)
}
})
}
})
}
})
}
})
location.js is set up to expose a function taking one parameter. That parameter is to be the express "req" object. This script also takes the client IP Address. I take the client IP and get the second octet of the string.
Based on that value, I assign a property in the session object of the req object in express session.
req.session.site = <some-value>
Once that is set, I perform a switch case on that value assigned. If some value, assign more variables to certain mongoose models. For example, I set up a "Loggings" variable to a certain mongoose model.
var Loggings = require('../models/passdown_<site-id>')
Assuming those variables are assigned, I should be able to "require" this script inside of my reporting.js script.
Inside reporting.js, I assign a variable to that module.
var Location = require('location')
Then, I call that variable and pass in the req parameter when on some route. For example, when a "GET" is performed on some route, I call this Location module "function" passing in the req object.
Location(req)
Now, assuming all of this works, shouldn't I be able to perform a mongoose query referencing the "Loggings" variable I set up in location.js? I'm getting some undefined and I believe it's due to variable scope issues. In this instance, should I "export" those model requires? For example,
exports.Loggings = require('../models/passdown_<site-id>')
Apologies for my ignorance here.
Nevermind. I decided to throw location check logic into function under app.js. Added next() method to the end of function after location variables are determined. This way, after function runs, it will proceed to any awaiting routes.
Then I tell app to use this middleware function.
app.use(checkLocation);
This works for me and the app needs. Probably not the best solution, but I got it working using this. If anyone has any other input, I'm open to it.
I am coding a simple registration form using mongoose.
I have use a javascript file to process the values of the registration form.
Here is my registrationButtonAction.js
window.onload = function() {
var User = require('/models/Mongoose Database/user_database');
// this line is causing the problem
var registerButton = document.getElementById("registerMe");
var firstName = document.getElementById("firstName");
var lastName = document.getElementById("lastName");
var usernameRegister = document.getElementById("usernameRegister");
var passwordRegister = document.getElementById("passwordRegister");
var repasswordRegister = document.getElementById("repasswordRegister");
registerButton.onclick = function () {
if(!firstName.value || !passwordRegister.value || !repasswordRegister.value || !usernameRegister.value){
alert("Enter all required fields");
}else if (passwordRegister.value != repasswordRegister.value){
alert("Passwords must match");
}else {
var newUser = new User({
username : usernameRegister.value,
password : passwordRegister.value
});
User.find({username:usernameRegister.value}, function (error, user) {
if (error) throw error;
if(user){
window.location("/register");
}else {
newUser.save(function (error) {
if(error) throw error;
});
window.location("/login");
}
// user.comparePassword(passwordRegister.value, function (error, isMatch) {
// if (error) throw error;
//
// return 1;
//})
});
}
}
}
When I comment the var User = require('/models/Mongoose Database/user_database');, all the checks are working fine inside the onclick function. But when I uncomment it, it is not recognizing the button click.
I want to know whether this is a correct way of taking values from the registration page and storing them in a mongoose database.
You are mixing server and client code. Mongoose models and Node.js functions are not accessible inside window.onload on your client.
To put it simply, you need to create a REST API to perform database operations on the server. You have all the right tools aready, just need to reorder them.
The flow would be as such :
get the values entered in the browser
call an endpoint on your server (for example /api/createUser)
in the express router, have a route called /api/createUser in which you can access your User model and perform creation/deletion/update, etc.
My suggestion would be for you to go through this tutorial which should remove your confusion and bring you up to speed in no time. Good Luck!
Also, Passport can help you with authentication, but I believe you should first learn how to build a basic API. Authentication is a tricky beast ;)
I've followed every step of this walkthrough, but when I try to create a new row, I get a 403:
code: 119
message: "This user is not allowed to perform the create
operation on Messages. You can change this setting in the Data Browser."
My code:
Messages = Parse.Object.extend("Messages")
var message = new Messages();
message.set("sender", Parse.User.current());
message.set("receiver", *anotherUser*);
message.set("subject", "foo")
message.set("body", "bar")
message.save()
.then(
function(message){
console.log("success!")
},function(error){
console.log("error: ", error);
});
My CLPs are set as follows:
It looks like someone else posted the same issue in a google group. What are we missing?
I've submitted this as a bug to Parse (Facebook), and they replied:
We have managed to reproduce this issue and it appears to be a valid bug. We are assigning this to the appropriate team.
I will update this answer once the issue has been resolved. If this issue is impacting you, please subscribe to the bug, as this will help prioritize the fix.
UPDATE
Facebook replied:
Turns out that this is actually by design. To create an object, the class should have public create permissions on it
Unfortunately, with this solution, I can create a message "from" any other user (another user set as the sender). This is unacceptable and unusable IMHO.
That has been a bug since the launch of Pointer Permissions, which effectively makes them useless. My impression is they built this with the idea of letting developers secure existing schemas in one go, but of course you need it to work for future creation.
One workaround would involve combining the older Class Level Permissions and per-row ACL's while being careful to not disable your Data Browser. Let's assume you have classes "Puppy" and "Cat" and both have a field called "owner".
In your Data Browser, for each class where it makes sense to have an owner field, you set its Class Level Permissions for Puppy and Cat each to:
Public - Read: Yes or No, depends on your use case, Write: Yes
Add a Pointer Permission for "owner" - Read: Yes, Write: Yes (can skip this for now, see below)
Then in your cloud/main.js, you can use the following as a starting point (which I often call "types" below, sorry).
When Parse fixes the creation issue, you remove the Public Write Class Level permission (above), leave the Pointer Permission one, and get rid of the workaround code below.
--
var validateAndUpdateOwnerWritePerms = function(request){
var object = request.object;
var error = null;
var owner = object.get('owner');
if (!Parse.User.current()) {
error = 'User session required to create or modify object.';
} else if (!owner) {
error = 'Owner expected, but not found.';
} else if (owner && owner.id != Parse.User.current().id && !object.existed()) {
error = 'User session must match the owner field in the new object.';
}
if (request.master) {
error = null;
}
if (error) {
return error;
}
if (object.existed()) {
return null;
}
var acl = new Parse.ACL();
acl.setReadAccess(owner, true);
acl.setWriteAccess(owner, true);
object.setACL(acl);
return null;
}
// Wrapper that makes beforeSave, beforeDelete, etc. respect master-key calls.
// If you use one of those hooks directly, your tests or admin
// console may not work.
var adminWriteHook = function(cloudHook, dataType, callback) {
cloudHook(dataType, function(request, response) {
if (request.master) {
Parse.Cloud.useMasterKey();
} else {
var noUserAllowed = false;
if (cloudHook == Parse.Cloud.beforeSave &&
(dataType == Parse.Installation || dataType == Parse.User)) {
noUserAllowed = true;
}
if (!noUserAllowed && !Parse.User.current()) {
response.error('Neither user session, nor master key was found.');
return null;
}
}
return callback(request, response);
});
};
// Set hooks for permission checks to run on delete and save.
var beforeOwnedTypeWriteHook = function(type) {
var callback = function (request, response) {
var error = validateAndUpdateOwnerWritePerms(request);
if (error) {
response.error(error);
return;
}
response.success();
};
return adminWriteHook(Parse.Cloud.beforeSave, type, callback);
return adminWriteHook(Parse.Cloud.beforeDelete, type, callback);
};
beforeOwnedTypeWriteHook('Puppy');
beforeOwnedTypeWriteHook('Cat');
Unfortunately it seems that Parse Pointer Permissions do not work as you expect it on Create. The quick fix would be to allow Create permission to Public. Then to ensure that the user who is creating a record is the same as the sender. So you need to perform a manual check in the beforeSave trigger for Messages class in cloud code and if that check fails, reject the record being created.