I am trying to create a Firebase Scheduled Function that runs every 60 minutes and updates some countries. These countries data is obtained from an external API.
Here is what I have got right now:
const functions = require("firebase-functions");
exports.updateCountries = functions.pubsub.schedule('every 60 minutes').onRun((context) => {
const URL = //build my request URL
const axios = require('axios')
return axios.get(url).then(response => {
return updateCountries(response.data.results)
}).catch(error => {
return console.log("Request Error:", error)
});
}
function updateCountries(results) {
console.log("Updating data!", results)
}
Requesting my URL in the browser or Postman gives the following result:
{
"code": 200,
"results": [
{
"country": "in"
},
//...
}
}
Now, my problem is that it seems that the updateCountries function doesn't receive all data. Making a console log, I can see that the results JSONArray is not complete.
I tried JSON.parse(results) and it throws an unexpected token at line exception. So I am pretty sure that the axios response is not complete.
What I am missing here? How should I request my external API from a Scheduled Function?
It seems you are returning directly from the Axios, this will end the cloud function.
make sure your endpoints are resolved fully before logging and returning your data which is an async result as well.
Related
I'm working with React.js and I have the following problem:
import axios from "axios";
export default function Home() {
const [products, setProducts] = useState([]);
const ax = axios.create({ headers: { Accept: 'application/json' }});
function test() {
const res = ax.get("https://vtexstore.codeby.com.br/api/catalog_system/pub/products/search").then((response) => {
// expected the setProducts to be filled with the return of this request
setProducts(response.data);
});
}
test();
// and when I get here to see if the products have been filled, I get an empty array [ ]
console.log(products);
/*
as the products variable was not filled within the axios promise by setProducts,
there is no way to throw the products array here in the HTML to make a forEach or
a map to look cute together with the tags
*/
return (
<sup>how sad, with the product array empty, I can't put the data here '-'</sup>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
See how the result comes out in the IDE console:
I'm in Visual Studio not knowing what to do, I'm new to ReactJS with NextJS and from an early age I've been trying to see if I could solve this problem, but without success.
What can I do to bring the products to the HTML page?
UPDATE: As per the solution below, I created a possible workaround that indicates a path that could have returned a solution
ax.get("https://vtexstore.codeby.com.br/api/catalog_system/pub/products/search/", {})
.then((response) => setProducts(response.data))
.catch((error) => {
console.log(error); // AxiosError {message: 'Network Error', name: 'AxiosError', ...}
console.log(error.status); // undefined
console.log(error.code); // ERR_NETWORK
});
useEffect(() => {
console.log(products);
}, []);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.0.2/umd/react-dom.production.min.js"></script>
and I'm getting the same error that I put in the comments of the first answer below:
but when I change the setProducts by the console.log to see if it returns the same result, this appears in the terminal where my next.js application is running
that:
ax.get("https://vtexstore.codeby.com.br/api/catalog_system/pub/products/search/", {})
.then((response) => console.log(response.data.length)) // returns the length of the products array
returns this when I update my app:
NOTE: That's why I'm not able to understand my application in Next.js. I'm following all the correct guidelines, writing the code perfectly using axios and when I run the application on the website it gives a network error and doesn't show exactly the amount of products that were displayed in the terminal where my application is running.
I've already configured all the request headers correctly, enabling CORS to allow external requests with other API's, and I still don't succeed in returning the data to my application's page.
Wrap the stuff you have to fetch products inside useEffect hook
useEffect(()=>{
const ax = axios.create({ headers: { Accept: 'application/json' }});
function test() {
const res = ax.get("https://vtexstore.codeby.com.br/api/catalog_system/pub/products/search").then((response) => {
// expected the setProducts to be filled with the return of this request
setProducts(response.data);
console.log(response.data)
});
}
test();
},[])
Then in your return of the component, you can use map on products array with null and undefined checks
Like
{products && products.map(product=>{})}
I have created a url scraper function, working and tested on Google Cloud, but I am really drawing a blank on how to invoke it. I have tried two methods, one using the cloud_functions package, and the other using a standard HTTPS get. I've tried looking online, but none of the solutions/guides involve functions with an input from the Flutter app, and an output back to the app.
Here's the structure of the function (which is working alright). I've named this function Parse in Google Cloud Platform.
<PYTHON PACKAGE IMPORTS>
def Parser(url):
<URL PARSE FUNCTIONS>
return source, datetime, imageurl, keyword
def invoke_parse(request):
request_json = request.get_json(silent=True)
file = Parser(request_json['url'])
return jsonify({
"source": file[0],
"datetime": file[1],
"imageurl": file[2],
"keyword": file[3],
})
The first method I tried was using an HTTP CALL to get the function. But that isn't working, even though there are no errors - I suspect it's just returning nothing.
parser(String url) async{ // Here I honestly don't know where to use the url input within the function
var uri = Uri.parse(<Function URL String>);
HttpClient client;
try {
var request = await client.getUrl(uri);
var response = await request.close();
if (response.statusCode == HttpStatus.ok) {
var json = await response.transform(utf8.decoder).join();
Map data = jsonDecode(json) as Map;
source = data['source']; // These are the variables used in the main Flutter app
postedAt = data['datetime'];
_imageUrl = data['image'];
keyword = data['keyword'];
} else {
print('Error running parse:\nHttp status ${response.statusCode}');
}
} catch (exception) {
print('Failed invoking the parse function.');
}
}
That didn't work, so I thought I might alternatively use the cloud_functions package as follows (in lieu of the previous):
parser(String url) async {
var functionUrl = <FUNCTION URL>;
HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(functionName: 'Parse')
..timeout = const Duration(seconds: 30);
try {
final HttpsCallableResult result = await callable.call(
<String, dynamic>{
'url': url,
}
);
setState(() {
source = result.data['source']; //THESE ARE VARIABLES USED IN THE FLUTTER APP
postedAt = result.data['datetime'];
_imageUrl = result.data['image'];
keyword = result.data['keyword'];
});
}
on CloudFunctionsException catch (e) {
print('caught firebase functions exception');
print(e.code);
print(e.message);
print(e.details);
} catch (e) {
print('caught generic exception');
print(e);
}
}
In the latter case, the code ran without errors but doesn't work. My flutter log states the following error:
I/flutter ( 2821): caught generic exception
I/flutter ( 2821): PlatformException(functionsError, Cloud function failed with exception., {code: NOT_FOUND, details: null, message: NOT_FOUND})
which I'm assuming is also an error at not being able to read the function.
Any help on how I should go about processing my function would be appreciated. Apologies if something is a really obvious solution, but I am not familiar as much with HTTP requests and cloud platforms.
Thanks and cheers.
Node Js Backend Function
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.test = functions.https.onCall(async (data, context) => {
functions.logger.info("Hello logs: ", {structuredData: true});
functions.logger.info( data.token, {structuredData: true});
}
Flutter frontend
1- pubspec.yaml
cloud_functions: ^1.1.2
2 - Code
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('test');
final HttpsCallableResult results = await callable.call<Map>( {
'token': token,
});
I want to send multiple replies for a single user-triggered postback on Messenger. I've been following Messenger's developer documentation and couldn't really find how to do this.
My code structure is very similar to the tutorials they've given on the site, I have a 'handlePostback' function which identifies the received postback and compares it to a set of predefined payloads to find the 'response' JSON object. This response is given to 'callSendAPI' which puts this JSON object into the basic format of sending the message back to the Messenger API.
function handlePostback(sender_psid,receivedPostback)
{ if(payload== 'defined_payload') {
response = {
text: 'Some text'
};
callSendAPI(sender_psid,response);
}
function callSendAPI(sender_psid,response) {
let body = {
recipient: {
id= sender_psid
},
message: response
};
// Followed by code for POST request to the webhook
}
This being the basic structure, now I want to send multiple messages as a reply to one postback. I did some digging, and I found that the solution might be to create a message [] array. But how do I do this? Because my 'response' is being generated through that function, and the messages structure should look like this (I think):
let body = {
recipient: {
id=sender_psid
},
messages: [ {
response1
},
{
response2
}
]
};
I hope I could explain my question, please let me know if I can provide more details!
Nice question. If you are not familiar with Node.js the way to do it is not too obvious and this is not documented well on Facebook's Send API Documentation.
Firstly, your approach of sending multiple messages, using an array, as you may have observed won't work. Facebook has a solution for sending up to 100 API Calls with one request but in my opinion this is not needed in your situation. If you want to find out more about it check out the Batched Request Documentation, you'll find out that the implementation is different than yours.
One solution that will work is to call the callSendAPI function multiple times. But this solution has one major drawback: You won't be able to control the actual sequence of the messages sent. For example if you want to send two separate messages, you cannot guarantee which will be sent first to the user.
To solve this issue you need to chain your callSendAPI functions in a way that guarantees that the next callSendAPI call will happen only after the first message is already sent. You can do this in NodeJS by using either callbacks or promises. If you are not familiar with either of them, you can read this for callbacks and this for promises.
You'll need to modify your callSendAPI function and especially the part that sends the POST request to Facebook. I will present a solution to your issue by using promises and the module node-fetch.
const fetch = require('node-fetch');
function handlePostback(sender_psid,receivedPostback){
if (payload == 'defined_payload') {
response = {
text: 'Some text'
};
response2 = //... Another response
response3 = //... Another response
callSendAPI(sender_psid,response).then(() => {
return callSendAPI(sender_psid, response2).then(() => {
return callSendAPI(sender_psid, response3); // You can add as many calls as you want
});
});
}
}
function callSendAPI(sender_psid,response) {
let body = {
recipient: {
id= sender_psid
},
message: response
};
const qs = 'access_token=' + encodeURIComponent(FB_PAGE_TOKEN); // Here you'll need to add your PAGE TOKEN from Facebook
return fetch('https://graph.facebook.com/me/messages?' + qs, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body),
});
}
I found the below link useful to sort out the way to implement multiple responses on single post back.
https://codingislove.com/build-facebook-chat-bot-javascript/
Like you said, array should work. Create an array variable with multiple response messages
var multipleResponse = {
messages: [{
response1
},
{
response2
}]
};
And push the array variable to your function
function callSendAPI(sender_psid,response) {
let body = {
recipient: {
id= sender_psid
},
message: []
};
// Followed by code for POST request to the webhook
}
Finally push the array to your function array
body.message.push(multipleResponse.messages);
#Christos Panagiotakopoulos. I am not getting my mainMenuResponse which is chained using then. Rather, i am getting response thrice.
handlePostback function =>
// Handles messaging_postbacks events
function handlePostback(sender_psid, received_postback) {
let response;
// Get the payload for the postback
let payload = received_postback.payload;
// Set the response based on the postback payload
if (payload === 'fashionTip') {
response = { "text": getFashionTip() } // calls a function which gives a fashion-tip
// Send the message to acknowledge the postback
callSendAPI(sender_psid, response).then(() => {
return callSendAPI(sender_psid, mainMenuResponse)
});
callSendAPI function =>
// Sends response messages via the Send API
function callSendAPI(sender_psid, response) {
// construct the message body
let request_body = {
"recipient": {
"id": sender_psid
},
"message": response
}
// Send the HTTP request to the messenger platform
request({
"uri": "https://graph.facebook.com/v2.6/me/messages",
"qs": {"access_token": PAGE_ACCESS_TOKEN},
"method": "POST",
"json": request_body
}, (err, res, body) => {
if (!err) {
console.log("Message sent!");
} else {
console.error("Unable to send mesage:" + err);
}
});
}
Don't modify callSendAPI function. In your handlePostback function call callSendAPI multiple times.
callsendAPI(sender_psid,response1);
callsendAPI(sender_psid,response2);
I am trying to convert a CSV data to JSON data while streaming from a HTTP url by using "csvtojson" package.
const csv = require("csvtojson");
const request = require('request');
let options = {
uri: '',
****
};
let tempArr = [];
csv()
.fromStream(request(options))
.on("json", (jsonObj) => {
if (JSON.parse(jsonObj.Response).intents[0].intent == "None")
tempArr.push(JSON.parse(jsonObj.Response));
})
.on('done', (error) => {
callback(null, tempArr)
})
This is calling under an API. When I starts the server and call this api to convert csv to json, it works perfectly.
And If I call the same API again, the "json" event is not getting triggered, Instead "done" event is triggered directly.
i.e., the streaming is not done from the second time. why is it behaving like this?
and what should I do to solve this problem?
In my angular JS app i need to send data to server:
"profile":"OLTP",
"security":"rsh",
"availability":"4",
"performance": {
"TRANSACTION_PER_SEC":1000,
"RESPONSE_TIME":200,
"CONCURRENT_CONNECTION_COUNT":500,
"STORAGE_SIZE":200,
"TOTAL_CONNECTION_COUNT":500
}
and in return i'll get
{"estimate" : 1600,"quoteid" : "Q1234"}
I was trying to do that with $resource but I am lost in syntax.
app.factory("VaaniEstimateService", function($resource) {
var requestURL = "http://128.34.32.34:8080/enquiry";
return $resource(requestURL, {callback: 'JSON_CALLBACK'}, { get: { method:'JSON'}, isArray:false });
});
Can you please provide me something to get me on the right path.
You must use JSONP method and insert JSON_CALLBACK keyword to your url as callback function.
app.factory("VaaniEstimateService", function($resource) {
return $resource("http://128.34.32.34:8080/enquiry?callback=JSON_CALLBACK", {}, {
get: {
method:'JSONP'
},
isArray:false
}).get({
profile:"OLTP",
security:"rsh",
availability:"4",
"performance.TRANSACTION_PER_SEC":1000,
"performance.RESPONSE_TIME":200,
"performance.CONCURRENT_CONNECTION_COUNT":500,
"performance.STORAGE_SIZE":200,
"performance.TOTAL_CONNECTION_COUNT":500
}, function (response) {
console.log('Success, data received!', response);
});
});
Your params will be sent as query params. Angularjs will automatically generate a global function for callback and replace its name with JSON_CALLBACK keyword. Your server must return json as javascript code by calling function that sent with callback parameter. For example, AngularJS is going to make GET request to that url:
http://128.34.32.34:8080/enquiry?callback=angular.callbacks._0?availability=4&performance.CONCURRENT_CONNECTION_COUNT=500&performance.RESPONSE_TIME=200&performance.STORAGE_SIZE=200&performance.TOTAL_CONNECTION_COUNT=500&performance.TRANSACTION_PER_SEC=1000&profile=OLTP&security=rsh
And your server must return response like that:
angular.callbacks._0({"estimate" : 1600,"quoteid" : "Q1234"});
Hope that's enough to give you an idea how jsonp works.