Find data node js using knex - node.js

i just created a function to enter using google oauth. If the data has the same google user id, the data should not add with the same google user id, but what I created can't compare the same data.
Here's my function:
// Check user
const existingUser = new User(database)
await existingUser.getUserGoogleId({ google_user_id: profile.id })
if (existingUser) {
// eslint-disable-next-line no-console
console.log('User already exists in our DB')
// eslint-disable-next-line no-console
console.log(existingUser)
return done(null, existingUser)
}
And here's my repository:
async getUserGoogleId(google_user_id) {
const query = this.db
.select(
'id',
'google_user_id',
'username',
)
.from(TABLE_NAME)
.where({
google_user_id,
})
return query.first()
}
Can you guys tell me and maybe give some improvement from it? Thank's. sorry if I have a bad explanation

You are passing an object to your getUserGoogleId, then in the where clause you are creating a nested object.
The object that you pass to the where clause looks like:
{
google_user_id: {
google_user_id: 'some-id';
}
}
All you need to fix this is pass the object it self:
async getUserGoogleId(google_user_id) {
return this.db
.select('id', 'google_user_id', 'username')
.from(TABLE_NAME)
.where(google_user_id)
// --------^
.first();
}
Tip: enable .debug() at the end of the query to check what query & params are passed to the DB.

Related

Unable to get document using document.findOne()

I tried to get a document using document.findOne() but it's value is showing undefined .
Here is my code
`app.post("/studentlogin",(req,res)=>
{
let password;
console.log("login page");
bcrypt.hash(req.body.password,saltRounds,(err,hash)=>
{
const user= Student.findOne({srno:req.body.srno});
console.log(user.srno);
if(req.body.srno==user.srno && hash==user.password)
{
session=req.username;
session.userid=req.body.srno;
res.redirect("/");
}
else{
console.log("invalid user");
res.redirect("/studentlogin");
}
});
})`
I'm implementing session authentication using express-session. Here when I log the user it's showing schema and bunch of other stuff which I don't know(The error is too long) . user.srno is also showing as undefined. How can I fix it?
I tried using call-back function which gave me required document correctly. But I want the query to return the correct document and store it in user.
Using callback function
`app.post("/studentlogin",(req,res)=>
{
let password;
console.log("login page");
bcrypt.hash(req.body.password,saltRounds,(err,hash)=>
{
Student.findOne({srno:req.body.srno},(err,result)=>
{
console.log(result);
});
//console.log(user.srno);
if(req.body.srno==user.srno && hash==user.password)
{
session=req.username;
session.userid=req.body.srno;
res.redirect("/");
}
else{
console.log("invalid user");
res.redirect("/studentlogin");
}
});
})`
You need to wait the result from your query on the database before doing the next task like comparing your password, and looks like you just try to log in, you re not going to register a new one, so it's better to use the compare method in Bcrypt like this :
app.post("/studentlogin", async (req , res) => {
const {srno, password} = req.body // destructuring your request is better for visibility
try {
const user = await Student.findOne({srno: srno});//await the result before next step
console.log(user.srno) //you can check
if(!user) {
console.log("invalid user");
// your logic to tell not student found /wrong username or password, and/or redirect
}
const isMatch = await bcrypt.compare(password, user.password) //await the result and this method for comparing the request password and the user's password found
if(!isMatch) {
//your code to tell Wrong username or password
res.redirect("/studentlogin");
} else {
// your code to access to the login.
res.redirect("/");
}
} catch (err) {
console.log(err)
}
The error you are facing is because you are not using async await in your code. use async in your function definition then use await where you are searching the database.
const someFunction = async(req,res)=>{
// your code
const user=await Student.findOne({srno:req.body.srno});
// your code
}
Your code should look like this.
app.post("/studentlogin", async(req,res)=> {
// your code
const user=await Student.findOne({srno:req.body.srno});
// your code
}
console.log(user) to verify.
Hope it helps.

Firebase Functions won't read document on Firestore

Hi I'm trying to read a users document stored on Firestore using Firebase Functions. Each user has a unique document with extra data that cannot be stored on Firebase Auth. The document name is the user UID.
But I can't access the doc when I'm trying to read it on my callable function.
Code to create doc when user is created:
exports.createdacc = functions.auth.user().onCreate(user => {
console.log('User created', user.phoneNumber);
return admin.firestore().collection('users').doc(user.uid).set({
number: user.phoneNumber,
verified: false,
});
});
Callable function to read that doc so I can make some decisions
exports.checkVerification = functions.https.onCall((data, context) => {
if (!context.auth){
throw new functions.https.HttpsError('unauthenticated');
}
console.log('user is ', context.auth.uid);
const user = admin.firestore().collection('users').doc(context.auth.uid);
user.get().then(doc => {
//temp code -- Not working
console.log('data read');
if (doc.get().verified){
console.log('verified');
} else {
console.log('not verified');
}
return "success";
}).catch(error => {
throw new functions.https.HttpsError('internal');
});
});
Why cant I read the doc? Nothing inside there executes.
Try to use data() at callback of user.get()
user.get().then(doc => {
//you get user doc value by using data()
const userData = doc.data();
// then you can use all properties from userData
const verified = userData.verified;
});
You don't return the promise returned by user.get().then(...);: your Cloud Function may be cleaned up before the asynchronous work is complete and the response sent back to the front-end.
Note that doing doc.get().verified is incorrect: as you will see in the doc, you need to pass the field path of a specific field to this method. So either you do doc.get("verified") or you can do doc.data().verified;.
Therefore the following should work:
exports.checkVerification = functions.https.onCall((data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated');
}
console.log('user is ', context.auth.uid);
const user = admin.firestore().collection('users').doc(context.auth.uid);
return user.get().then(doc => {
console.log('data read');
if (doc.get("verified") {
console.log('verified');
} else {
console.log('not verified');
}
return "success";
}).catch(error => {
throw new functions.https.HttpsError('internal');
});
});
In addition, note that you may throw an error if the user document does not exist and return a specific error to the front-end, i.e. not the generic internal one (maybe not-found, see the list of possible codes).
I have seen, on occasion, that information coming in to the function via context and data are actually JSON, and not strictly a standard Javascript object. In a similar issue of matching (in my case, a customClaim on the context.auth.token), I had to do something like:
JSON.parse(JSON.stringify(context.auth.token.customCLaim))
They behave like an object (i.e. I can call/assign context.auth.token.customClaim), but results from a console.log are different.
console.log(context.auth.token.customCLaim);
//prints {"userID": "1234567890"}
console.log(JSON.parse(JSON.stringify(context.auth.token.customClaim)));
//prints {userID: "1234567890"}
Subtle, but it tripped me up in a few authentication cases.

How to update the user object in back4app?

I use Node.js and back4app.com
I try to update the user object. Therefore I have read a lot and found this promissing documentation:
let progressId = "xyz";
let userId = "12354"; //aka objectId
const User = new Parse.User();
const query = new Parse.Query(User);
// Finds the user by its ID
query.get(userId).then((user) => {
// Updates the data we want
user.set('progressId', progressId);
// Saves the user with the updated data
user.save()
.then((response) => {
console.log('Updated user', response);
})
.catch((error) => {
console.error('Error while updating user', error);
});
});
But there also is a warning. It states:
The Parse.User class is secured by default, you are not able to invoke save method unless the Parse.User was obtained using an authenticated method, like logIn, signUp or current
How would this look like in code?
My solution
Well, I got it to work. While I figured it out, I have found some small show stoppers. I list it for anyone it may concern.
Thanks #RamosCharles I added the Master Key in Parse._initialize. Only with that .save(null, {useMasterKey: true}) works. Take notice, without null it also won't work.
That's my working code:
let progressId = "xyz";
const User = Parse.Object.extend('User'); //instead of const User = new Parse.User();
const query = new Parse.Query(User);
query.equalTo("objectId", '123xyz');
query.get(userId).then((userObj) => {
// Updates the data we want
userObj.set('progressId', progressId);
// Saves the user with the updated data
userObj.save(null, {useMasterKey: true}).then((response) => {
console.log('Updated user', response);
}).catch((error) => {
console.error('Error while updating user', error);
});
});
Now I'm wondering
why my working code is different from documentation?
how secure is my code? And what is to do to get it more secure?
Yes, their API Reference is very helpful! On this section, there's a "try on JSFiddle" button, have you already seen that?
To update a user object, you must use the Master Key. On the frontend, it's not recommended, and it's better to create a cloud code function and call it on your frontend. However, for test purposes, you can keep using the API Reference, but on JSFiddle, you need to do some changes, here is their sample code, but with the adjustments:
Parse.serverURL = 'https://parseapi.back4app.com';
Parse._initialize('<your-appID-here>', '<your-JSKey-here>', '<Your-MasterKey-here>');
const MyCustomClass = Parse.Object.extend('User');
const query = new Parse.Query(MyCustomClass);
query.equalTo("objectId", "<object-ID-here>");
query.find({useMasterKey: true}).then((results) => {
if (typeof document !== 'undefined') document.write(`ParseObjects found: ${JSON.stringify(results)}`);
console.log('ParseObjects found:', results);
}, (error) => {
if (typeof document !== 'undefined') document.write(`Error while fetching ParseObjects: ${JSON.stringify(error)}`);
console.error('Error while fetching ParseObjects', error);
});
You'll need to insert the "_" before the "initialize" in your "Parse._initialize" and insert the Master Key in your query as I did on the query.find.

Google Cloud Datastore: lookup and update in one transaction

I'm following the Cloud Datastore sample from the Google documentation as well as the Github sample, following the tasks sample. I'm trying to make a single function call, and mark a task as done by looking it up by the description.
function markDoneByDesc(queryString) {
const query = datastore
.createQuery('Task')
.filter('description', '=', queryString);
var taskKeyId;
datastore
.runQuery(query)
.then(results => {
const tasks = results[0];
console.log('Task found:', tasks[0]);
// I realize there might be multiple tasks with the same desc,
// but I want to update just one for now
taskKeyId = tasks[0][datastore.KEY].id;
console.log('Saving the task Key ID', taskKeyId);
return taskKeyId;
})
.then((taskKeyId) => {
console.log('Calling markDone with task Key ID', taskKeyId);
markDone(taskKeyId); // From the original function in the sample
console.log('Updated task');
})
.catch(err => {
console.error('ERROR:', err);
});
}
Right now, the update doesn't happen :(
I found the solution, thanks to #callmehiphop's help!
Looks like I need to convert the taskKeyId that is returned in the datastore query into an integer, and then pass it to the markDone() function. Otherwise it is passed as a string and the lookup by that ID Key fails.
Here's what the correct code should look like (note the parseInt() in the first return statement):
function markDoneByDesc(queryString) {
const query = datastore
.createQuery('Task')
.filter('description', '=', queryString);
var taskKeyId;
datastore
.runQuery(query)
.then(results => {
const tasks = results[0];
console.log('Task found:', tasks[0]);
// I realize there might be multiple tasks with the same desc,
// but I want to update just one for now
taskKeyId = tasks[0][datastore.KEY].id;
console.log('Saving the task Key ID', taskKeyId);
return parseInt(taskKeyId,10);
})
.then((taskKeyId) => {
console.log('Calling markDone with task Key ID', taskKeyId);
markDone(taskKeyId); // From the original function in the sample
console.log('Updated task');
})
.catch(err => {
console.error('ERROR:', err);
});
}

What's the correct syntax to check if cypher query return zero row in javascript

I am trying to determine if a username exists before creating the user. I am using the following code. I need an elegant way to determine if the zero row is returned ...username doesn't exist. for example I know the returned row value would be zero if not found. How can I get access to the row value in the code. Can someone assist...thanks...BTW I am using neo4j3.0 Nodejs with express and Passport
neo4jSession
.run(MATCH (user {email: newUser.email}) RETURN user);
.then (function(result) {
if ((not found) {
.run(CREATE (user: {email:newUser.email, password:newUser.password} ) ASSERT email is UNIQUE RETURN user);
neo4jSession.close();
}) //end of if not found
else (found)
{
// email address already exist
console.log("email address already exist");
neo4jSession.close();
}
}); //end .then
.catch(function(error) {
console.log(error);
});
The Neo4j Driver for JavaScript record module can check if a value from record exists by index or field key using the has method. When evaluating or validating the existence of any field within a record (e.g. determining if a User node contains an existing email address property), using the has instead of the get method can allow for shorter Cypher statements and condensed javascript code; which (IMO) can lead to elegance you are seeking.
Using your original example, you can use a simple Cypher statement to search if a User node contains an email property by passing in a user's input. Utilizing the Neo4j Driver for JavaScript, you can return a result stream with a single record.
Cypher Statement:
MATCH ( u:User { email: $email } )
RETURN u, u.email
If an email address exists as a User node property in the Neo4j database, a stream of records with one field named "u.email" be will returned . The record represents one user found by the statement above. You can access the field value by key using the record module's has method.
Access Record by Field Key:
result.records[0].has('u.email')
The following example is one of many ways you could implement both the simple Cypher statement and has method:
async (_, { email, password }) => {
const session = await driver.session()
const closeSession = await session.close()
const endSession = await driver.close()
let query = 'MATCH (u:User{email: $email}) RETURN u, u.email'
return session
.run(query, { email })
.then(async result => {
closeSession()
let emailExists = result.records[0].has('u.email')
let newUser = result.records[0].get('u').properties
if (
(Array.isArray(result.records) && !result.records.length) ||
(Object.keys(result).length === 0 && result.constructor === Object)
) {
if (!emailExists) {
let query =
'MERGE (u:User { email: $email }) ON CREATE SET u.password = $password RETURN u'
return session
.run(query, { email, password })
.then(result => {
closeSession()
return newUser
})
} else if (Array.isArray(result.records) && result.records.length) {
const emailExists = result.records[0].has('u.email')
if (emailExists) {
closeSession()
throw new Error(emailExists + ' already exists.')
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
})
.catch(function(err) {
closeSession()
endSession()
if (err) throw err
})
}
Note: This example validates whether a record exists first by evaluating the result with conditional statements, then the e-mail property is checked. A few errors have been handled as well.
Query (use counter):
MATCH (user {email: newUser.email})
RETURN count(user)=1 as user_exists
In javascript:
if ( result.records[0].get('user_exists') !== true ) {
// create new user
}
And, of course, add a unique constraint to the email address for the user.
Though the answer from #stdob is an accepted answer on further research I found out that in case the record do exist, you can't retrieve any data on that query... so:
MATCH (user {email: newUser.email})
RETURN user.name AS Name, count(user)=1 as user_exists
wont yield any data if user exist. The following works:
MATCH (user {email: newUser.email}) RETURN user;
if (!result[0]) {
//no records found
}
else {get user properties}
Thanks to:
https://github.com/mfong/node-neo4j-passport-template/blob/master/models/user.js

Resources