I am now using the below cloud code to only update "downloads" column on my parse server running on AWS EC2 instance. But I am getting the error code 141(invalid function)
Parse.Cloud.define("updateDownloads", async (request) => {
const query = new Parse.Query(request.params.className);
query.get(request.params.objectId)
.then((watchFace) => {
downloads = watchFace.get("downloads")
watchFace.set("downloads", downloads + 1);
await watchFace.save(null, { useMasterKey: true });
return "download updated";
}, (error) => {
return "something went wrong";
});
});
I have place my code in /opt/bitnami/cloud/main.js.
I even tried adding “cloud”: “/opt/bitnami/cloud/main.js” in config.json file but then the parse server gives 503 Service Unavailable error. So I removed it.
If you don't add the cloud code main.js file to your parse server configuration, parse server will never find your function, and that's why you get the invalid function error.
If you get error when adding the file, you are either adding it in a wrong way (you need to check your parse server initialization code) or the config.json is in wrong format or the cloud code has a problem.
The best way to figure it out is by checking your logs.
At a first glance, a problem that I see (may have others) is the usage of await in a function that is not async. You are also using a combination of async and then, which is little strange.
I'd recommend you to change the code to something like:
Parse.Cloud.define("updateDownloads", async (request) => {
const query = new Parse.Query(request.params.className);
const watchFace = await query.get(request.params.objectId);
const downloads = watchFace.get("downloads");
watchFace.set("downloads", downloads + 1); // You can use inc function to avoid concurrency problem
await watchFace.save(null, { useMasterKey: true });
return "download updated";
});
Related
I followed the documentation on pub/sub notifications with the push method here
And I want to have authentication on my call with JWT. I looked at their GitHub example here
app.post('/pubsub/authenticated-push', jsonBodyParser, async (req, res) => {
// Verify that the request originates from the application.
if (req.query.token !== PUBSUB_VERIFICATION_TOKEN) {
res.status(400).send('Invalid request');
return;
}
// Verify that the push request originates from Cloud Pub/Sub.
try {
// Get the Cloud Pub/Sub-generated JWT in the "Authorization" header.
const bearer = req.header('Authorization');
const [, token] = bearer.match(/Bearer (.*)/);
tokens.push(token);
// Verify and decode the JWT.
// Note: For high volume push requests, it would save some network
// overhead if you verify the tokens offline by decoding them using
// Google's Public Cert; caching already seen tokens works best when
// a large volume of messages have prompted a single push server to
// handle them, in which case they would all share the same token for
// a limited time window.
const ticket = await authClient.verifyIdToken({
idToken: token,
audience: 'example.com',
});
const claim = ticket.getPayload();
claims.push(claim);
} catch (e) {
res.status(400).send('Invalid token');
return;
}
// The message is a unicode string encoded in base64.
const message = Buffer.from(req.body.message.data, 'base64').toString(
'utf-8'
);
messages.push(message);
res.status(200).send();
});
But I have some questions.
What is the PUBSUB_VERIFICATION_TOKEN and how do I get it and store it in my environment?
const [, token] = bearer?.match(/Bearer (.*)/); throws the following error
Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)
Why do they push the claims and tokens in an array if they never check that array in this function for already existing tokens / claims?
I am trying to implement this with a Firebase Cloud Function and this is what I have. Is it even possible to cache the tokens / claims?
//Service account auth client
const authClient = new google.auth.JWT({
email: android_key.client_email,
key: android_key.private_key,
scopes: ["https://www.googleapis.com/auth/androidpublisher"]
});
export const handlePubSub = functions.region('europe-west1').https.onRequest(async (req, res) => {
// What is PUBSUB_VERIFICATION_TOKEN???
if (req.query.token !== PUBSUB_VERIFICATION_TOKEN) {
res.status(400).send('Invalid request');
return;
}
try {
const bearer = req.header('Authorization');
const [, token] = bearer?.match(/Bearer (.*)/); //Error Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)
tokens.push(token); // Why do this? Can I do this in firebase cloud functions
const ticket = await authClient.verifyIdToken({
idToken: token,
});
const claim = ticket.getPayload();
claims.push(claim); // Why do this? Can I do this in firebase cloud functions
} catch (e) {
res.status(400).send('Invalid token');
return;
}
const message = Buffer.from(req.body.message.data, 'base64').toString(
'utf-8'
);
console.log(message);
return res.status(200).json({
statusCode: 200,
method: req.method,
message: 'Recieved successfully'
});
});
What is the PUBSUB_VERIFICATION_TOKEN and how do I get it and store it
in my environment?
PUBSUB_VERIFICATION_TOKEN can be any value you want. Easiest way to set an environment variable is on the command line when running node:
PUBSUB_VERIFICATION_TOKEN=whatevertoken node app.js
The req.query.token that is compared too comes from the URL query string.
GET /whatever?token=whatevertoken
Type 'RegExpMatchArray | null | undefined' must have a
'Symbol.iterator' method that returns an iterator.ts(2488)
That's a bug in their code. bearer.match can return undefined/null which can't be spread into the array [, token]. The example will only work when there is a successful regex match. This will parse in plain javascript but typescript highlights this issue at compile time.
const bearer = req.header('Authorization');
const m = /Bearer (.*)/.exec(bearer)
if (m) tokens.push(m[1])
Why do they push the claims and tokens in an array if they never check
that array in this function for already existing tokens / claims?
The example comments // List of all messages received by this instance.
So more a debug store than something functional.
Update At Bottom
I am trying to build a signup page in my Android app that signs users up for a subscription through Stripe. What I am stuck on is adding a payment source from Android, through a cloud function, and receive a token from Stripe.
I currently have solved, automatically adding a newly created User to Stripe. As well creating the subscription when (/users/{userId}/membership/token) is written to, or changed.
On Android I am able to obtain the credit card data through the input..
PaymentMethodCreateParams.Card card = cardInputWidget.getPaymentMethodCard();
I next need to submit this to my cloud function by using..
mFunctions = FirebaseFunctions.getInstance();
mFunctions.getHttpsCallable("addPaymentSource")
.call()
.addOnCompleteListener(task -> {
...
Being I am having trouble finding information on this, here is all I have for this cloud function (Javascript)
exports.addPaymentSource = functions.https.onCall((data, context) =>{
const pm = await stripe.paymentMethods.attach('pm_678', {customer: 'cus_123'});
return admin.firestore().collection('users').doc(user.uid).get('membership').set({token: token});
}
I need to obtain the customer number which is saved at - /users/{user.uid}/customerId'. As well pass the payment method through my http data call, and pass/obtain the user_id (which would have been created long before all this).
I got this far watching this youtube video and converting my code over. Subscription Payments with Stripe, Angular, and Firebase
I also referenced Stripe's Cloud Function examples quite a bit. The one issue is everyone seems to be using this code (below), which doesn't work in my implementation. With most guides/examples not being used for Subscriptions.
// Add a payment source (card) for a user by writing a stripe payment source token to Cloud Firestore
exports.addPaymentSource = functions.firestore.document('/stripe_customers/{userId}/tokens/{pushId}').onCreate(async (snap, context) => {
const source = snap.data();
const token = source.token;
if (source === null){
return null;
}
try {
const snapshot = await admin.firestore().collection('stripe_customers').doc(context.params.userId).get();
const customer = snapshot.data().customer_id;
const response = await stripe.customers.createSource(customer, {source: token});
return admin.firestore().collection('stripe_customers').doc(context.params.userId).collection("sources").doc(response.fingerprint).set(response, {merge: true});
} catch (error) {
await snap.ref.set({'error':userFacingMessage(error)},{merge:true});
return reportError(error, {user: context.params.userId});
}
});
Update:
Made some small changes to get try and get this to work..
exports.addPaymentSource = functions.https.onCall((data, context) =>{
///users/{userId}/membership/token
// Create Payment Method
const paymentMethod = stripe.paymentMethods.create(
{
type: 'card',
card: {
number: '4242424242424242',
exp_month: 5,
exp_year: 2021,
cvc: '314',
},
}).then(pm => {
console.log('paymentMethod: ', paymentMethod.id);
return stripe.paymentMethods.attach(paymentMethod.id, { customer: 'cus_HCQNxmI5CSlIV5' })
.then(pm => {
return admin.firestore().collection('users').doc(user.uid).get('membership').set({token: pm.id});
});
});
});
I am getting close, the problem is paymentMethod.id is 'undefined'
While I'm not a Firebase expert, on your Android side, you want to call your cloud function with parameters of the Customer ID and PaymentMethod ID in order to pass them to your cloud function.
Passing parameters shown here: https://stackoverflow.com/a/56298213/10654456
Then in your cloud function, you want to attach the PaymentMethod to the Customer (as you are doing using stripe-node) and make it the Customer's default for Subscriptions, as shown here: https://stripe.com/docs/billing/subscriptions/payment#signup-3
Then, you should create a Subscription on the Customer for a particular Plan, again using stripe-node, as shown here https://stripe.com/docs/billing/subscriptions/payment#signup-4
Here I have my functioning code. (I used some placeholder data to fill in the variables)
exports.addPaymentSource = functions.https.onCall((data, context) =>{
// Create Payment Method
stripe.paymentMethods.create( {
type: 'card',
card: {
number: '4242424242424242',
exp_month: 5,
exp_year: 2021,
cvc: '314',
},
})
.then(pm => {
return stripe.paymentMethods.attach(pm.id, { customer: 'cus_HCCNMAAwRhNM3c' })
})
.then(pm => {
console.log('final step');
console.log('paymentMethod: ', pm.id);
admin.firestore().collection('users').doc('LzgbQBtk0QSZi7QISIbV').set({token: pm.id});
return admin.firestore().collection('users').doc(user.uid).collection('membership').set({token: pm.id});
})
.catch(error => { return null });
});
So I manually pasted in some variables to confirm my features were functioning. The CustomerID and card details need to be passed in from the Android app. These card details are the only ones I should need for a subscription
'pm' is the returned Payment Method object, in which id is the variable that needs to be attached to the user.
Finally pm.id is the token that must be saved inside into the firestore. Doing this triggers my subscription setup cloud function(not displayed).
The code displayed shows how to avoid nested then statements, and Android firestore direct function calling. While also not shown, the data field can call upon any variable's key word "data.cardnbr".
The method avoids any use of SetupIntents. While this is incredibly effective for Subscription based charging, it might not be best practice for direct charges.
So yesterday I was developing some sort of offline functionality. Therefore, I added an ApiService that returns Observables.
Currently, I fetch my access_token for jwt-Authentication and then use this token to generate Headers for my API-Request. After a successful request, I save the result to my storage. This works fine. Now here is the problem I want to check for an unsuccessful request (e.g. servers are down, app is offline) and then return my stored result from storage. But I can't get it to work.
Here is my code:
getJobs(): Observable<any> {
this.auth.checkToken()
return from(this.storage.get(ACCESS_TOKEN)).pipe(
switchMap(token => {
let options = this.auth.addToken(token)
return this.http.get(API_URL + "jobs", options)
}),
map(res => {
if (res) {
this.storage.set(JOBS, res)
return res
} else {
return from(this.storage.get(JOBS))
}
}),
catchError(() => {
return from(this.storage.get(JOBS))
})
)
}
Further investigations have shown that after the server or the app is offline neither the map() nor the catchError() functions were executed.
UPDATE:
The solution provided by DJ House is correct. My Code works perfectly in my browser but if I build my app with ionic cordova build android it gets stuck after the this.http.get(...) So it's clearly and issue with cordova
SOLUTION:
Wow! Something magical happened! I've found out that the catchError method gets called BUT after almost 2 Minutes, which is way to slow... So I will implement a timeout.
Thanks
flixoflax
The main issue that you may be facing is you are using the map incorrectly. Map acts upon a normal value (usually, its not an observable) and returns a new value. map() should always return the same type of value. In your map() you are either return the response (which I am assuming is of type Jobs) OR you are return an Observable<Jobs>. This will cause your subscribers to need verbose logic to handle that.
It looks like you are trying to use that map() to set your local storage with the returned jobs from your api. I would recommend using tap() since you aren't trying to change the value you are returning.
function getJobs(): Observable<any> {
this.auth.checkToken()
return from(this.storage.get(ACCESS_TOKEN)).pipe(
switchMap(token => {
let options = this.auth.addToken(token)
return this.http.get(API_URL + "jobs", options)
}),
// change to tap()
tap(res => {
if (res) {
this.storage.set(JOBS, res)
}
}),
catchError(() => {
return from(this.storage.get(JOBS))
})
)
}
If the switchMap throws an error, the tap will be skipped. That will ensure you only set storage if you recieve a value from the API. If you always want to set the storage (even if the API threw an error) then move the tap() to be after the catchError().
Can you please try moving the catchError operator as first operator inside pipe method. This is to ensure that you catch error as soon as you recieve it from observable. Please change it like below:
getJobs(): Observable<any> {
this.auth.checkToken()
return from(this.storage.get(ACCESS_TOKEN)).pipe(
switchMap(token => {
let options = this.auth.addToken(token)
return this.http.get(API_URL + "jobs", options)
}),
catchError(() => {
return from(this.storage.get(JOBS))
})
map(res => {
if (res) {
this.storage.set(JOBS, res)
return res
} else {
return from(this.storage.get(JOBS))
}
}),
)
}
In my Flutter mobile app while loading profile image of users through NetworkImage(), I am getting 403 status code in response.
How can I handle this by displaying an Image from my assets folder in case of 403 status code or if image URL is broken etc.
Currently I've handled it by sending a HTTP GET request to the image URL and checking if the status code is 200. If it is 200 I use the NetworkImage() or else I use AssetImage() to load the image and use a FutureBuilder() to build the Widget.
While this works perfectly, I feel this a lot of trouble for handling such a small scenario and it can be done in a better way that I am unaware of.
What is the best way to handle such scenarios?
Please try below approach. Here, If image is available, It will load network image else it will redirect to the error callback. In error callback you can return the widget you want. In your case, you want to load from asset so you can use it like as below. If you want to check error status code, you need to parse the exception that I have print.
Image.network('https://www.outbrain.com/techblog/wp-content/uploads/2017/05/road-sign-361513_960_720.jpg',
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
print("Exception >> ${exception.toString()}");
return Image.asset('assets/images/lake.jpg');
},
)
You did it simple for this scenario, I didnt see any trouble:
if (response.statusCode == 200) {
return NetworkImage();
} else {
return AssetImage();
}
I achieved this by using precacheImage. This will precache your image to flutter cache. It also has a onError function where you can handle errors while caching the image.
String validatedUrl;
precacheImage(
NetworkImage(urlToCheck),
context,
onError: (
exception,
stacktrace,
) {
print(exception.toString());
setState(() {
validatedUrl = '';
});
},
).whenComplete(() {
if (validatedUrl == null) {
setState(() {
validatedUrl = urlToCheck;
});
}
});
Then validatedUrl is either null, empty or carries the validated url.
null -> not validated yet
empty -> error while downloading image
url -> successfully downloaded image
I'm trying to implement a user registration system, on android with node as my backend server.
I'm using Node 4.4.5, on localhost, and using the package "email-verification" - https://www.npmjs.com/package/email-verification
So on request from android, a confirmation email with a verification link is sent, which is working fine.
When the link is clicked, a GET request is made, which confirms the user, adds it to the MongoDB database, and a JSON response is sent.
An email is sent to the user that the account is confirmed.
After sending the confirmation email, the server crashes.
Here's my code--
router.get('/email-verification/:URL', function(req, res, next){
var url = req.params.URL;
console.log('email-verify-start');
nev.confirmTempUser(url, function(err, user) {
console.log('error is :' + err);
if (user) {
nev.sendConfirmationEmail(user.email, function(err, info) {
if (err) {
console.log('sending_conf_email_failed');
return res.json({'email': 'sending_conf_email_failed'});
}
console.log('user_confirmed');
res.json({
'email': 'user_confirmed'
});
console.log('Done, and confirmed');
});
} else {
console.log('conf_temp_ser_failed');
return res.json({'email': 'conf_temp_ser_failed'});
}
});
});
And here's my log--
error is :null
user_confirmed
Done, and confirmed
GET /register/email-verification/SfC9VlnUv91RkFBHDURIbHodnYme0RdfbTYBj0I4oXyywrpW 200 5177.724 ms - 26
h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\src\smtp-transport.js:136
return callback(null, info);
^
TypeError: callback is not a function
at h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\src\smtp-transport.js:136:20
at h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:279:20
at SMTPConnection._actionStream (h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:966:16)
at SMTPConnection.<anonymous> (h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:594:14)
at SMTPConnection._processResponse (h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:516:16)
at SMTPConnection._onData (h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:353:10)
at emitOne (events.js:77:13)
at TLSSocket.emit (events.js:169:7)
at readableAddChunk (_stream_readable.js:153:18)
at TLSSocket.Readable.push (_stream_readable.js:111:10)
at TLSWrap.onread (net.js:531:20)
Process finished with exit code 1
Till the server crashes, everything's working fine. I receive all emails and responses are sent properly, I even see the JSON response {"email":"user_confirmed"} on my browser. The only problem is that the server crashes afterwards.
EDIT 1
I tried adding return statements-- Still the same problem. I added them here--
return res.json({
'email': 'user_confirmed'
});
I also tried adding a return--
res.json({
'email': 'user_confirmed'
});
return;
No luck till now...
EDIT 2
Ok. so this is actually an open issue on GitHUB, this is reported as a bug.
https://github.com/whitef0x0/node-email-verification/issues/44
So, I tried the GitHUB the solution this way and it is now working flawlessly, even though an official fix is not released...
In the source folder of the module, in the file 'index.js' -->
Go to line 340 --
You'll see this line
callback = options.shouldSendConfirmation;
Change it to -->
callback = function(){};
Hope this helps...
You could change your nev.sendConfirmationEmail method to include the callback as the third argument:
nev.sendConfirmationEmail(user.email, function(err, info) {
if (err) {
console.log('sending_conf_email_failed');
return res.json({'email': 'sending_conf_email_failed'});
}
console.log('user_confirmed');
res.json({
'email': 'user_confirmed'
});
console.log('Done, and confirmed');
}, function(){});