I'm trying to call an external POST API from my firebase function but it's not working as expected. I'm calling the firebase function from my android app. Following are the code snippets for my function, android call and the error that I'm getting in firebase console:
Firebase Function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const https = require('https');
admin.initializeApp(functions.config().firebase);
exports.generateToken = functions.https.onCall((data, context) => {
const variable = data.variable;
const uid = context.auth.uid;
console.log("Function Trigerred");
if(uid !== null) {
console.log("UID: " + uid);
const requestPayload = JSON.stringify({
"variable": variable
});
console.log(requestPayload);
const config = {
hostname: 'API-HOST',
port: 443,
path: '/API-PATH',
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
console.log(config);
const response = "";
const req = https.request(config, (res) => {
res.on('data', (data) => {
console.log(data);
res.send(data);
});
});
req.on('error', (error) => {
console.log(error);
res.end(error);
});
req.write(requestPayload);
req.end();
} else {
console.log("Invalid User");
res.send("Invalid User");
}
});
Call in App
Map<String, Object> data = new HashMap<>();
data.put("variable", "value");
FirebaseFunctions.getInstance()
.getHttpsCallable("generateToken")
.call(data)
.continueWith(task -> {
String result = (String) task.getResult().getData();
Log.e("1: ", result);
return result;
})
.addOnCompleteListener(task -> {
Log.e("2: ", "Complete");
if (!task.isSuccessful()) {
Exception e = task.getException();
if (e instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
FirebaseFunctionsException.Code code = ffe.getCode();
Object details = ffe.getDetails();
}
Log.e("3: ", task.getException().toString());
}
});
Firebase Console Log
{
Error: getaddrinfo ENOTFOUND API-HOST API-HOST:443 at GetAddrInfoReqWrap.onlookup [as oncomplete (dns.js:67:26)
errno: 'ENOTFOUND',
code: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'API-HOST',
host: 'API-HOST',
port: 443
}
However, when I tried hitting this API from Postman, I got the successful response.
I'm new to Node.js.
Related
I have used dialogflow fulfillment to get data from an external api. It works fine with the test console. But on being deployed on to an android app, it gives a blank response. How do I fix this? Thanks.
The code in fulfillment:
'use strict';
const axios = require('axios');
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
function welcome(agent) {
agent.add(`Welcome to my agent!`);
}
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function rhymingWordHandler(agent){
const word = agent.parameters.word;
agent.add(`Here are the rhyming words for ${word}`);
return axios.get(`https://api.datamuse.com/words?rel_rhy=${word}`)
.then((result) => {
result.data.map(wordObj => {
agent.add(wordObj.word);
});
});
}
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('Rhyme Scheme', rhymingWordHandler);
agent.handleRequest(intentMap);
});
The code in MainActivity.java
public void callback(AIResponse aiResponse) {
if (aiResponse != null) {
// process aiResponse here
String botReply = aiResponse.getResult().getFulfillment().getSpeech();
Log.d(TAG, "Bot Reply: " + botReply);
showTextView(botReply, BOT);
} else {
Log.d(TAG, "Bot Reply: Null");
showTextView("There was some communication issue. Please Try again!", BOT);
}
}
How can i get the information from [object Promise]?
I am using GCF (cloud function) to process square payments.
As of right now I am getting back a response of { response="OK:[object Promise]" }
This is processing the cloud function on the cloud platform:
const functions = require('firebase-functions');
const SquareConnect = require('square-connect');
const crypto = require('crypto');
exports.fxtest = functions.https.onCall((data, context) => {
const defaultClient = SquareConnect.ApiClient.instance;
defaultClient.basePath = "https://connect.squareupsandbox.com";
const oauth2 = defaultClient.authentications['oauth2'];
oauth2.accessToken = 'sandbox-token-ommitted';
const idempotency_key = crypto.randomBytes(23).toString('hex');
const payments_api = new SquareConnect.PaymentsApi();
const item_source = data.source_id;
const item_price = 1.00;
const item_currency = 'USD';
const request_body = {
"idempotency_key": idempotency_key,
"source_id": item_source,
"amount_money": {
"amount": item_price,
"currency": item_currency
}
};
var rsp;
try{
const response = payments_api.createPayment(request_body)
.then(
r=> { return r; })
.catch(
e => { return e; });
const json = JSON.stringify('OK:' + response);
rsp = json;
} catch(error){
return rsp = 'ERROR:' + error;
}
return{
response: rsp
};
});
This is the processing the returned data on an Android device:
private FirebaseFunctions mFunctions;
private Task<HttpsCallableResult> fxtest(String text, Context ctx, CardDetails crds){
Map<String, Object> data = new HashMap<>();
data.put("source_id",crds.getNonce());
return this.mFunctions.getHttpsCallable("fxtest").call(data)
.addOnCompleteListener((Activity) ctx, new OnCompleteListener<HttpsCallableResult>() {
#Override public void onComplete(#NonNull Task<HttpsCallableResult> task) {
Toast.makeText(ctx, "result: " + task.getResult().getData(),Toast.LENGTH_LONG).show();
}
});
}
Some sources i was looking at:
connect-api-example using nodejs on github
square-conect on npm
I've found the solution to the question, basically my "credentials access token was using authorization access token" that was the first mistake, the other was due to the itempotency key which has a max limit of 45 characters according to the API reference square connect api, and the other was how i returned the response which is meant to be in JSON format as far as the promise i consumed was doing. Here is the source code, (the java is fine, no need to edit that) it was only on the nodejs side. The API keys are referenced in the environmental variables side on the GCF platform. This will effectively allow processing a square payment through android applications using "serverless approach".
const functions = require('firebase-functions');
const SquareConnect = require('square-connect');
const crypto = require('crypto');
exports.fxtest = functions.https.onCall(async (data, context) => {
/* testing url for sandbox */
//defaultClient.basePath = process.env.TESTING_SQUARE_CONNECT_URL;
const defaultClient = SquareConnect.ApiClient.instance;
defaultClient.basePath = process.env.PRODUCTION_SQUARE_CONNECT_URL;
const oauth2 = defaultClient.authentications["oauth2"];
oauth2.accessToken = process.env.PRODUCTION_APPLICATION_ACCESS_TOKEN;
const idempotency_key = crypto.randomBytes(16).toString("hex");
const payments_api = new SquareConnect.PaymentsApi() ;
/* value of amount is in cents as of 11/29/2019
, 1 is equal to 1 cent, 100 is equal to 100 cents */
const request_body = {
"idempotency_key": idempotency_key,
"source_id": data.source_id,
"amount_money": {
"amount": 100,
"currency": "USD"
},
};
try{
response = await payments_api.createPayment(request_body)
.then(
r=> {
if(r.ok) { return Promise.resolve(r); }
return Promise.reject(Error("TRY ERROR_ON_RESPONSE: " + JSON.stringify(r)))
})
.catch(
e=> {
return Promise.reject(Error("TRY ERROR_ON_EXCEPTION: " + JSON.stringify(e)))
});
return "TRY OKAY: " + JSON.stringify(response);
} catch(error){
return "CATCH ERROR: " + JSON.stringify(error);
}
});
Just trying to impletment Stripe Payment into my Android App.
The trouble i have is that my cloud function is triggered twice when i enter a credit card in my app. the first trigger returns an "error" status and the second trigger returns an "ok" status
Here is the code i use to save the Stripe token to my firebase realtime database:
if (cardToSave != null) {
stripe.createToken(
cardToSave,
object:TokenCallback {
override fun onSuccess(token: Token?) {
val currentUser = FirebaseAuth.getInstance().currentUser?.uid
val database = FirebaseDatabase.getInstance()
val pushId = database.getReference("stripe_customers/$currentUser/sources/").push().key
val ref = database.getReference("stripe_customers/$currentUser/sources/$pushId/token/")
//save the token id from the "token" object we received from Stripe
ref.setValue(token?.id)
.addOnSuccessListener {
Log.d(TAG, "Added Stripe Token to database successfully")
}
.addOnFailureListener {
Log.d(TAG, "Failed to add Token to database")
}
}
...
Here is the cloud function i copied straight from Stripe's example in their github repo:
// Add a payment source (card) for a user by writing a stripe payment source token to Realtime database
exports.addPaymentSource = functions.database
.ref('/stripe_customers/{userId}/sources/{pushId}/token').onWrite((change, context) => {
const source = change.after.val();
if (source === null){
return null;
}
return admin.database().ref(`/stripe_customers/${context.params.userId}/customer_id`)
.once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.createSource(customer, {source});
}).then((response) => {
return change.after.ref.parent.set(response);
}, (error) => {
return change.after.ref.parent.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: context.params.userId});
});
});
Any help would be appreciated!
EDIT:
index.js
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const logging = require('#google-cloud/logging');
//functions.config() is firebase's environment variables
const stripe = require('stripe')(functions.config().stripe.token);
const currency = functions.config().stripe.currency || 'USD';
// [START chargecustomer]
// Charge the Stripe customer whenever an amount is written to the Realtime database
exports.createStripeCharge = functions.database.ref('/stripe_customers/{userId}/charges/{id}')
.onCreate((snap, context) => {
const val = snap.val();
// Look up the Stripe customer id written in createStripeCustomer
return admin.database().ref(`/stripe_customers/${context.params.userId}/customer_id`)
.once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
// Create a charge using the pushId as the idempotency key
// protecting against double charges
const amount = val.amount;
const idempotencyKey = context.params.id;
const charge = {amount, currency, customer};
if (val.source !== null) {
charge.source = val.source;
}
return stripe.charges.create(charge, {idempotency_key: idempotencyKey});
}).then((response) => {
// If the result is successful, write it back to the database
return snap.ref.set(response);
}).catch((error) => {
// We want to capture errors and render them in a user-friendly way, while
// still logging an exception with StackDriver
return snap.ref.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: context.params.userId});
});
});
// [END chargecustomer]]
// When a user is created, register them with Stripe
exports.createStripeCustomer = functions.auth.user().onCreate((user) => {
return stripe.customers.create({
email: user.email,
}).then((customer) => {
return admin.database().ref(`/stripe_customers/${user.uid}/customer_id`).set(customer.id);
});
});
// Add a payment source (card) for a user by writing a stripe payment source token to Realtime database
exports.addPaymentSource = functions.database
.ref('/stripe_customers/{userId}/sources/{pushId}/token').onWrite((change, context) => {
const source = change.after.val();
if (source === null){
return null;
}
return admin.database().ref(`/stripe_customers/${context.params.userId}/customer_id`)
.once('value').then((snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.createSource(customer, {source:source});
}).then((response) => {
return change.after.ref.parent.set(response);
}, (error) => {
return change.after.ref.parent.child('error').set(userFacingMessage(error));
}).then(() => {
return reportError(error, {user: context.params.userId});
});
});
// When a user deletes their account, clean up after them
exports.cleanupUser = functions.auth.user().onDelete((user) => {
return admin.database().ref(`/stripe_customers/${user.uid}`).once('value').then(
(snapshot) => {
return snapshot.val();
}).then((customer) => {
return stripe.customers.del(customer.customer_id);
}).then(() => {
return admin.database().ref(`/stripe_customers/${user.uid}`).remove();
});
});
// To keep on top of errors, we should raise a verbose error report with Stackdriver rather
// than simply relying on console.error. This will calculate users affected + send you email
// alerts, if you've opted into receiving them.
// [START reporterror]
function reportError(err, context = {}) {
// This is the name of the StackDriver log stream that will receive the log
// entry. This name can be any valid log stream name, but must contain "err"
// in order for the error to be picked up by StackDriver Error Reporting.
const logName = 'errors';
const log = logging.log(logName);
// https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource
const metadata = {
resource: {
type: 'cloud_function',
labels: {function_name: process.env.FUNCTION_NAME},
},
};
// https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent
const errorEvent = {
message: err.stack,
serviceContext: {
service: process.env.FUNCTION_NAME,
resourceType: 'cloud_function',
},
context: context,
};
// Write the error log entry
return new Promise((resolve, reject) => {
log.write(log.entry(metadata, errorEvent), (error) => {
if (error) {
return reject(error);
}
return resolve();
});
});
}
// [END reporterror]
// Sanitize the error message for the user
function userFacingMessage(error) {
return error.type ? error.message : 'An error occurred, developers have been alerted';
}
"Server Error" in nodejs api (android to web server request)
I am a new node.js learner. I'm doing a project that user are registration by android application, and user details will go to web server. but when i send request to web server by API that time show "Server Error Problem". but when i send data by (postman row data) that time it works, but if i send by (postman form-data) that time it not work, and show "Server Error Problem".
In addition, I am using a shared linux Server supported by cpanel and CloudLinux.
-----------------------
index.js file
------------------------
const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const cors = require('cors');
const verifyToken = require('./jwt/verify/verifytoken');
const mysql = require('mysql');
global.config = require('./jwt/config/config');
const extInfo = require('./info/extInfo');
const port = process.env.PORT || 3000;
const connection = mysql.createConnection({
host: "localhost",
user: “abcd”,
password: "123345",
database: “xyz”
});
var point = 0;
const app = express();
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.use(express.json());
app.use(bodyParser.urlencoded({extended : false}));
app.use(cors());
app.use('/api/v1', require('./api/v1'));
------------------------------------
api file (code)
---------------------------
const express = require('express');
const router = express.Router();
const mysql = require('mysql');
var error = ["error", "error", "error"];
const connection = mysql.createConnection({
host: "localhost",
user: “abcd”,
password: “12345”,
database: “xyz”
});
router.get('/login?', (req , res) => {
var studentdata = {
A: req.query.a,
B: req.query.b
}
connection.query(`Select Student_id from student where Mobile = ${a} and Password = ${b}`, (err, result, fields) => {
if (err) {
console.log("error");
res.send(error);
} else if (result.length < 1){
console.log("error");
res.send(err);
}
return res.send(result);
let Student_id = result[0].Student_id ;
connection.query(`SELECT * FROM B where A = ${A}`, (err1, result1, fields1) => {
if(err1){
res.send(err1);
} else if (result1.length < 1){
res.send(error);
}
res.send(result1);
});
});
});
router.post('/registration', (req , res) => {
let newStudent = {
a : req.body['a'],
b: req.body.b,
....
c: req.body.c
}
connection.query(`SELECT * FROM d1 where Mobile = ${a}`, (err, result , fields) => {
if(err) {
return res.send("Server Error");
} else if (result.length > 0) {
return res.send("wrong phone");
}
connection.query(`INSERT INTO d1 (Student_name, School , ..... , Password ) VALUES ('${newStudent.student_name}', ..... , '${newStudent.student_password}')`, (err1, result1, fields1) => {
if(err1) {
return res.send(error);
} else if (result1.length > 0) {
return res.send("wrong phone");
}
let insertId = result1.insertId;
// Create Score table Account
connection.query(`INSERT INTO d2( A ) VALUES ('${insertId}')`, (err2, result2, fields) => {
if (err2) {
return res.send(err2);
}
return res.send("Registration Successful");
});
})
});
});
router.get('*', (req , res) => {
res.send({"info" : "Nothing Found"});
});
module.exports = router ;
I am trying to implement device groups in my node.js file to trigger notifications.
I want to get the token_id and create a notification_key for the device groups for a single user.
but i am getting the error as.
ReferenceError: options is not defined
at Object.<anonymous> (D:\SweExpress\functions\index.js:13:9)
at Module._compile (internal/modules/cjs/loader.js:678:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:689:10)
at Module.load (internal/modules/cjs/loader.js:589:32)
at tryModuleLoad (internal/modules/cjs/loader.js:528:12)
at Function.Module._load (internal/modules/cjs/loader.js:520:3)
at Module.require (internal/modules/cjs/loader.js:626:17)
at require (internal/modules/cjs/helpers.js:20:18)
at C:\Users\user\AppData\Roaming\npm\node_modules\firebase-tools\lib\triggerParser.js:21:11
at Object.<anonymous> (C:\Users\user\AppData\Roaming\npm\node_modules\firebase-tools\lib\triggerParser.js:61:3)
and my index.js file is.
'use-strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
var request = require('request');
admin.initializeApp();
var headers = {
'Authorization': 'key = my service account key here',
'project_id': 'server_id',
'Content-Type': 'application/json'
}
request(options, function (error, response, body) {
console.log(body)
})
exports.sendNotification = functions.firestore.document("Users/ {user_id}/Notifications/{notification_id}").onWrite((change,context) => {
const user_id = context.params.user_id;
const notification_id = context.params.notification_id;
return admin.firestore().collection("Users").doc(user_id).collection("Notifications").doc(notification_id).get().then(queryResult => {
const from_user_id = queryResult.data().from;
const from_message = queryResult.data().message;
const from_data = admin.firestore().collection("Users").doc(from_user_id).get();
const to_data = admin.firestore().collection("Users").doc(user_id).get();
return Promise.all([from_data, to_data]).then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
const token_id = result[1].data().token_id;
var options = {
url: 'https://android.googleapis.com/gcm/notification',
method: 'POST',
headers: headers,
json: {'operation': 'create',
'notification_key_name': 'you#example.com',
'registration_ids': [token_id]}
}
request(options, function (error, response, body) {
console.log(body)
})
var notificationKey = body;
const payload = {
notification: {
title: "Notification From: " + from_name,
body : from_message,
icon : "notification.png",
sound: "orion.mp3"
}
};
return admin.messaging().sendToDeviceGroup(notificationKey,payload).then(result => {
var c = console.log("Notification sent ");
return c;
});
});
});
});
I am new to node.js as well.
Secondly, is my index.js file correct? or I am doing something wrong?.
Without initializing variable named option, your are passing it as a parameter for the request. Thats why you are getting following error ReferenceError: options is not defined. Here I commented the unused part. checkout this snippet now
'use-strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
var request = require('request');
admin.initializeApp();
var headers = {
'Authorization': 'key = my service account key here',
'project_id': 'server_id',
'Content-Type': 'application/json'
}
/* Code which causes error
request(options, function (error, response, body) {
console.log(body)
})*/
exports.sendNotification = functions.firestore.document("Users/ {user_id}/Notifications/{notification_id}").onWrite((change,context) => {
const user_id = context.params.user_id;
const notification_id = context.params.notification_id;
return admin.firestore().collection("Users").doc(user_id).collection("Notifications").doc(notification_id).get().then(queryResult => {
const from_user_id = queryResult.data().from;
const from_message = queryResult.data().message;
const from_data = admin.firestore().collection("Users").doc(from_user_id).get();
const to_data = admin.firestore().collection("Users").doc(user_id).get();
return Promise.all([from_data, to_data]).then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
const token_id = result[1].data().token_id;
var options = {
url: 'https://android.googleapis.com/gcm/notification',
method: 'POST',
headers: headers,
json: {'operation': 'create',
'notification_key_name': 'you#example.com',
'registration_ids': [token_id]}
}
request(options, function (error, response, body) {
console.log(body)
})
var notificationKey = body;
const payload = {
notification: {
title: "Notification From: " + from_name,
body : from_message,
icon : "notification.png",
sound: "orion.mp3"
}
};
return admin.messaging().sendToDeviceGroup(notificationKey,payload).then(result => {
var c = console.log("Notification sent ");
return c;
});
});
});
});
Your options object is defined after its first use:
request(options, function (error, response, body) {
console.log(body)
})
Define
var options = { ...
before it is used in
request(options ...
Hope that helps :)