I'm writing a simple server/client to keep track of the amount of times a user has logged in. A user can create an account and have their count set to 1. Following logins will increase their count in the backend SQLITE3 database.
In the example below, I run the "add" function which correctly checks if the user exists already, then if not, adds the username, password, and 1 to the table of users.
This properly returns 1 as you can see in the output, but why is it erroring at the end? I'm not making any other calls, but it's returning a no such table error. The only call I make is console.log(UsersModel.add('kpam', '123'));, which is on the last line of the code. I tried looking into the line 72 of events.js, but it didn't really give me much. I added print statements to make it trace more obvious, but I have a feeling something is going on behind the scenes?
Basically, I'm confused why if I only called one function, and that function returns successfully, theres an error at the end of execution?
Here is the error returned:
:$ node warmup.js
Creating DB file.
making table!
adding user!
1
events.js:72
throw er; // Unhandled 'error' event
^
Error: SQLITE_ERROR: no such table: Users
:$
And here is my code:
var http = require('http');
var fs = require('fs');
var file = 'data.db';
var exists = fs.existsSync(file);
if (!exists) {
console.log("Creating DB file.");
fs.openSync(file, 'w');
}
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(file);
var UsersModel = {
// success, no errors/problems
SUCCESS: 1,
// cannot find the user/password pair in the database (for 'login' only)
ERR_BAD_CREDENTIALS: -1,
// trying to add a user that already exists (for 'add' only)
ERR_USER_EXISTS: -2,
// invalid user name (empty or longer than MAX_USERNAME_LENGTH) (for 'add'/'login')
ERR_BAD_USERNAME: -3,
// invalid password name (longer than MAX_PASSWORD_LENGTH) (for 'add')
ERR_BAD_PASSWORD: -4,
// maximum user name length
MAX_USERNAME_LENGTH: 128,
// maximum password length
MAX_PASSWORD_LENGTH: 128,
login: function(user, password) {
if (!UsersModel.userExists(user, false)) {
return UsersModel.ERR_BAD_CREDENTIALS;
}
if (!UsersModel.checkPassword(user, password)) {
return UsersModel.ERR_BAD_CREDENTIALS;
}
count = UsersModel.increaseCount(user);
return count;
},
add: function(user, password) {
if (UsersModel.userExists(user, true)) {
return UsersModel.ERR_USER_EXISTS;
}
if (!UsersModel.isValidUsername(user)) {
return UsersModel.ERR_BAD_USERNAME;
}
if (!UsersModel.isValidPassword(password)) {
return UsersModel.ERR_BAD_PASSWORD;
}
UsersModel.addUser(user, password);
return 1;
},
userExists: function(user, makeTable) {
if (!exists) {
if (makeTable) {
console.log('making table!');
db.run('CREATE TABLE Users (name TEXT, password TEXT, count INT)');
}
return false;
}
db.serialize(function() {
console.log('checking user!');
row = db.get("SELECT name FROM Users WHERE name = '" + user + "'");
});
return !(typeof(row.name) === 'undefined');
},
increaseCount: function(user) {
db.serialize(function() {
console.log('increasing count!');
count = db.get("SELECT count FROM Users WHERE name = '" + user + "'") + 1;
db.run("UPDATE Users SET count = '" + count + "' WHERE name = '" + user + "'");
return count;
});
},
addUser: function(user, password) {
count = 0;
console.log('adding user!');
db.run("INSERT INTO Users (name, password, count) VALUES ('" + user + "','" + password + "','" + 0 + "')");
},
checkPassword: function(user, password) {
db.serialize(function() {
console.log('checking pw!');
row = db.get("SELECT password FROM Users WHERE name = '" + user + "'");
});
return row.password == password;
},
isValidUsername: function(user) {
return user.length < 129;
},
isValidPassword: function(password) {
return password.length < 129;
}
}
console.log(UsersModel.add('kpam', '123'));
The db.run(...) calls are asynchronous. So they return immediately, and look like success to your code. However they're still running in the background. Because of this, the SELECT statement is likely starting to run before the CREATE TABLE completes. And that's why you get that error.
I notice that the SELECT statement is inside of a db.serialize(...) call. Unfortunately, that call only serializes statements that are directly inside its scope. All calls outside of the serialize block continue to run in parallel (this includes the INSERT statement that comes up later).
Your code needs to be restructured to use the callbacks that the node sqlite3 module relies on. Take a look at the simple example at:
https://github.com/mapbox/node-sqlite3/blob/master/examples/simple-chaining.js
Notice how the last parameter to each db operation is the name of the function to call after the operation is completed.
Related
I have a block of code that calls an Api and saves results if there are differences or not. I would like to return different values for DATA as layed out on the code. But this is obviously not working since Its returning undefined.
let compare = (term) => {
let DATA;
//declare empty array where we will push every thinkpad computer for sale.
let arrayToStore = [];
//declare page variable, that will be the amount of pages based on the primary results
let pages;
//this is the Initial get request to calculate amount of iterations depending on result quantities.
axios.get('https://api.mercadolibre.com/sites/MLA/search?q='+ term +'&condition=used&category=MLA1652&offset=' + 0)
.then(function (response) {
//begin calculation of pages
let amount = response.data.paging.primary_results;
//since we only care about the primary results, this is fine. Since there are 50 items per page, we divide
//amount by 50, and round it up, since the last page can contain less than 50 items
pages = Math.ceil(amount / 50);
//here we begin the for loop.
for(i = 0; i < pages; i++) {
// So for each page we will do an axios request in order to get results
//Since each page is 50 as offset, then i should be multiplied by 50.
axios.get('https://api.mercadolibre.com/sites/MLA/search?q='+ term +'&condition=used&category=MLA1652&offset=' + i * 50)
.then((response) => {
const cleanUp = response.data.results.map((result) => {
let image = result.thumbnail.replace("I.jpg", "O.jpg");
return importante = {
id: result.id,
title: result.title,
price: result.price,
link: result.permalink,
image: image,
state: result.address.state_name,
city: result.address.city_name
}
});
arrayToStore.push(cleanUp);
console.log(pages, i)
if (i === pages) {
let path = ('./compare/yesterday-' + term +'.json');
if (fs.existsSync(path)) {
console.log("Loop Finished. Reading data from Yesterday")
fs.readFile('./compare/yesterday-' + term +'.json', (err, data) => {
if (err) throw err;
let rawDataFromYesterday = JSON.parse(data);
// test
//first convert both items to check to JSON strings in order to check them.
if(JSON.stringify(rawDataFromYesterday) !== JSON.stringify(arrayToStore)) {
//Then Check difference using id, otherwise it did not work. Using lodash to help.
let difference = _.differenceBy(arrayToStore[0], rawDataFromYesterday[0],'id');
fs.writeFileSync('./compare/New'+ term + '.json', JSON.stringify(difference));
//if they are different save the new file.
//Then send it via mail
console.log("different entries, wrote difference to JSON");
let newMail = mail(difference, term);
fs.writeFileSync('./compare/yesterday-' + term +'.json', JSON.stringify(arrayToStore));
DATA = {
content: difference,
message: "These were the differences, items could be new or deleted.",
info: "an email was sent, details are the following:"
}
return DATA;
} else {
console.log("no new entries, cleaning up JSON");
fs.writeFileSync('./compare/New'+ term + '.json', []);
DATA = {
content: null,
message: "There were no difference from last consultation",
info: "The file" + './compare/New'+ term + '.json' + ' was cleaned'
}
return DATA;
}
});
} else {
console.error("error");
console.log("file did not exist, writing new file");
fs.writeFileSync('./compare/yesterday-' + term +'.json', JSON.stringify(arrayToStore));
DATA = {
content: arrayToStore,
message: "There were no registries of the consultation",
info: "Writing new file to ' " + path + "'"
}
return DATA;
}
}
})
}
}).catch(err => console.log(err));
}
module.exports = compare
So I export this compare function, which I call on my app.js.
What I want is to make this compare function return the DATA object, so I can display the actual messages on the front end,
My hopes would be, putting this compare(term) function inside a route in app.js like so:
app.get("/api/compare/:term", (req, res) => {
let {term} = req.params
let data = compare(term);
res.send(data);
})
But as I said, Its returning undefined. I tried with async await, or returning the whole axios first axios call, but Im always returning undefined.
Thank you
I have a Firebase Cloud Function that is called in my app with JavaScript.
When the function is called it fetches the user data from the user ID, then fetched a record from the Realtime Database to check for a match.
This function works but is returning "null" and finishing early instead of returning the success or error message when the match is detected.
How can I make the return text be the success or error from the match and only complete once this match is decided?
exports.matchNumber = functions.https.onCall((data, context) => {
// ID String passed from the client.
const ID = data.ID;
const uid = context.auth.uid;
//Get user data
admin.auth().getUser(uid)
.then(function(userRecord) {
// Get a database reference to our posts
var db = admin.database();
var ref = db.ref("path/to/data/" + ID);
return ref.on("value", function(snapshot) {
//Fetch current phone number
var phoneORStr = (snapshot.val() && snapshot.val().phone) || "";
//Fetch the current auth user phone number
var userAuthPhoneNumber = userRecord.toJSON().phoneNumber;
//Check if they match
if (userAuthPhoneNumber === phoneORStr) {
console.log("Phone numbers match");
var updateRef = db.ref("path/to/data/" + ID);
updateRef.update({
"userID": uid
});
return {text: "Success"};
} else {
console.log("Phone numbers DO NOT match");
return {text: "Phone number does not match the one on record."};
}
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
return {text: "Error fetching current data."};
});
})
.catch(function(error) {
console.log('Error fetching user data:', error);
return {text: "Error fetching data for authenticated user."};
});
});
Thank you
The Firebase ref.on() method doesn't return a promise, so the return statements you have in there do nothing.
You're looking for ref.once(), which returns a promise, and thus will bubble up the return statements you have within it:
return ref.once("value").then(function(snapshot) {
...
As Doug pointed out, you'll also need to return the promise from the top level. So:
//Get user data
return admin.auth().getUser(uid)
.then(function(userRecord) {
I think a firebase function updating a list that I have in the firebase database is being captured by a subscription that is subscribed to that list. From what the list output looks like on my phone (in the app)...and from what my console output looks like (the way it repeats) it seems like it is capturing the whole list and displaying it each time one is added. So (I looked this up)...I believe this equation represents what is happening:
(N(N + 1))/2
It is how you get the sum of all of the numbers from 1 to N. Doing the math in my case (N = 30 or so), I get around 465 entries...so you can see it is loading a ton, when I only want it to load the first 10.
To show what is happening with the output here is a pastebin https://pastebin.com/B7yitqvD.
In the output pay attention to the array that is above/before length - 1 load. You can see that it is rapidly returning an array with one more entry every time and adding it to the list. I did an extremely rough count of how many items are in my list too, and I got 440...so that roughly matches the 465 number.
The chain of events starts in a page that isn't the page with the list with this function - which initiates the sorting on the firebase functions side:
let a = this.http.get('https://us-central1-mane-4152c.cloudfunctions.net/sortDistance?text='+resp.coords.latitude+':'+resp.coords.longitude+':'+this.username);
this.subscription6 = a.subscribe(res => {
console.log(res + "response from firesbase functions");
loading.dismiss();
}, err => {
console.log(JSON.stringify(err))
loading.dismiss();
})
Here is the function on the page with the list that I think is capturing the entire sort for some reason. The subscription is being repeated as the firebase function sorts, I believe.
loadDistances() {
//return new Promise((resolve, reject) => {
let cacheKey = "distances"
let arr = [];
let mapped;
console.log("IN LOADDISTANCES #$$$$$$$$$$$$$$$$$$$$$");
console.log("IN geo get position #$$$$$$$5354554354$$$$$$$");
this.distancelist = this.af.list('distances/' + this.username, { query: {
orderByChild: 'distance',
limitToFirst: 10
}});
this.subscription6 = this.distancelist.subscribe(items => {
let x = 0;
console.log(JSON.stringify(items) + " length - 1 load");
items.forEach(item => {
let storageRef = firebase.storage().ref().child('/settings/' + item.username + '/profilepicture.png');
storageRef.getDownloadURL().then(url => {
console.log(url + "in download url !!!!!!!!!!!!!!!!!!!!!!!!");
item.picURL = url;
}).catch((e) => {
console.log("in caught url !!!!!!!$$$$$$$!!");
item.picURL = 'assets/blankprof.png';
});
this.distances.push(item);
if(x == items.length - 1) {
this.startAtKey4 = items[x].distance;
}
x++;
})
//this.subscription6.unsubscribe();
})
}
The subscription in loadDistances function works fine as long as I don't update the list from the other page - another indicator that it might be capturing the whole sort and listing it repeatedly as it sorts.
I have tried as as I could think of to unsubscribe from the list after I update...so then I could just load the list of 10 the next time the page with the list enters, instead of right after the update (over and over again). I know that firebase functions is in beta. Could this be a bug on their side? Here is my firebase functions code:
exports.sortDistance = functions.https.onRequest((req, res) => {
// Grab the text parameter.
var array = req.query.text.split(':');
// Push the new message into the Realtime Database using the Firebase Admin SDK.
// Get a database reference to our posts
var db = admin.database();
var ref = db.ref("profiles/stylists");
var promises = [];
// Attach an asynchronous callback to read the data at our posts reference
ref.on("value", function(snapshot) {
//console.log(snapshot.val());
var snap = snapshot.val();
for(const user in snap) {
promises.push(new Promise(function(resolve, reject) {
var snapadd = snap[user].address;
console.log(snapadd + " snap user address (((((((())))))))");
if(snapadd != null || typeof snapadd != undefined) {
googleMapsClient.geocode({
address: snapadd
}).asPromise()
.then(response => {
console.log(response.json.results[0].geometry.location.lat);
console.log(" +++ " + response.json.results[0].geometry.location.lat + ' ' + response.json.results[0].geometry.location.lng + ' ' + array[0] + ' ' + array[1]);
var distanceBetween = distance(response.json.results[0].geometry.location.lat, response.json.results[0].geometry.location.lng, array[0], array[1]);
console.log(distanceBetween + " distance between spots");
var refList = db.ref("distances/"+array[2]);
console.log(snap[user].username + " snap username");
refList.push({
username: snap[user].username,
distance: Math.round(distanceBetween * 100) / 100
})
resolve();
})
.catch(err => { console.log(err); resolve();})
}
else {
resolve();
}
}).catch(err => console.log('error from catch ' + err)));
//console.log(typeof user + 'type of');
}
var p = Promise.all(promises);
console.log(JSON.stringify(p) + " promises logged");
res.status(200).end();
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
});
What is weird is, when I check the firebase functions logs, all of this appears to only run once...but I still think the subscription could be capturing the whole sorting process in some weird way while rapidly returning it. To be as clear as possible with what I think is going on - I think each stage of the sort is being captured in an (N(N + 1))/2...starting at 1 and going to roughly 30...and the sum of the sorting ends up being the length of my list (with 1-10 items repeated over and over again).
I updated to angularfire2 5.0 and angular 5.0...which took a little while, but ended up solving the problem:
this.distanceList = this.af.list('/distances/' + this.username,
ref => ref.orderByChild("distance").limitToFirst(50)).valueChanges();
In my HTML I used an async pipe, which solved the sorting problem:
...
<ion-item *ngFor="let z of (distanceList|async)" no-padding>
...
The problem : the data always get updated into 4350,
And the alert keep's pop-up-ing.
The code:
// Get no antrian function
function getNoAntri(tipe, username, name) {
// Define firebase URL
var faskesRef = new Firebase("https://cepatsembuh.firebaseio.com/" + tipe + "/faskes/" + username);
// Log firebase URL
console.log('Url :' + "https://cepatsembuh.firebaseio.com/" + tipe + "/faskes/" + username);
// Warn user that this fiture need internet
alert('Fitur ini membutuhkan internet untuk mengambil data');
// Confirmation
alert("Mohon konfirmasi ulang");
var nama = prompt("Masukan nama"),
nik = prompt("Masukan NIK:");
if (nama != "" || nik.length != 16) {
var pasien = new Firebase("https://cepatsembuh.firebaseio.com/" + tipe + '/pasien/');
// Initialize data
faskesRef.on("value", function(snapshot) {
// Update variables
var data = snapshot.val().antrian,
one = 1,
sum = data + one;
// Update nomor antrian
faskesRef.update({
nama: name,
antrian: sum
});
// Print data
alert('No antrian: ' + snapshot.val().antrian);
// Push data to firebase
pasien.push().set({
nama: nama,
nomor_antrian: snapshot.val().antrian
})
});
} else {
// Error message
alert("Input anda tidak valid. \n Anda tidak bisa mendapatkan nomor antrian");
}
}
I've try many ways, but the code still never work.
Sorry If I doesn't ask a proper question btw
It's a bit unclear what your problem is, but an educated guess is that it boils down to this fragment of your code:
// Push input value to firebase
pasien.push().set({
nama: nama,
nik: nik,
lokasi: lokasi
});
window.location.href = 'option/' + 'available.html';
Writing data to Firebase is an asynchronous operation. Calling set() starts that operation, but by the time the set window.location, the write operation won't be done yet.
The solution is to wait for the write operation to complete before navigating away, which you can do by using a Firebase completion listener:
// Push input value to firebase
pasien.push().set({
nama: nama,
nik: nik,
lokasi: lokasi
}, function(error) {
if (!error) {
window.location.href = 'option/' + 'available.html';
}
else {
// TODO: handle error
}
});
i am quiet new to java script and node js.
i have a problem with a simple function that i call, and it gets done more than one time.
this is my code
app.post('/checkGetSensorIds', function (req, res) {
var tables=['temperature', 'pressure', 'linear_acceleration'];
var ids= [1];
DButils.checkAllSensorsForId(connection, 1 , tables , function(idHasSensorsInfo){
console.log("idHasSensorsInfo is: \n" , idHasSensorsInfo);
});
res.end();
});
/*this function gets a user Id, and the table of all sensors the customer wants, and return true if this
user id has information in all the sesnsor tables that were requested, otherwise returns false*/
exports.checkAllSensorsForId= function(dbConnection, id , sensorsTables, callback){
var sensorsTablesLength= sensorsTables.length;
for (var i = 0; i < sensorsTables.length; i++) {
var tableName= sensorsTables[i];
DButils.checkSingleSensorForId(dbConnection, id, tableName, function(idHasSensorInfo){
if(idHasSensorInfo == false){
callback(false);
return;
}
//in case user have all info in db, we get here and need to return false
if(i == sensorsTablesLength){
callback(true);
return;
}
});
}
};
/*this function gets a user Id, and a single sensor table, and returns true if the user has information
in the requested sensor table, otherwise returns false*/
exports.checkSingleSensorForId= function(dbConnection , id , sensorTable, callback){
var myQuery = 'SELECT count(*) as IdCount FROM ' + sensorTable + ' WHERE id= ' + id;
var query = dbConnection.query(myQuery, function (err, row, result) {
console.log(query.sql);
if (err) {
console.log("checkSingleSensorForId error");
console.error(err);
return;
}
var count= row[0].IdCount;
var idHasSensorInfo = (count > 0);
callback(idHasSensorInfo);
});
};
console.log("idHasSensorsInfo is: \n" , idHasSensorsInfo); is a line that invoked 3 times, while should be only once.
someone has any idea why, and what i need to do to fix it?
You have this line:
DButils.checkAllSensorsForId(connection, 1 , tables , function(idHasSensorsInfo){
console.log("idHasSensorsInfo is: \n" , idHasSensorsInfo);
});
Then you have this:
exports.checkAllSensorsForId= function(dbConnection, id , sensorsTables, callback){
...
for (var i = 0; i < sensorsTables.length; i++) {
...
callback();
...
}
};
So the callback line will be invoked as many times as you call it, which in your case is probably 3 - all it does is call the function from above, so thats why you see it invoked 3 times.
I'm not sure exactly what you are trying to do, but if the callback should be only called once, make sure its ran only once - if it should 'cancel' the for - add a condition to the for or use a promise to resolve whenever you are ready.