I have a firebase database that I wish to create a cloud function that triggers when adding a child node to the parent node , which should call a url with the parameters of the child node added in the parent node.
The URL which would be called is a NodeJS Express app hosted in Google App Engine.
How do I do that, if it is even possible?
You can use the node.js request library to do so.
Since, inside your Cloud Function, you must return a Promise when performing asynchronous tasks, you will need to use an interface wrapper for request, like request-promise.
You could do something along these lines:
.....
var rp = require('request-promise');
.....
exports.yourCloudFucntion = functions.database.ref('/parent/{childId}')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const createdData = snapshot.val();
var options = {
url: 'https://.......',
method: 'POST',
body: ....
json: true // Automatically stringifies the body to JSON
};
return rp(options);
});
If you want to pass parameters to the HTTP(S) service/endpoint you are calling, you can do it through the body of the request, like:
.....
const createdData = snapshot.val();
var options = {
url: 'https://.......',
method: 'POST',
body: {
some: createdData.someFieldName
},
json: true // Automatically stringifies the body to JSON
};
.....
or through some query string key-value pairs, like:
.....
const createdData = snapshot.val();
const queryStringObject = {
some: createdData.someFieldName,
another: createdData.anotherFieldName
};
var options = {
url: 'https://.......',
method: 'POST',
qs: queryStringObject
};
.....
Related
I trying to write node function to call third party API . I using the angular Fire Function for display the results in angular project. The issues is no data response;
Here is my node js code.
const request = require('request');
const UserDetail = () => {
const options ={
url: 'https://www.reddit.com/r/funny.json',
method: 'GET',
headers: {
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
'User-Agent': 'my-reddit-client'
}
}
request(options, function(err, res, body) {
let json = JSON.parse(body);
console.log(json);
});
}
UserDetail();
Here is my firebase function code:
exports.userdetails = functions.https.onRequest(require('./api/user/userdetail'));
Here is my angular service calling firebase function code:
callUserDetails(){
const details = this.functions.httpsCallable('userdetails')({ text: 'Some Request Data' })
.pipe()
.subscribe(resp => {
console.log({ resp });
}, err => {
console.error({ err });
});
}
You are mixing up Callable Cloud Functions and HTTPS Cloud Functions.
By doing
exports.userdetails = functions.https.onRequest(...)
you define an HTTPS Cloud Function,
but by doing
this.functions.httpsCallable('userdetails')({ text: 'Some Request Data' })
in your front-end, you actually call a Callable Cloud Function.
You should either change your Cloud Function to a Callable one, or call the userdetails HTTPS Cloud Function by sending an HTTP Request to the Cloud Function URL.
I would advise the first approach because Callable brings several advantages over a "simmple" HTTPS one (see the doc).
In addition you need to note that request supports callback interfaces natively but does not return a Promise. And it is necessary to use Promises in order to manage the life cycle of a Callable Cloud Function (see the official video serie).
I would use Axios along the following lines (untested):
exports.userdetails = functions.https.onCall(async (data, context) => {
try {
const options = {
url: 'https://www.reddit.com/r/funny.json',
method: 'get',
headers: {
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
'User-Agent': 'my-reddit-client'
}
}
const axiosResponse = await axios(options);
// Build the resp to be sent to the frontend by
// using axiosResponse.data .... up to you, see https://github.com/axios/axios#response-schema
return { resp: .... }
} catch (error) {
// See https://firebase.google.com/docs/functions/callable#handle_errors
}
});
Maybe you should call angular service like this:
// provider class
constructor(private http: HttpClient) {}
this.http.get(url, {params: {}, headers: {}}).subscribe(result => {
//// result
})
I'm using the Github Contents API to update a .json file within a repo, but it doesn't seem to work as expected. This is a client-side (browser) request.
Basically, I have a json file and I would like to add a new JSON object to it. The structure of the object is
id: ''
isEnabled: true
With the Contents API I am able to create the commit but it sort of looks like this -
This is the code that is responsible for creating and pushing the commit object
let updatedContent = utf8.decode(base64.encode(modifiedContent));
console.log(updatedContent);
// const commit = await fetch(
// `${GITHUB_HOST}/repos/${OWNER}/${REPO}/contents/src/components.json`,
// {
// method: 'PUT',
// headers: AuthService.getAuthServiceInstance().withGithubAuthHeaders({
// 'Content-Type': 'application/json',
// }),
// body: JSON.stringify({
// path: 'src/components.json',
// content: updatedContent,
// sha,
// message,
// branch: activeBranch,
// }),
// }
// );
I'm not sure what I'm doing incorrect in this case.
Assuming modifiedContent is a valid JSON Object:
For NodeJS:
const updatedContent = Buffer.from(JSON.stringify(modifiedContent), "utf8").toString("base64")
For Browser:
const updatedContent = btoa(JSON.stringify(content))
Then proceed to construct your request as you've shown above.
To use the API you've mentioned, you're code would look something like:
const originalContent = await getFromOrigin(...);
const modifiedContent = await getUpdatedContentFromEditor(...);
const updatedContent = btoa(modifiedContent);
Edit 1: Added the browser and NodeJS variants.
Edit 2: Added more context.
I am using node js for testing apis. In 1 API i need to upload a file. I am not able to attach file in the payload. I am able to do it in postman but not do it with nodejs. I am using request-promise for managing requests. How can i go about it?
Thanks.
Slight modification in the #rustam's answer,
return request({
method: 'POST',
uri: 'http://localhost:8333',
formData ={
'file': fileStream,
}
});
If you just need to specify the file in payload, then please look at this invented example:
const fs = require('fs');
const request = require('request-promise')
function sendFile() {
const readStream = fs.createReadStream('./test.txt');
return request({
method: 'POST',
uri: 'http://localhost:8333',
body: readStream
});
}
async function main() {
const response = await sendFile();
console.log({ response });
}
main();
In this example, I hard-coded the file name and url, but for you this may be function parameters.
I need to add an file attachment to a soap request from node.js application.
I am able to send request with node-soap library, and now I need to add a file to the request.
I did it with a java client or with soapUI, but I have to do it in node.js, maybe it's possible to do that overriding default request object ?
I didn't find a solution with node-soap, I had to build manually my soap request :
var soapHeader = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><ws:method>';
var soapFooter = '</ws:method></soapenv:Body></soapenv:Envelope>';
function sendSoapRequestWithAttachments(soap,files){
var soapRequest = jsonToXml.buildObject(mail);
var finalSoapRequest = soapHeader + soapRequest + soapFooter;
var multipartMail = [];
// Add soap request
multipartMail.push({
'Content-Type': 'text/xml; charset=utf-8',
body: finalSoapRequest
});
// Add attachments
if (files) {
files.forEach(function (file) {
multipartMail.push({
'Content-Id': '<' + file.uuid + '>',
'Content-Type': 'application/octet-stream',
'Content-Transfer-Encoding': 'binary',
body: fs.createReadStream(file.path)
});
});
}
var options = {
uri: URL,
method: 'POST',
multipart: multipartMail
};
request.post(options, function (error, response) {
...
}
}
I found a way to send attachment using node-soap using base64 encoding
here is an example
import soap from 'soap'
import fs from 'fs'
async function main(){
const filepath = '/path/to/attachment/file.extension'
const client = await soap.createClientAsync(/* ... */)
const result = await client.AnApiMethodAsync({
expectedKeyForTheFile: await fs.readFileAsync(filepath, 'base64')
})
}
main()
I have to POST to an API that someone else has developed in order to obtain an authorization code, and as I have to do it in several different contexts I thought I'd move the code for handling the POST and getting the response to a service.
The frustrating thing at the moment is that I seem to be getting back the value that I want from the API, but can't return it from the server to the calling sails controller.
Here's the service source code:
module.exports = {
getVerifyCode: function(uuid, ip_addr) {
console.log (uuid);
console.log (ip_addr);
var http = require('http'),
querystring = require('querystring'),
// data to send
send_data = querystring.stringify({
uuid : uuid,
ip_addr : ip_addr
}),
// options for posting to api
options = {
host: sails.config.api.host,
port: sails.config.api.port,
path: '/verifyCode/update',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(send_data)
}
},
json_output = "",
// post request
post_req = http.request(options, function(post_res) {
post_res.on('data', function(chunk) {
json_output += chunk;
});
post_res.on('end', function() {
var json_data = JSON.parse(json_output),
verify_code = json_data.verify_code;
console.log("code="+verify_code);
return ('vc='+verify_code);
});
});
post_req.write(send_data);
post_req.end();
}
}
And here's two relevant lines from my controller action:
var vc = verify.getVerifyCode(req.session.uuid, req.connection.remoteAddress);
console.log('vc='+vc);
The weird thing is that the controller console log gets written before the service one does:
vc=undefined
code=YoAr3ofWRIFGpoi4cRRehP3eH+MHYo3EogvDNcrNDTc=
Any ideas? I have a much simpler service running (just some string manipulation stuff); I have a feeling the issue here relates to the asynchronous nature of the API request and response.
Jasper, your correct in your assumption that it is the " asynchronous nature of the API request and response".
When you execute your http call in your verify service, node makes that call and them moves on to the rest of the code console.log('vc='+vc); and does not wait for the http call to finish.
I'm not sure what your end result should be but you can rewrite your controller / service to include the callback (this is just one options, there are many ways to do this of course, other people should suggest others)
verify.js
getVerifyCode: function(uuid, ip_addr, cb) {
// Bunch of stuff
return post_req = http.request(options, cb(post_res});
}
then in your controller
controller.js
verify.getVerifyCode(req.session.uuid, req.connection.remoteAddress, function(resps){
// code in here to execute when http call is finished
})