Ionic2 - SQLite executing SQL - sqlite

I'm creating app with using SQLite. I have some problems with using SQLite.
I have page with variable for managing database.
public sqlite_object: any;
In the constructor i'm opening/creating if not exists database, and saving db object to variable.
constructor(...){
let name = 'db_name';
this.openDatabase(name);
}
openDatabase(name){
let db = new SQLite();
db.create({ name: name, location: 'default' })
.then( (db_obj: SQLiteObject) => {
this.sqlite_object = db_obj
}, (error) => {
alert('error');
}
}
So, in the constructor i'm opening db, and saving it for future.
On of buttons calling that function:
testSQL(sql_queries){
this.sqlite_object.transaction( function(tx){
Object.keys(sql_queries)
.sort()
.forEach( function(v,i){
tx.executeSql(sql_queries[v],
null,
function (transaction, result){
alert('executing sql');
},
function (transaction, error){
alert('error');
});
});
}, function (error){
alert('error2');
}, function (){
alert('success');
}
}
My sql_queries have about ~30 queries (correct and incorrect).
When i put alert into forEach(), it will be executed everytime (same times as sql_queries length).
When executeSql(...) getting incorrect query, alert about error is showing, but i never seen alert 'executing sql' - is sth wrong here? (i don't know if my queries executing correctly)
Also i have one more question. How can i get list of tables from my database?

your "executing sql.." is inside a success callback function. it will be executed after the entire for each() query is successful (not after each loop). So as you said it's showing error. So, It will not execute the successcallback function. else it will execute the errorcallback function. this is beacause your entire query is not successful. I hope you get the point

Related

How to implement transactions in Meteor Method calls

Suppose I have 2 collections "PlanSubscriptions" and "ClientActivations". I am serially doing a insert on both the collections.
Later one depends on previous one, if any of the transaction fails then the entire operation must rollback.
How can I achieve that in Meteor 1.4?
Since MongoDB doesn't support atomicity, you will have to manage it with Method Chaining.
You can write a method, say, transaction where you will call PlanSubscriptions.insert(data, callback). Then in the callback function you will call ClientActivations.insert(data, callback1) if the first insertion is success and in callback1 return truthy if second insertion is succes, otherwise falsy. If the first insertion returns error you don't need to do anything, but if the second insertion returns error then remove the id got from the insertion in first collection.
I can suggest following structure:
'transaction'(){
PlanSubscriptions.insert(data, (error, result)=>{
if(result){
// result contains the _id
let id_plan = result;
ClientActivations.insert(data, (error, result)=>{
if(result){
// result contains the _id
return true;
}
else if(error){
PlanSubscriptions.remove(id_plan);
return false;
}
})
}
else if(error){
return false;
}
})
}
There is no way to do that in Meteor, since mongodb is not an ACID-compliant database. It has a single-document update atomicity, but not a multiple-document one, which is your case with the two collections.
From the mongo documentation:
When a single write operation modifies multiple documents, the modification of each document is atomic, but the operation as a whole is not atomic and other operations may interleave.
A way to isolate the visibility of your multi-document updates is available, but it's probably not what you need.
Using the $isolated operator, a write operation that affects multiple documents can prevent other processes from interleaving once the write operation modifies the first document. This ensures that no client sees the changes until the write operation completes or errors out.
An isolated write operation does not provide “all-or-nothing” atomicity. That is, an error during the write operation does not roll back all its changes that preceded the error.
However, there are a couple of libraries which try to tackle the problem at the app-level. I recommend taking a look at fawn
In your case, where you have exactly two dependent collections, it's possible to take advantage of the two phase commits technique. Read more about it here: two-phase-commits
Well I figured it out myself.
I added a package babrahams:transactions
At server side Meteor Method call, I called tx Object that is globally exposed by the package. The overall Server Side Meteor.method({}) looks like below.
import { Meteor } from 'meteor/meteor';
import {PlanSubscriptions} from '/imports/api/plansubscriptions/plansubscriptions.js';
import {ClientActivations} from '/imports/api/clientactivation/clientactivations.js';
Meteor.methods({
'createClientSubscription' (subscriptionData, clientActivationData) {
var txid;
try {
txid = tx.start("Adding Subscription to our database");
PlanSubscriptions.insert(subscriptionData, {tx: true})
ClientActivations.insert(activation, {tx: true});
tx.commit();
return true;
} catch(e){
tx.undo(txid);
}
return false;
}
});
With every insert I had added {tx : true}, this concluded it to be a apart of transaction.
Server Console Output:
I20170523-18:43:23.544(5.5)? Started "Adding Subscription to our database" with
transaction_id: vdJQvFgtyZuWcinyF
I20170523-18:43:23.547(5.5)? Pushed insert command to stack: vdJQvFgtyZuWcinyF
I20170523-18:43:23.549(5.5)? Pushed insert command to stack: vdJQvFgtyZuWcinyF
I20170523-18:43:23.551(5.5)? Beginning commit with transaction_id: vdJQvFgtyZuWcinyF
I20170523-18:43:23.655(5.5)? Executed insert
I20170523-18:43:23.666(5.5)? Executed insert
I20170523-18:43:23.698(5.5)? Commit reset transaction manager to clean state
For more Information you can goto link : https://github.com/JackAdams/meteor-transactions
NOTE: I am using Meteor 1.4.4.2
Just sharing this link for future readers:
https://forums.meteor.com/t/solved-transactions-with-mongodb-meteor-methods/48677
import { MongoInternals } from 'meteor/mongo';
// utility async function to wrap async raw mongo operations with a transaction
const runTransactionAsync = async asyncRawMongoOperations => {
// setup a transaction
const { client } = MongoInternals.defaultRemoteCollectionDriver().mongo;
const session = await client.startSession();
await session.startTransaction();
try {
// running the async operations
let result = await asyncRawMongoOperations(session);
await session.commitTransaction();
// transaction committed - return value to the client
return result;
} catch (err) {
await session.abortTransaction();
console.error(err.message);
// transaction aborted - report error to the client
throw new Meteor.Error('Database Transaction Failed', err.message);
} finally {
session.endSession();
}
};
import { runTransactionAsync } from '/imports/utils'; // or where you defined it
Meteor.methods({
async doSomething(arg) {
// remember to check method input first
// define the operations we want to run in transaction
const asyncRawMongoOperations = async session => {
// it's critical to receive the session parameter here
// and pass it to every raw operation as shown below
const item = await collection1.rawCollection().findOne(arg, { session: session });
const response = await collection2.rawCollection().insertOne(item, { session: session });
// if Mongo or you throw an error here runTransactionAsync(..) will catch it
// and wrap it with a Meteor.Error(..) so it will arrive to the client safely
return 'whatever you want'; // will be the result in the client
};
let result = await runTransactionAsync(asyncRawMongoOperations);
return result;
}
});

meteorjs get data returned from server callback

In meteor im trying to insert a new document by making a Meteor.call from the client. Everything is working ok, except that I want to return the id of create document to the client, so that i can redirect to the proper url.
I have something similar to this (client):
Meteor.call('scenarioCreate', scenarioObj, function(err, response) {
if(err) {
console.warn(err);
return;
}
console.info(response);
});
On server:
Meteor.methods({
'scenarioCreate': function(scenarioObj) {
Scenarios.insert( scenarioObj, function(err, id) {
console.info("new id: "+id);
if (err) {
console.warn(err);
throw new Meteor.Error(404, 'Something went wrong');
}
return id;
});
}
});
From the server side i get the console log "new id: DDaq4aWsGf3fxG7RP", but I should get that same value on the client on the "response" value, but I always get undefined.
Can anybody explain to me why or what I am doing wrong.
Meteor.methods doesn't wait for your callback. Try this:
On server:
Meteor.methods({
'scenarioCreate': function(scenarioObj) {
var newId = Scenarios.insert(scenarioObj);
return newId;
}
});
To catch any error, use try/catch
Note: If you still want to use callback, checkout fibers/future.
Your first error is that you are returning id inside the callback for insert. That doesn't do anything. Then your second is not knowing that methods are synchronously executed. That would mean that you could only use the information available inside the callback using Meteor.wrapAsync. But that's besides the point. The insert function returns the new _id. Meaning you can do this:
Meteor.methods({
'scenarioCreate': function(scenarioObj) {
return Scenarios.insert( scenarioObj );
}
});

Error: Meteor code must always run within a Fiber

I am using stripe for payments in my app, I want to create a receipt document in my own database after a succesful transaction
My code:
Meteor.methods({
makePurchase: function(tabId, token) {
check(tabId, String);
tab = Tabs.findOne(tabId);
Stripe.charges.create({
amount: tab.price,
currency: "USD",
card: token.id
}, function (error, result) {
console.log(result);
if (error) {
console.log('makePurchaseError: ' + error);
return error;
}
Purchases.insert({
sellerId: tab.userId,
tabId: tab._id,
price: tab.price
}, function(error, result) {
if (error) {
console.log('InsertionError: ' + error);
return error;
}
});
});
}
});
However this code returns an error:
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
I am not familiar with Fibers, any idea as to why this is?
The problem here is that the callback function which you pass to Stripe.charges.create is called asynchronously (of course), so it's happening outside the current Meteor's Fiber.
One way to fix that is to create your own Fiber, but the easiest thing you can do is to wrap the callback with Meteor.bindEnvironment, so basically
Stripe.charges.create({
// ...
}, Meteor.bindEnvironment(function (error, result) {
// ...
}));
Edit
As suggested in the other answer, another and probably better pattern to follow here is using Meteor.wrapAsync helper method (see docs), which basically allows you to turn any asynchronous method into a function that is fiber aware and can be used synchronously.
In your specific case an equivalent solution would be to write:
let result;
try {
result = Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)({ /* ... */ });
} catch(error) {
// ...
}
Please note the second argument passed to Meteor.wrapAsync. It is there to make sure that the original Stripe.charges.create will receive the proper this context, just in case it's needed.
You might want to take a look at the docs for http://docs.meteor.com/#/full/meteor_wrapasync.

Meteor template updates before result of Meteor.users.update

I'm trying to figure out how to prevent a template from updating until Meteor.users.update() finishes.
First I'm trying to make sense of the documentation and the use of an optional callback argument in order to sort out what is happening.
Here is what I have:
Meteor.users.update(Meteor.userId(),
{$set:{'profile.reviewList': []}},
[],
function(err, result){
if (err){
console.log('oh no!');
} else {
console.log('Result achieved: '+this.profile.reviewList);
}
});
Currently the console.log('Result achieved: '+this.profile.reviewList); always returns something like ...TypeError: Cannot read property 'reviewList' of undefined... the first time though which tells me its firing before the result comes back.
I'm sure I'm not implementing the callback properly, but I was attempting to model this answer: How do you ensure an update has finished in meteor before running a find?
I'd really just like to delay the re-rendering of the associated template until the property gets created.
Any help will be greatly appreciated.
You assume that scope (this) in callback function return user object, which is wrong.
If you want to get user object in that callback simply query it there:
var user = Meteor.users.find(Meteor.userId()).fetch()
Another thing, you passed empty array as 2nd argument which is not needed.
Meteor.users.update(
Meteor.userId(),
{
$set: {
'profile.reviewList': 'testData'
}
},
function(err, result) {
if (err) {
console.log('oh no!');
} else {
var user = Meteor.users.find(Meteor.userId()).fetch();
console.log('Result achieved: ' , user && user.profile && user.profile.reviewList);
}
}
);

How do I ensure that nested async code will test correctly using Mocha?

I am working with Mocha and am trying to test an API that I am in the process of building.
I am having trouble understanding where to place the done() function.
If I place it where it is now, it doesn't execute the callback function of User.findOne().
If I place the done at the bottom of the callback function of User.findOne(), then it creates a timeout.
I am relatively new to async and this done function, so can someone help explain why these two cases happen, and how to fix the code so that it will test correctly in Mocha?
describe('POST /signup', function() {
before(checkServerIsRunning); // Need to implement
it('create a new user if username is unique', function(done) {
httpReq({
method : 'POST',
url : url + '/signup',
json : true,
body : JSON.stringify({
username : 'test',
first : 'first',
last : 'last' })
},
function (err, res, body) {
if (err) {
done(err);
}
else {
res.statusCode.should.be.equal(201);
User.findOne( { username: 'test' }, function(err, user) {
user.should.have.property('username', 'testy');
user.should.have.property('firstName', 'first');
user.should.have.property('lastName', 'last');
usersToRemove.push(user);
});
done();
}
}
);
});
});
You should place done() inside the called to findOne.
If you're finding that it times out then either findOne is never calling its callback (which is an error!) or it's taking too long to execute.
In this case you could up the timeout by sticking something like this.timeout(5000) at the beginning of the test (which increases the timeout to 5 seconds).
In general you wouldn't usually want tests that slow though, so maybe try and figure out why it takes so long.

Resources