Zapier POST with error "body used already for" - fetch-api

Basically following the code example verbatim, but trying to make a POST request with fetch
fetch('YOUR URL HERE', { method: 'POST', 'body': content })
.then(function (res) {
console.log(res.text());
return res.text();
})
.then(function (plain) {
var output = { okay: true, raw: plain };
callback(null, output);
})
.catch(callback);
Results in angry red box with:
We had trouble sending your test through. Please try again. Error:
Error: body used already for: https://YOURURLHERE
Not sure what it means?

...and then I realized it's not a Zapier question, but a fetch issue.
Google says it's because of what I'm doing in the promise, by trying to read the res stream as text twice (note the two res.text() calls).
(for about 2 minutes I had omitted what I thought was an unimportant detail from the question, the console.log line which was the culprit)

Related

Fetch Post request not working in Custom functions Office Addin [TypeError: Network request failed]

I having been facing this error in custom functions excel Add-in, where I'm trying to call an external service inside a custom function. It works fine for a GET request such as this:
function stockPrice(ticker) {
var url = "https://api.iextrading.com/1.0/stock/" + ticker + "/price";
return fetch(url)
.then(function(response) {
return response.text();
})
.then(function(text) {
return parseFloat(text);
});
}
CustomFunctionMappings.STOCKPRICE = stockPrice;
Taken from https://learn.microsoft.com/en-us/office/dev/add-ins/excel/excel-tutorial-custom-functions#create-a-custom-function-that-requests-data-from-the-web
But gives an exception for a POST request like this:
function stockPrice(ticker) {
var url = "https://westcentralus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment";
return fetch(url, {
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': key,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(body))
.then(function(response) {
return response.json();
})
.then(function(response) {
return response.somevalue;
})
.catch(e => {
console.error("Caught exception");
return JSON.stringify(e);
});
}
The above is just a sample to have an idea, of how I'm calling my service. I have tried it with 2-3 different services, and I figured out that after running fetch, the code goes to catch block, and the error value that is returned in the excel is an empty object '{}'. Since there are no ways to debug custom functions on windows, and since there is no specific error description, I'm unable to figure out the issue. I have also added my service domain to App Domain list in manifest file but still no effect.
I am not sure that particular API accepts POST requests, so you maybe running into that.
Debugging in Windows is still being worked on but you can use Excel online and F12tools to debug.
If you are on Windows, you can console.log statements in conjunction with the Runtime logging:
https://learn.microsoft.com/en-us/office/dev/add-ins/excel/custom-functions-best-practices#troubleshooting
Hope that helps and we will update this when debugging is ready on for custom functions on windows desktop.

set status code before returning in loopback

I have inherited some very messy loopback code and I want to know if it is possible to return a status code other than 200.
For example I have the following code:
PensionsUser.loginView = function (ctx, credentials, cb) {
var respJSON = util.getresponseTemplate();
credentials.email = credentials.email.toLowerCase().trim();
PensionsUser.login(credentials, 'user', function (err, loginResp) {
if (err) {
util.addErrorToResponse(err, respJSON, 'NO-PUS-LV-001');
app.log.error(util.loggingTemplate('PensionsUser', 'loginView', 'NO-PUS-LV-001', credentials.email));
ctx.res.status = 401; //does not work
cb(null, respJSON);
//etc.
I know cb(null, respJSON) should be returning the error like this cb(respJSON) but sadly the frontend code relies on this JSON being returned as currently is so my first step would be to just change the status code.
Is this possible?
You can set custom error like {error:500, message: "Custom Error"} or you can configure "strong-error-handler" in middleware config file.
For error you can set status like this:
callback({status: 401, message: 'Your error message'});
And for success status codes you someone already answered here: https://stackoverflow.com/a/26942431/1827886
To send response code according to your own requirements, you can use this code:
"YourModelName"."YourRemoteMethod" = function("YourAcceptArguments", callback) {
var error = new Error("New password and confirmation do not match");
error.status = "Enter the resposne code that you want to send";
// Example => error.status = 400
return callback(error);
};

Configuring Restivus POST method for Meteor

I have the following Restivus configuration:
if(Meteor.isServer){
Restivus.configure({
});
//Allow Restivus to manage Reports
Restivus.addCollection('reports');
Restivus.addRoute('newReport/:message', {}, {
// POST
post: {
action: function(){
var response = null;
var message = this.urlParams.message;
if(message){
console.log("Message received: " + message);
return {status: "success", data: message};
} else {
console.log("Message empty...");
return {status: "fail", message: "Post not found"};
}
//Response to caller
return;
}
}
})
}
Following the explanation of Restivus, when I make a GET call to http://localhost:3000/api/newReport/ I should get a "Get All" result from the server, on the caller.
However, if I use curl -X GET http://localhost:3000/api/newReport/ on the command line, I seem to be getting the HTML code of the site at api/NewReport/ (which is empty, except for the header and empty body)
Knowing that, I know my error is in the Restivus Route configuration, but I cannot pinpoint the reason.
The expected behavior is that when I make a POST from a Ruby script, I should get a returned message (Ok or Fail), and in my Meteor console, I should see either "Message received" or "Post not found" (both placeholders).
Additional question, is there a way to disable the default GET method Restivus creates when we add a collection?
You have to create a variable in the JavaScript part and use that in the Restivus.addCollection() call.
Reports = Mongo.Collection('reports')
if(Meteor.isServer){
Restivus.configure({
});
//Allow Restivus to manage Reports
Restivus.addCollection(Reports);
...

Prediction request end up with "invalid data" errors

I get errors when posting prediction requests, but when posting the same requests from the online interface (from Google's API reference page) works well.
I have also posted a new issue on the Git repository, but it seems that no-one even looks at these issues. How lame of Google !!
Well I am posting predict request, which look like this:
var parameters = {
auth: jwtClient,
project: googleProjectID,
id: 'interest_classifier',
input: {
csvInstance: ["country united states", "/location/location"]
}
};
return prediction.trainedmodels.predict(parameters, function(err, response) {
if (err) {
logger.info(null, "prediction err ", err);
}
logger.info(null, "response from predict ", response);
return callback(null, response);
});
And I get this:
err { [Error: Input data invalid.]
code: 400,
errors:
[ { domain: 'global',
reason: 'invalidValue',
message: 'Input data invalid.' } ] }
To clarify: my trained model contains a value and two textual features.
Again, when running this from the online tool (client side) it works well, only when I run it from my node.js server, does it fail.
What's up with that? Anyone knows?
Could this be some encoding issue? request headers?
EDIT:
this is how i authenticate :
var jwtClient = new google.auth.JWT('*****#developer.gserviceaccount.com', 'keys/key.pem', null, ['https://www.googleapis.com/auth/prediction']);
jwtClient.authorize(function(err, tokens) {
logger.info(null, "authorizing");
if (err) {
logger.info(null, "error ", err);
} else {
return logger.info(null, "authenticated ", tokens);
}
});
OK,
So I've dug into Google's code and found out an important detail that is not properly documented.
I think this might be relevant to other people using this library, not only the ones who use google predict.
So most requests need to include a project id in the parameters, but in some cases you also need to include post data (like in my case).
I was trying to include this post data directly in the parameters like so :
var parameters = {
auth: jwtClient,
project: googleProjectID,
id: 'interest_classifier',
input: {
csvInstance: ["country united states", "/location/location"]
}
};
Now I found (this is not documented !!) that the whole part which belongs to the post data should be included within a "resource" field, like this:
var parameters = {
auth: jwtClient,
project: googleProjectID,
id: 'interest_classifier',
resource:{
input: {
csvInstance: ["country united states", "/location/location"]
}
}
};
I believe this will be relevant to any request which includes post data.
Cheers,
Ilan

How to get the REST response message in ExtJs 4?

I'm building upon RESTFul Store example of ExtJs 4. I'd like my script to display errors provided by the REST server, when either Add or Delete request fails. I've managed to obtain the success status of a request (see the code below), but how do I reach the message provided with the response?
Store:
var store = Ext.create('Ext.data.Store', {
model: 'Users',
autoLoad: true,
autoSync: true,
proxy: {
type: 'rest',
url: 'test.php',
reader: {
type: 'json',
root: 'data',
model: 'Users'
},
writer: {
type: 'json'
},
afterRequest: function(request, success) {
console.log(success); // either true or false
},
listeners: {
exception: function(proxy, response, options) {
// response contains responseText, which has the message
// but in unparsed Json (see below) - so I think
// there should be a better way to reach it than
// parse it myself
console.log(proxy, response, options);
}
}
}
});
Typical REST response:
"{"success":false,"data":"","message":"VERBOSE ERROR"}"
Perhaps I'm doing it all wrong, so any advice is appreciated.
I assume that your service follows the REST principle and uses HTTP status codes other than 2xx for unsuccessful operations.
However, Ext will not parse the response body for responses that do not return status OK 2xx.
What the exception/response object (that is passed to 'exception' event listeners) does provide in such cases is only the HTTP status message in response.statusText.
Therefore you will have to parse the responseText to JSON yourself. Which is not really a problem since it can be accomplished with a single line.
var data = Ext.decode(response.responseText);
Depending on your coding style you might also want to add some error handling and/or distinguish between 'expected' and 'unexpected' HTTP error status codes. (This is from Ext.data.reader.Json)
getResponseData: function(response) {
try {
var data = Ext.decode(response.responseText);
}
catch (ex) {
Ext.Error.raise({
response: response,
json: response.responseText,
parseError: ex,
msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
});
}
return data;
},
The reason for this behavior is probably because of the REST proxy class not being a first class member in the data package. It is derived from a common base class that also defines the behavior for the standard AJAX (or JsonP) proxy which use HTTP status codes only for communication channel errors. Hence they don't expect any parsable message from the server in such cases.
Server responses indicating application errors are instead expected to be returned with HTTP status OK, and a JSON response as posted in your question (with success:"false" and message:"[your error message]").
Interestingly, a REST server could return a response with a non-2xx status and a response body with a valid JSON response (in Ext terms) and the success property set to 'true'. The exception event would still be fired and the response body not parsed.
This setup doesn't make a lot of sense - I just want to point out the difference between 'success' in terms of HTTP status code compared to the success property in the body (with the first having precedence over the latter).
Update
For a more transparent solution you could extend (or override) Ext.data.proxy.Rest: this will change the success value from false to true and then call the standard processResponse implementation. This will emulate 'standard' Ext behavior and parse the responseText. Of course this will expect a standard JSON response as outlined in your original post with success:"false" (or otherwise fail).
This is untested though, and the if expression should probably be smarter.
Ext.define('Ext.ux.data.proxy.Rest', {
extend: 'Ext.data.proxy.Rest',
processResponse: function(success, operation, request, response, callback, scope){
if(!success && typeof response.responseText === 'string') { // we could do a regex match here
var args = Array.prototype.slice.call(arguments);
args[0] = true;
this.callParent(args);
} else {
this.callParent(arguments);
}
}
})