I've been trying to implement just a simple Firebase fetch since November. At this point, I wish I'd just created a new Rails api; it would have been faster.
But everyone insists Firebase is Oh So Simple.
In app.js,
import firebase from 'nativescript-plugin-firebase';
That part seems OK.
Instructions are all over the place after that.
The plugin's ReadMe suggests an initialization:
firebase.init({
// Optionally pass in properties for database, authentication and cloud messaging,
// see their respective docs.
}).then(
function () {
console.log("firebase.init done");
},
function (error) {
console.log("firebase.init error: " + error);
}
);
Several others have insisted that the init code is unnecessary. It does run without errors, but the code he gives after that produces nothing. Also,
const db = firebase.firestore;
const UserStatusCollection = db.collection("UserStatus");
UserStatusCollection.get();
produce an empty object {}.
Here's my Firebase collection:
If I wrap the firebase call in async/await (and no one is showing it as this complicated),
async function getFireStoreData() {
try {
let result = await this.UserStatusCollection.get();
console.log(result);
return result;
}
catch (error) {
console.error(
"UserStatusCollection.get()" + error
);
}
}
And call that
let temp2 = getFireStoreData();
console.log("temp2:" + temp2);
All I ever get is an object promise.
As I said, I wish I had just built up a new Rails API and had a far simpler life since November.
Your getFireStoreData method is asynchronous and you're not awaiting it. That is probably the reason why you're getting a promise back. Try to await getFireStoreData(). See if that works.
Since it's also a promise, you can try to use .then.
getFireStoreData().then(data => {
console.log(data);
})
Related
I understand the issue but can't figure out the workaround. I am querying a specific document to extract an array of token strings. I need to append a new token to the end of this string and then update the current document with this new token array.
To do this, I have subscribed to a query and within, I update that document. But of course, when you update the same object, the subscription runs again thus creating an infinite loop. I tried incorporating a take(1) pipe rxjs operator but that did not change anything. Any suggestions?
Here's my code:
this.afs.collection('users').doc(user.userUID).valueChanges().pipe(take(1)).subscribe((user: userModel) => {
const currentTokens: string[] = user.notifTokens ? user.notifTokens : [];
//token variable is provided outside this query
currentTokens.push(token);
//this next lines causes the subscription to trigger again
userRef.doc(user.userUID).update({notifTokens: currentTokens})
})
I would recommend you avoid using a subscription in this situation, for exactly this reason. I realize the Angularfire2 docs don't list this method, but the base Firebase package includes a .get() method... and while the AF2 docs don't mention the .get() method... the source code shows that it is supported.
Try something like:
this.afs.collection('users').doc(user.userUID).get().then( (user: userModel) => {
if (user.exists) {
console.log("Document data:", user.data());
// Do stuff with the info you get back here
const currentTokens: string[] = user.data().notifTokens ? user.data().notifTokens : [];
currentTokens.push(token);
userRef.doc(user.data().userUID).update({notifTokens: currentTokens})
} else {
// user.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
I have been searching all over on how to implement firebase functions with a flutter application. It does not seem like there is an SDK available (yet). I've also tried adding the gradle dependency implementation 'com.google.firebase:firebase-functions:15.0.0' to my app/build.gradle but this causes build errors.
Has anyone done an implementation that works? I am unable to find any documentation on how to handle credentials and the transport of data in order to build my own firebase functions call.
I have created a rough outline of how I am thinking this is intended to work, but may be way off base.
Future<dynamic> updateProfile(String uid, AccountMasterViewModel avm) async {
Uri uri = Uri.parse(finalizeProfileFunctionURL);
var httpClient = new HttpClient();
String _result = '';
try {
return await httpClient
.postUrl(uri)
.then((HttpClientRequest request) {
return request.close();
// authentication??
// Fields and data??
})
.then((HttpClientResponse response) async {
print(response.transform(new Utf8Codec().decoder).join());
if (response.statusCode == HttpStatus.OK) {
String json = await response.transform(new Utf8Codec().decoder).join();
_result = jsonDecode(json);
// Do some work
return json;
}
else {
return ':\nHttp status ${response.statusCode}';
}
});
}
catch (exception) {
return 'Failed ' + exception.toString();
}
}
I'd like to be able to send an object, like
{
accountID: src.accountID,
accountName: src.name,
accountImg: src.image
}
and then handle the response. But as I said, I can't find any working examples or tutorials on how to do this. It's fairly simple to do this client size and talk directly to the database, however, there are validations and data components that need to be hidden from the client, so cloud functions is the way I would like to do this.
Yes, there is a cloud_function package available here: https://pub.dartlang.org/packages/cloud_function.
so as to make a call to the function you can just call
CloudFunctions.instance.call(
functionName: 'yourCloudFunction',
parameters: <String, dynamic>{
'param1': 'this is just a test',
'param2': 'hi there',
},
);
An updated answer to calling Firebase's Cloud Functions in Flutter would be
var callable = CloudFunctions.instance.getHttpsCallable(functionName: 'functionName'); // replace 'functionName' with the name of your function
dynamic response = callable.call(<String, dynamic>{
'param1': param1 //replace param1 with the name of the parameter in the Cloud Function and the value you want to insert
}).catchError((onError) {
//Handle your error here if the function failed
});
This is a good tutorial on cloud functions in flutter which helped me:
https://rominirani.com/tutorial-flutter-app-powered-by-google-cloud-functions-3eab0df5f957
Cloud functions can be triggered by data change triggers in the realtime database, Firestore, or Datastore, as well as authentication triggers.
You could just persist
{
accountID: src.accountID,
accountName: src.name,
accountImg: src.image
}
to the database and register a trigger that runs a Cloud Function when data at a specific path is inserted, updated, or deleted.
https://firebase.google.com/docs/functions/firestore-events
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;
}
});
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.
I'm using the Firebase node module and trying to convert it's callbacks to thunks to be able to use them in Koa.
This is the original event listener callback as per the Firebase documentation:
projects.on('value', function (snapshot) {
console.log('The read succeeded: ' + snapshot.val());
}, function (errorObject) {
console.log('The read failed: ' + errorObject.code);
});
And this is the where I want to add it in my Koa project:
function *list() {
// Get the data here and set it to the projects var
this.body = yield render('list', { projects: projects });
}
Anyone know how to to do it? Have tried thunkify, thunker and thu without success...
I don't think you can use thunkify etc because they are trying to convert a standard node function to a thunk. The firebase api doesn't follow the standard node.js callback signature of
fn(param1, parm2,.., function(err, result){});
which thunkify is expecting.
I think this would do it
var findProjectsByValue = function(value){
return function(callback){
projects.on(value, function(result){
callback(null, result);
}, function(err){
callback(err);
})
}
};
then you would consume it
var projects = yield findProjectsByValue('value');
Or you could just do rest api calls, which I assume is what you want. The firebase api seems to be more for evented scenarios, socketio etc