How to make http request in Apigee JavaScript policy? - apigee

I am using Apigee API proxy and in the PreFlow part of the Proxy endpoint I have to make a http request. I created a JavaScript policy, where I tried using fetch to make the request, but when I call the endpoint, the response is ReferenceError: "fetch" is not defined. Does anybody have any suggestions what could work?

Apigee JavaScript Object Model exposes httpClient object.
More detail can be found in the docs .

I set a Variable to be called later and used the httpClient call like so:
//===================================
//== Set Function to pass response ==
//===================================
function onComplete (response, error){
//== Check if HTTP request was successful ==
//==========================================
if(response){
context.setVariable("responsePayload1", response.content);
}
//== Set Error Variable is it fails ==
//====================================
else {
context.setVariable("example.error", "Whoops: "+error)
}}
//==================================
//== Set Variable to run function ==
//==================================
var calloutResponse = httpClient.get("http://yourwebsitename.com/your-call-uri", onComplete);
By setting the variable it forces the JS to run the httpClient.get command in addition to running the above function.

Related

Unit test Actions on Google Dialogflow locally

I'm trying to unit test a DialogflowApp locally by using the firebase shell environment. (in a cli do firebase experimental:functions:shell and then call my methods)
I have followed this guide by google https://firebase.google.com/docs/functions/local-emulator but they don't use the DialogflowApp where the invoked function tries to bind a request object containing intents and parameters like this ->
exports.myFunction = functions.https.onRequest((request, response) => {
const app = new App({ request, response });
function myMethod(app) {
let myArgument = app.getArgument(MY_ARGUMENT);
app.tell('Here we are responding');
}
let actionMap = new Map();
actionMap.set(MYMETHOD_ACTION, myMethod);
app.handleRequest(actionMap);
});
Regardless of what request object I send in the CLI, like this myFunction(require("../test/testdata.json")), the request body object is empty, like this body: {} which means I can't do app.handleRequest() or app.getArgument(). The error message I get is
RESPONSE RECEIVED FROM FUNCTION: 400, Action Error: no matching intent
handler for: null
I thought that if I populated testdata.json with the json request data shown in Actions on Google -> console.actions.google.com -> Simulator it would be valid data but no.
My question is, how can i mock my request data so that I can start unit testing my fullfillment methods locally?
EDIT 1:
firebase > myMethod.post("/").form(require("../test/testdata.json"))
Sent request to function.
firebase > info: User function triggered, starting execution
info: Function crashed
info: TypeError: Cannot destructure property `parameters` of 'undefined' or 'null'.
if we look in dialogflow_app.js we can see this code for fetching an argument value
getArgument (argName) {
debug('getArgument: argName=%s', argName);
if (!argName) {
error('Invalid argument name');
return null;
}
const { parameters } = this.body_.result;
if (parameters && parameters[argName]) {
return parameters[argName];
}
return this.getArgumentCommon(argName);
}
this.body_ is always just empty {}, regardless of how and what I send into the method when running locally.
EDIT 3
firebase > myMethod({method: "post",json: true, body: require("../test/testdata.json")})
Sent request to function.
firebase > info: User function triggered, starting execution
info: Function crashed
info: TypeError: Cannot destructure property parameters of 'undefined' or 'null'.
Invoking a Firebase HTTPS function using the shell requires a different form. It takes the parameters that the request module does, so in order to emulate a webhook, it will be something like this:
myfunction({
method: 'POST',
json: true,
body: require("../test/testdata.json")
});
These three parameters are important:
You need to specify that this is a POST operation
You need to indicate that the body will be JSON. This will send the correct header and won't try to send the body as x-www-form-urlencoded
You need to include the body. As an object is ok because you've set the json parameter to true.

Meteor Get request

Bear with me for any mistakes/wrong terminology since I am new to all this. I am using meteor to develop my project and i need to make a get request to an external API. (I already added meteor add http) Below is my code:
HTTP.call( 'GET', 'url', {}, function( error, response ) {
if ( error ) {
console.log( error );
} else {
console.log( response );
}
});
If i use the code inside my Client folder in Meteor I get the following error No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access meteor It has something to do with CORS which I didn't understand how to implement. If I use the code above in my Server side I do get the correct response in the console but how do I use it as a var on my client javascript code?
Tou can use .call function of HTTP and pass your header in options:
HTTP.call(method, url, [options], [asyncCallback])
Arguments
method String
The HTTP method to use, such as "GET", "POST", or "HEAD".
url String
The URL to retrieve.
asyncCallback Function
Optional callback. If passed, the method runs asynchronously, instead of synchronously, and calls asyncCallback. On the client, this callback is required.
Options
content String
String to use as the HTTP request body.
data Object
JSON-able object to stringify and use as the HTTP request body. Overwrites content.
query String
Query string to go in the URL. Overwrites any query string in url.
params Object
Dictionary of request parameters to be encoded and placed in the URL (for GETs) or request body (for POSTs). If content or data is specified, params will always be placed in the URL.
auth String
HTTP basic authentication string of the form "username:password"
headers Object
Dictionary of strings, headers to add to the HTTP request.
timeout Number
Maximum time in milliseconds to wait for the request before failing. There is no timeout by default.
followRedirects Boolean
If true, transparently follow HTTP redirects. Cannot be set to false on the client. Default true.
npmRequestOptions Object
On the server, HTTP.call is implemented by using the npm request module. Any options in this object will be passed directly to the request invocation.
beforeSend Function
On the client, this will be called before the request is sent to allow for more direct manipulation of the underlying XMLHttpRequest object, which will be passed as the first argument. If the callback returns false, the request will be not be send.
Souce: Here
Fixed it. On client side
Meteor.call("getURL",'url',{},function(err,res){
if(err){
console.log('Error: '+err);
}
if(!err){
console.log('Response: '+res);
}
and on server
Meteor.methods({
'getURL': function(url_l){
console.log("Request: "+url_l)
return HTTP.get(url_l)
}
});

Arbitrary response content types in Feathers

I have a custom service that must return data in CSV format.
I can't use a standard Express route, because I need Feathers' hooks on this endpoint.
I couldn't find an example of a Feathers service returning non-HTML, non-JSON data, and have found no way to specify a response content type.
Using res.set('Content-Type', 'text/csv') before returning from the service method didn't work; the final Content-Type header was reset to application/json, even though the method's return value was a regular string.
How can I properly set arbitrary response content types in Feathers' custom service methods?
You can customize the response format like this:
const feathers = require('feathers');
const rest = require('feathers-rest');
const app = feathers();
function restFormatter(req, res) {
res.format({
'text/plain': function() {
res.end(`The Message is: "${res.data.text}"`);
}
});
}
app.configure(rest(restFormatter));
The complete documentation can be found here.
Using your own service specific middleware to send the response should also work.

Meteor http calls limitations

Currently, I use the built-in meteor http method (see http://docs.meteor.com/#http) for issuing http calls, on both my client and my server.
However, I'm experiencing two issues:
is it possible to cancel a request?
is it possible to have multiple query parameters which share the same key?
Are these just Meteor limitations, or are there ways to get both to work using Meteor?
I know I could you jquery on the clientside, and there must be a server-side solution which supports both as wel, but I'd prefer sticking with meteor code here.
"is it possible to cancel a request?"
HTTP.call() does not appear to return an object on which we could call something like a stop() method. Perhaps a solution would be to prevent execution of your callback based on a Session variable?
HTTP.call("GET", url, function(error, result) {
if (!Session.get("stopHTTP")) {
// Callback code here
}
});
Then when you reach a point where you want to cancel the request, do this:
Session.set("stopHTTP", true);
On the server, instead of Session perhaps you could use an environment variable?
Note that the HTTP.call() options object does accept a timeout key, so if you're just worried about the request never timing out, you can set this to whatever millisecond integer you want.
"is it possible to have multiple query parameters which share the same key?"
Yes, this appears to be possible. Here's a simple test I used:
Meteor code:
HTTP.call("GET", "http://localhost:1337", {
query: "id=foo&id=bar"
}, function(error, result) {
// ...
});
Separate Node.js server: (just the basic example on the Node.js homepage, with a console.log line to output the request URL with query string)
var http = require('http');
http.createServer(function(req, res) {
console.log(req.url); // Here I log the request URL, with the query string
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
When the Meteor server is run, the Node.js server logged:
/?id=foo&id=bar
Of course, this is only for GET URL query parameters. If you need to do this for POST params, perhaps you could store the separate values as a serialized array string with EJSON.stringify?

Meteor.http.call gives not allowed by Access-Control-Allow-Origin

When I try to call an external server for JSON queries in Meteor with the Meteor.http.call("GET") method I get the error message "not allowed by Access-Control-Allow-Origin".
How do I allow my meteor app to make HTTP calls to other servers?
Right now I run it on localhost.
The code I run is this:
Meteor.http.call("GET",
"http://api.vasttrafik.se/bin/rest.exe/v1/location.name?authKey=XXXX&format=json&jsonpCallback=processJSON&input=kungsportsplatsen",
function(error, result) {
console.log("test");
}
);
There are other questions similar to this on StackOverflow.
You're restricted by the server you're trying to connect to when you do this from the client side (AJAX).
One way to solve it is if you have access to the external server, you can modify the header file to allow some, or all origins by:
Access-Control-Allow-Origin: *
However, if you place the call on the server side and not provide a callback function, the call will be made synchronously, thus not with AJAX, and it should succeed.
Here's
Meteor.methods({checkTwitter: function (userId) {
this.unblock();
var result = Meteor.http.call("GET", "http://api.twitter.com/xyz", {params: {user: userId}});
if (result.statusCode === 200) return true
return false;
}});

Resources