Can we use async/await in cloud functions in firebase? - javascript

I have to functions called: getMatchDataApi() and saveApiDataToDb(). getMatchDataApi() function returns value from an api and saveApiDataToDb() function is used to store getMatchDataApi() value into firestore database.
function getMatchDataApi() {
var options = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/scorecards.php?seriesid=2141&matchid=43431",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
json: true
};
var req = http.request(options, res => {
var chunks = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
playerName = json.fullScorecardAwards.manOfTheMatchName;
console.log("player name", playerName);
});
});
req.end();
}
async function saveApiDataToDb() {
await getMatchDataApi();
var name = playerName;
console.log("Aman Singh", name);
}
Here i am using async function. So that first i want it should execute this getMatchDataApi() first and returns the value and after that it should print value inside this function saveApiDataToDb().
And then i am calling saveApiDataToDb() as follow:
exports.storeMatchData = functions.https.onRequest((request, response) => {
saveApiDataToDb()
});

Yes, you can use async/await in cloud functions. But, you can't access/fetch the data outside the google servers in the Spark Plan (Free Plan).
Hope this helps.
Modify your functions/index.js file like this way:
const functions = require('firebase-functions');
const request = require('request');
exports.storeMatchData = functions.https.onRequest( async (req, res) => {
let body = '';
await getMatchDataApi().then(data => body = data).catch(err => res.status(400).end(err));
if (!body) {
return res.status(404).end('Unable to fetch the app data :/');
}
// let json = JSON.parse(body);
// playerName = json.fullScorecardAwards.manOfTheMatchName;
// console.log("Aman Singh", playerName);
res.send(body);
});
function getMatchDataApi() {
const options = {
url: 'https://dev132-cricket-live-scores-v1.p.rapidapi.com/scorecards.php?seriesid=2141&matchid=43431',
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
};
return cURL(options);
}
function cURL(obj, output = 'body') {
return new Promise((resolve, reject) => {
request(obj, (error, response, body) => {
if (error)
reject(error);
else if (response.statusCode != 200)
reject(`cURL Error: ${response.statusCode} ${response.statusMessage}`);
else if (response.headers['content-type'].match(/json/i) && output == 'body')
resolve(JSON.parse(body));
else if (output == 'body')
resolve(body);
else
resolve(response);
});
});
}

I try to solve my issue using promise in cloud functions. so it could help someone.
This is my cloud function
exports.storeMatchData = functions.https.onRequest((request, response) => {
a().then(
result => {
saveApiDataToDb(result);
},
error => {}
);
});
This is the function from which i am calling api and resolving its data first what i want
var options = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/scorecards.php?seriesid=2141&matchid=43431",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
json: true
};
var options1 = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/matches.php?completedlimit=5&inprogresslimit=5&upcomingLimit=5",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
}
};
var a = function getMatchDataApi() {
// Return new promise
return new Promise((resolve, reject) => {
// Do async job
let firstTask = new Promise((resolve, reject) => {
var req = http.request(options, res => {
var chunks = [];
var arr = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
const playerName = json.fullScorecardAwards.manOfTheMatchName;
resolve(playerName);
});
});
req.end();
});
let secondTask = new Promise((resolve, reject) => {
var req = http.request(options1, res => {
var chunks = [];
var arr = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
const playerName = json;
resolve(playerName);
});
});
req.end();
});
Promise.all([firstTask, secondTask]).then(
result => {
resolve(result);
},
error => {
reject(error);
}
);
});
};
This is the function in which I am going to use getMatchDataApi() values after resolving in this function.
function saveApiDataToDb(data) {
console.log("Name of player", data[0]);
}

Related

auth.getAccessToken() is not a function - Azure AD PowerBI + NodeJS integration

Still am able to get the accessToken successfully but don't understand why I'm getting auth.getAccessToken is not a function
index.js
$.ajax({
type: "GET",
url: "/getSingRpt",
dataType: "json",
success: function (embedData) {
let reportLoadConfig = {
type: "report",
tokenType: models.TokenType.Embed,
accessToken: embedData.accessToken,
embedUrl: embedData.embedUrl[0].embedUrl
};
tokenExpiry = embedData.expiry;
let report = powerbi.embed(reportContainer, reportLoadConfig);
report.off("loaded");
report.on("loaded", function () {
console.log("Report load successful");
});
report.off("rendered");
report.on("rendered", function () {
console.log("Report render successful");
});
report.off("error");
report.on("error", function (event) {
let errorMsg = event.detail;
console.error(errorMsg);
return;
});
},
error: function (err) {
let errorContainer = $(".error-container");
$(".embed-container").hide();
errorContainer.show();
let errMsg = JSON.parse(err.responseText)['error'];
let errorLines = errMsg.split("\r\n");
let errHeader = document.createElement("p");
let strong = document.createElement("strong");
let node = document.createTextNode("Error Details:");
let errContainer = errorContainer.get(0);
strong.appendChild(node);
errHeader.appendChild(strong);
errContainer.appendChild(errHeader);
errorLines.forEach(element => {
let errorContent = document.createElement("p");
let node = document.createTextNode(element);
errorContent.appendChild(node);
errContainer.appendChild(errorContent);
});
}
});
Server.js
app.get('/getSingRpt', async (req, res) => {
try {
await embedToken.getEmbedParamsForSingleReport().then((result) => {
console.log(result);
res.status(200).send({ success: true, data: result });
})
} catch (e) {
console.error(e);
res.status(400).send({ success: false, message: 'problem in getting report' });
}
});
embedSConfigService.js
async function getEmbedParamsForSingleReport(workspaceId, reportId, additionalDatasetId) {
const reportInGroupApi = `https://api.powerbi.com/v1.0/myorg/groups/${workspaceId}/reports/${reportId}`;
const headers = await getRequestHeader();
const result = await fetch(reportInGroupApi, {
method: 'GET',
headers: headers,
})
console.log('result', result);
if (!result.ok) {
throw result;
}
const resultJson = await result.json();
const reportDetails = new PowerBiReportDetails(resultJson.id, resultJson.name, resultJson.embedUrl);
const reportEmbedConfig = new EmbedConfig();
reportEmbedConfig.reportsDetail = [reportDetails];
let datasetIds = [resultJson.datasetId];
if (additionalDatasetId) {
datasetIds.push(additionalDatasetId);
}
reportEmbedConfig.embedToken = await getEmbedTokenForSingleReportSingleWorkspace(reportId, datasetIds, workspaceId);
return reportEmbedConfig;
}
async function getRequestHeader() {
let tokenResponse;
let errorResponse;
try {
tokenResponse = await auth.getAccessToken();
} catch (err) {
if (err.hasOwnProperty('error_description') && err.hasOwnProperty('error')) {
errorResponse = err.error_description;
} else {
errorResponse = err.toString();
}
return {
'status': 401,
'error': errorResponse
};
}
const token = tokenResponse;
console.log('TOKEN==>', tokenResponse)
return {
'Content-Type': "application/json",
'Authorization': utils.getAuthHeader(token)
};
}
Auth.js
const adal = require('adal-node');
const config = require(__dirname + '/../config/config.json');
const getAccessToken = () => {
return new Promise((resolve, reject) => {
try {
const authMode = config.authenticationMode.toLowerCase();
const AuthenticationContext = adal.AuthenticationContext;
let authorityUrl = config.authorityUri;
if (authMode === 'masteruser') {
new AuthenticationContext(
authorityUrl,
).acquireTokenWithUsernamePassword(
config.scope,
config.pbiUsername,
config.pbiPassword,
config.clientId,
(err, token) => {
if (err) reject(err);
resolve(token);
},
);
} else if (authMode === 'serviceprincipal') {
authorityUrl = authorityUrl.replace('common', config.tenantId);
new AuthenticationContext(
authorityUrl,
).acquireTokenWithClientCredentials(
config.scope,
config.clientId,
config.clientSecret,
(err, token) => {
if (err) reject(err);
resolve(token);
},
);
} else {
reject(new Error('Unknown auth mode'));
}
} catch (err) {
console.error(err);
reject(err);
}
});
};
getAccessToken()
.then((token) => console.log(token))
.catch((err) => console.error(err));
updated
utilities.js
let config = require(__dirname + "/../config/config.json");
function getAuthHeader(accessToken) {
// Function to append Bearer against the Access Token
return "Bearer ".concat(accessToken);
}

How do I add a body to a POST request in JavaScript using a promise?

I have the following function that works fine for a GET request:
function httpGet(options) {
return new Promise(((resolve, reject) => {
const request = https.request(options, (response) => {
response.setEncoding('utf8');
let returnData = '';
response.on('data', (chunk) => {
returnData += chunk;
});
response.on('end', () => {
resolve(JSON.parse(returnData));
});
response.on('error', (error) => {
reject(error);
});
});
request.end();
}));
}
Using the following options this works fine:
let path = `/1/indexes/combined-faq-directions?query=${location}&typoTolerance=false&getRankingInfo=true&optionalWords=${location}`;
var options = {
host: 'YQRKWVXBWV-1.algolianet.com',
port: 443,
path: path,
method: 'GET',
headers: {
"X-Algolia-API-Key": "APIKEYXXXX",
"X-Algolia-Application-ID": "IDXXX"
}
};
But I'm now trying to do a POST to a different URL using the same format. Through postman I can see this works fine but I can't seem to get it into the correct format for my promise.
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'POST',
'hostname': 'api.airtable.com',
'path': '/v0/appasoP3zaNL9k8Pj/Analytics',
'headers': {
'Authorization': 'Bearer keyxxxx',
'Content-Type': 'application/json',
'Cookie': 'brw=brw9wuY1b9RmtTl1q'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
var postData = JSON.stringify({"records":[{"fields":{"RoomNo":"Room 6"}}]});
req.write(postData);
req.end();
I have tried this to adjust it but getting an error ERR_STREAM_WHITE_AFTER_END
let path = `v0/appasoP3zaNL9k8Pj/Analytics`;
var options = {
host: "api.airtable.com",
port: 443,
path: path,
method: "POST",
headers: {
Authorization: "Bearer keyxxx",
"Content-Type": "application/json",
},
};
var postData = JSON.stringify({ records: [{ fields: { RoomNo: "Room 6" } }] });
async function asyncCall() {
const response = await httpGet(options, postData);
console.log(response);
}
asyncCall();
function httpGet(options, postData) {
return new Promise((resolve, reject) => {
const request = https.request(options, (response) => {
response.setEncoding("utf8");
let returnData = "";
response.on("data", (chunk) => {
returnData += chunk;
});
response.on("end", () => {
resolve(JSON.parse(returnData));
});
response.on("error", (error) => {
reject(error);
});
if (postData) {
request.write(postData);
}
});
request.end();
});
}
I think I'm very close but just can't figure out the syntax. Any help greatly appreciated!

In azure functions (js) POST request after async/await call does not work

I am currently working with azure functions in javascript. In my function, I am first getting a specific element from my CosmoDB (this is the async/await part). I get a result and then I want to do an https POST request. However, my problem is, that it never finished the HTTPs request and I don't really know why. What am I doing wrong?
(As you can see I tried 2 different ways of doing the request, once with the standard https function and the commented out the part with npm request package. However, both ways won't work).
Here is my code:
const CosmosClient = require('#azure/cosmos').CosmosClient;
var https = require('https');
var http = require('http');
var request = require('request');
const endpoint = "someEndpoint";
const masterKey = "anymasterkey";
const database = {
"id": "Database"
};
const container = {
"id": "Container1"
};
const databaseId = database.id;
const containerId = container.id;
const client = new CosmosClient({
endpoint: endpoint,
auth: {
masterKey: masterKey
}
});
module.exports = function (context, req) {
const country = "de";
const bban = 12345678;
const querySpec = {
query: "SELECT * FROM Container1 f WHERE f.country = #country AND f.bban = #bban",
parameters: [{
name: "#country",
value: country
},
{
name: "#bban",
value: bban
}
]
};
getContainers(querySpec).then((results) => {
const result = results[0];
context.log('here before request');
var options = {
host: 'example.com',
port: '80',
path: '/test',
method: 'POST'
};
// Set up the request
var req = http.request(options, (res) => {
var body = "";
context.log('request');
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
context.res = body;
context.done();
});
}).on("error", (error) => {
context.log('error');
context.res = {
status: 500,
body: error
};
context.done();
});
req.end();
// request({
// baseUrl: 'someURL',
// port: 443,
// uri: 'someuri',
// method: 'POST',
// headers: {
// 'Content-Type': 'text/xml;charset=UTF-8',
// 'SOAPAction': 'someaction'
// },
// function (error, response, body) {
// context.log('inside request')
// if (error) {
// context.log('error', error);
// } else {
// context.log('response');
// }
// }
// })
})
};
async function getContainers(querySpec) {
const {container, database} = await init();
return new Promise(async (resolve, reject) => {
const {
result: results
} = await container.items.query(querySpec).toArray();
resolve(results);
})
}
async function init() {
const {
database
} = await client.databases.createIfNotExists({
id: databaseId
});
const {
container
} = await database.containers.createIfNotExists({
id: containerId
});
return {
database,
container
};
}
The last thing that happens is the print of "here before request". After that the function just does nothing until it timesout. So what am I doing wrong? Can't I just this combination of await/async and requests?
As commented you are not sending any data to the POST call. You need to have a req.write before the req.end
req.write(data);
req.end();
That is why the POST call is failing for you. After this fix, it should work

How do I return the contents of this function to the client side?

I have a function that I call using
fetch(http://localhost:8888/.netlify/functions/token-hider?
stateName=' +stateName)
on my client side.
the token-hider function looks like this:
const qs = require("qs");
const fetch = require("node-fetch");
var alertEndpoint = "";
var parkEndpoint = "";
var parksWithAlerts = "";
exports.handler = async function getURLS(event, context, callback)
{
// Get env var values defined in our Netlify site UI
const {api_key, alert_api_url, park_api_url} = process.env;
var stateName =event.queryStringParameters.stateName;
alertEndpoint = `${alert_api_url}${stateName}${api_key}`;
parkEndpoint = `${park_api_url}${stateName}${api_key}`;
getData();
async function getData(alertsArea, alertHeader) {
const [getAlertData, getParkData] = await
Promise.all([fetch(alertEndpoint), fetch(parkEndpoint)] );
var alertResults = await getAlertData.json();
var parkResults= await getParkData.json();
var alertData = alertResults.data;
var parkData = parkResults.data;
parksWithAlerts = parkData.map(park => {
park.alertData = alertData.filter(alert => alert.parkCode ===
park.parkCode);
return park
});
console.log(parksWithAlerts);
}
console.log(callback);
};
how could I return the contents of parksWithAlerts back to the client side after this function is finished?
Try to learn more about callback functions in Javascript.
It is right there in your code, the callback that you are printing is actually suppose to be called after you have executed your code and you can do like this callback(parksWithAlerts);.
While calling the function getURLS you will provide the function which is suppose to get called with args.
Examples : https://www.geeksforgeeks.org/javascript-callbacks/
Here is an example with error handling and returning a response type of JSON
token-hider
import fetch from "node-fetch";
// Get env var values defined in our Netlify site UI
const {api_key, alert_api_url, park_api_url} = process.env;
async function getJson(response) {
return await response.json();
}
const alertEndpoint = stateName => {
return new Promise(function(resolve, reject) {
fetch(`${alert_api_url}${stateName}${api_key}`)
.then(response => {
if (!response.ok) { // NOT res.status >= 200 && res.status < 300
return reject({ statusCode: response.status, body: response.statusText });
}
return resolve(getJson(response))
})
.catch(err => {
console.log('alertEndpoint invocation error:', err); // output to netlify function log
reject({ statusCode: 500, body: err.message });
})
});
}
const parkEndpoint = stateName => {
return new Promise(function(resolve, reject) {
fetch(`${park_api_url}${stateName}${api_key}`)
.then(response => {
if (!response.ok) { // NOT res.status >= 200 && res.status < 300
return reject({ statusCode: response.status, body: response.statusText });
}
return resolve(getJson(response))
})
.catch(err => {
console.log('parkEndpoint invocation error:', err); // output to netlify function log
reject({ statusCode: 500, body: err.message });
})
})
}
exports.handler = function(event, context) {
const stateName = event.queryStringParameters.stateName;
return Promise.all([alertEndpoint(stateName), parkEndpoint(stateName)])
.then(values => {
const [alertData, parkData] = values;
const parksWithAlerts = parkData.map(park => {
park.alertData = alertData.filter(alert => alert.parkCode === park.parkCode);
return park;
});
return {
statusCode: 200,
headers: { 'content-type': 'application/json' },
body: JSON.stringify(parksWithAlerts)
};
})
.catch(error => {
return error;
});
};
NOTE: If you are trying to hide the token, make sure to not deploy this from a public repository on Netlify.
Also, this code has not been tested 100%, so there may be some things to resolve. The response layout and structure is something I use in a few of my lambda functions on Netlify.

How to fetch the image files from a server and zip it in sailsjs

I want to zip all the images from s3 urls. I am doing it on server side on sailsjs framework.
I tried using axios to download the images and used 'zipdir'. The images are getting downloaded in temp folder. But its not getting zipped properly.
this.downloadFiles = function (req, res) {
var resObj = {}
async.waterfall([
this.createFolder.bind(undefined, req),
this.downloadFilesAxios.bind(undefined, req),
this.zipTheFiles.bind(undefined, req)
], function final(err, result) {
if (err) {
console.log('SOME ERROR', err);
resObj.statusCode = err.statusCode || 500;
} else {
resObj.statusCode = 200;
resObj.result = result.questionList;
}
console.log('------', resObj.statusCode)
resObj.messageKey = sails.config.statusCode[resObj.statusCode].key;
resObj.message = sails.config.statusCode[resObj.statusCode].message;
return res.send(resObj);
});
};
}
this.downloadFilesAxios = function (req, obj, callback) {
SurveyDocs.find({ surveyId: req.body.surveyId })
.exec(function (err, docsDetails) {
async.map(docsDetails, function (img, cb) {
const url = img.docS3Url;
let imageName = img.docFileName;
const path = Path.resolve(__dirname, "temp", imageName);
const writer = Fs.createWriteStream(path)
Axios({
method: 'get',
url: url,
responseType: 'stream'
})
.then(function (response) {
response.data.pipe(writer)
})
writer.on('finish', (done) => {
console.log('success!!!');
cb(null, null)
});
writer.on('error', (err) => {
console.log('failed!!!');
cb(err, null)
});
}, (err, data) => {
if (err) {
console.log('errrr', err);
}
callback(null, obj);
});
})
};
this.zipTheFiles = function (req, obj, callback) {
var surveyId = req.body.surveyId;
var tempDir = 'assets/zip/' + surveyId + '.zip'
zipdir('temp', { saveTo: tempDir }, function (err, buffer) {
callback(null, obj);
});
callback(null, obj);
}
Here I am getting a corrupt zip file. Please suggest the solution.
I tried out your example there are a few things you need to consider in order to make it work.
const async = require('async');
const fs = require('fs');
const path = require('path');
const zipDir = require('zip-dir');
const axios = require('axios');
let writer;
async.waterfall([
createFolder,
downLoadFileAxios,
zip
], function (err, result) {
if (err) {
console.log(err);
} else {
console.log('result :', result);
}
});
let's assume this method creates the temp folder
function createFolder(callback) {
setTimeout(function() {
callback(null, 'temp');
}, 1000);
}
Here the writeStream object and it's events should be put inside the then block. So that it writes the stream to the file correctly.
Another important thing here is you are not having a cath block attached the promise, so if any exception occurs it will be simply eaten up.
function downLoadFileAxios(dirPath, callback) {
// Hard coded the images url for the sake of simplicity
let files = [
'https://free-images.com/lg/be5e/climbing_helmets_climbing_equipment.jpg',
'https://free-images.com/lg/87ce/lilac_lilac_bush_lilac.jpg'
];
async.mapSeries(files, function(img, cb) {
let name = img.slice(img.lastIndexOf('/') + 1);
let imagePath = path.resolve(__dirname, "newDir", name);
writer = fs.createWriteStream(imagePath);
axios({
method: 'get',
url: img,
responseType: 'stream'
}).
then(function(response) {
response.data.pipe(writer);
writer.on('finish', (done) => {
console.log('success!!!');
cb(null, null)
});
writer.on('error', (err) => {
console.log('failed!!!');
cb(err, null)
});
})
.catch((err) => {
console.log(err);
})
}, function(err, result) {
if (err) {
console.log('errrr', err);
}
callback(null, 'done downloading');
})
}
function zip (dirPath, callback) {
let zipPath = path.resolve(__dirname, "assets", "file.zip");
// console.log(`got directory path : ${dirPath}`);
zipDir("newDir", {
saveTo: zipPath
}, function(err, buffer) {
if(err) {
callback(err, null);
} else {
callback(null, 'done');
}
});
}
This can be easily done using Async/Await like following.
const async = require('async');
const fs = require('fs');
const path = require('path');
const zipDir = require('zip-dir');
const axios = require('axios');
var writer;
// faking the directory creation part
async function createFolder(callback) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, 2000);
});
}
//Executes in the specified order.
(async () => {
await createFolder();
await downLoadFile();
await zipTheFile();
})();
async function downLoadFile() {
let files = [
'https://free-images.com/lg/be5e/climbing_helmets_climbing_equipment.jpg',
'https://free-images.com/lg/87ce/lilac_lilac_bush_lilac.jpg'
];
for(let i= 0; i<files.length; i++) {
await downLoadFileAxios(files[i]);
}
}
async function downLoadFileAxios(url) {
let name = url.slice(url.lastIndexOf('/') + 1);
let imagePath = path.resolve(__dirname, "newDir", name);
let writer = fs.createWriteStream(imagePath);
const response = await axios({
url,
method: 'GET',
responseType: 'stream'
})
response.data.pipe(writer)
return new Promise((resolve, reject) => {
writer.on('finish', resolve)
writer.on('error', reject)
})
}
function zipTheFile () {
let zipPath = path.resolve(__dirname, "assets", "file.zip");
return new Promise((resolve, reject) => {
zipDir("newDir", {
saveTo: zipPath
}, function(err, buffer) {
if(err) {
return reject(err);
}
return resolve('done');
});
})
}
Hope this helps!.

Categories