Google Apps Script UrlFetchApp isn't encoding a JSON array correctly - json

I've looked everywhere and can't find this issue. I've come over from PeopleSoft to .NET and have only recently began learning JavaScript and I'm attempting to use Google Apps Script to send email notification messages to Slack.
It appears to me that GAS's UrlFetchApp isn't handling an array correctly. Below I didn't include all the Slack API options for clarity. Here how I constructed the payload, where 'attachments' contains the array in question:
var payload =
{
// ...
"username": "Test webhook Bot",
"attachments": [
{
"pretext": "pre-hello1",
"text": "text-world1"
},
{
"pretext": "pre-hello2",
"text": "text-world2"
}
]
// ...
};
var options =
{
"method" : "post",
"payload" : payload,
"contentType":"application/json"
};
var response = UrlFetchApp.fetch(requestURL, options);
When testing I found that the post was occuring but Slack was ignoring the attachments portion of the message. I used the following to examine the outgoing POST:
var response = UrlFetchApp.getRequest(requestURL, options);
And what I found looking at the execution transcript I find that the JSON array in my payload isn't being encoded the way I expected. Before execution, I clearly see the properly formatted array.
[16-01-26 07:26:39:050 MST] UrlFetchApp.getRequest([https://slack.com/api/chat.postMessage?, {method=post, payload={attachments=[{pretext=pre-hello1, text=text-world1}, {pretext=pre-hello2, text=text-world2}], username=Test webhook Bot}, contentType=application/json}]) [0 seconds]
But what is actually sent, in place of the attachments array is: %5BLjava.lang.Object;#37f01fb3
[16-01-26 07:26:39:051 MST] Logger.log([Test:https://slack.com/api/chat.postMessage?attachments=%5BLjava.lang.Object;#37f01fb3&username=Test+webhook+Bot, []]) [0 seconds]
I tried searching this out as much as I could before asking for help, but I'm not sure if I'm either loss. Does anyone know where I may look to find out what I'm missing? Thanks.

To the extent that this information is helpful almost 4 years out, I've been running into the same problem and here's the solution I came up with:
- I will be including all relevant information encoded in the URL JSON structure
- The "options" portion of the UrlFetchApp is then just specifying the method and contentType
An example would look like this:
var url = "https://slack.com/api/chat.postMessage?token=the-token-here&channel=channel_id_here&text=hello%20world";
var options = {
"method": "post",
"contentType": "application/json",
};
return UrlFetchApp.fetch(url,options);
}
I also got some more helpful information at this Stack Overflow thread.
I think this is the Slack API documentation that helps explain the constraints:
JSON-encoded bodies
For these write methods, you may alternatively send your HTTP POST
data as Content-type: application/json.
There are some ground rules:
You must explicitly set the Content-type HTTP header to
application/json. We won't interpret your POST body as such without
it. You must transmit your token as a bearer token in the
Authorization HTTP header. You cannot send your token as part of the
query string or as an attribute in your posted JSON. Do not mix
arguments between query string, URL-encoded POST body, and JSON
attributes. Choose one approach per request. Providing an explicitly
null value for an attribute will result in whichever default behavior
is assigned to it.

Based on a comment I received from a Google Drive Help Forum discussion , I wanted to pass on more information on what I found regarding the use of JSON.stringify() in creating my Slack request. I modified my options JSON
var options = {
'method': 'post',
'payload': JSON.stringify(payload)
};
Google then interprets the 'attachments' array correctly when constructing the request and I no longer see the java.lang.Object error.
Additional lessons learned: prior to using JSON.stringify() Slack would let me post using my personal developer token as part of the payload. Once I began using JSON.stringify() Slack would not accept my personal token nor could I pass a channel parameter. This resulted in me creating a Slack Incoming Webhook direct to the channel I wanted. I haven't tracked down why that would be the case. It may be in Slack's documentation somewhere, I just haven't had time to look yet.

Related

Why am I getting an empty response back from UrlFetchApp in Google Apps Script?

I am trying to make a GET request to an external API from a Google Apps Script using UrlFetchApp. When I make this request with Postman or curl, I get back the expected response. However, when I try it with UrlFetchApp, I get back an empty response, {}.
I have tried using Basic Auth and OAuth 2, as well as explicitly setting the oauthScopes property in the manifest as described here.
I have confirmed with the API team that they are indeed sending back a full response when I hit the endpoint, but all I receive is {}. My problem seems similar to this StackOverflow question which went unanswered.
var headers = {
"X-Client-Key": "KEY",
"Authorization": "Bearer TOKEN"
};
var options = {
method: "get",
headers: headers,
}
var response = UrlFetchApp.fetch(ENDPOINT, options);
console.log(JSON.stringify(response)); // returns {}
Do not take what you see in logs at face value. fetch method of the UrlFetchApp service always returns an instance of HTTPResponse which is an object first and foremost. This is what the logs show you (I am assuming you are logging the response because this is the only context I am aware of where {} is displayed).
To extract useful information from the response, use the appropriate methods exposed on HTTPResponse instances, like getResponseCode or getContentText.

How to parse HTTP response?

I'm trying to build a script to run in Google Sheets to automatically pull my bank account balance into the sheet. I'm using Plaid to get the account information. I've pulled the information using UrlFetchApp. I'm stuck trying to figure out how to parse the returned text for the available balance information.
I've tried to use an old XML method to parse it, but quite frankly I don't think I'm even close to figuring out this problem.
function myFunction() {
//HTTP Request
var avail_balance = [];
var data = {
"client_id": "5bf874c39bb5dc0012b1be13",
"secret" : "Redacted",
"access_token" : "Redacted"
};
var payload = JSON.stringify(data);
var options = {
"method" : "POST",
"contentType" : "application/json",
"payload" : payload
};
var url = "https://development.plaid.com/accounts/balance/get";
var response = UrlFetchApp.fetch(url, options);
//Parse HTML
//return avail_balance
return avail_balance
}
The docs for Plaid API say this under API protocols
The Plaid API uses POST requests to communicate and HTTP response
codes to indicate status and errors. All responses come in standard
JSON
Plaid API documentation
You should only use XmlService to parse XML, not JSON. JSON is a standard notation for objects, not just in JavaScript but in many other languages as well so the name is a bit confusing.
Calling UrlFetchApp.fetch() will return an HttpResponse object that you can then parse to JSON (if, in fact, it's a valid JSON - if not, try logging the response first via Logger.log(response))
var responseString = resonse.getContentText();
var data = JSON.parse(responseString);
UrlFetchApp methods
Checked the documentation for the Plaid service and it appears that the response payload is formatted as JSON. So I'm not sure why you'd need to parse the response as HTML.
Try using JSON.parse() on the response instead and then referencing the appropriate property as detailed in the documentation.

Can't reply in the Slack thread making http post request with JSON

I want to reply in a slack thread via Slack API.
I have an object.
var msg = {
text: 'test',
thread_ts: '1513168789.000263' // it's right ts, I have checked
}
I make the request
request.post({
method: 'post',
body: msg,
json: true,
url: '<incoming slack webhook>'
})
I expect to get the message to be replied in a thread, but it's just posted in a channel.
You need to use chat.postMessage if you want to send messages to a thread. The incoming webhook will not work (or at least there is no information in the documentation that it would support it).
chat.postMessage has a property called thread_ts, which you can use to address the correct thread.
See also this answer about webhooks and threads.

Not able to capture previous API response and append the response in URL of Post Request API

I am trying to POST API request where I am getting API response as
"d": {
"__metadata": {
"uri": "http://ev-qa02.zs.local/IncentiveManager/0002i1/wcf/v5.svc/InDataRequestCreators('9f31c6da-ec56-4360-8589-d21b6320f99b')",
"type": "ZSAssociates.Javelin.ETL.Rest.v5.InDataRequestCreator"
},
"ScenarioId": "9f31c6da-ec56-4360-8589-d21b6320f99b",
"CallbackUrl": "",
"DataExpiresOnUtc": "/Date(4103913600000)/",
"CreateScenarioIfMissing": false,
"AdapterId": "0fcbd8d2-f5cb-4e2a-bda8-bb37037b022d",
"InDataRequestIdOut": "eb36f8a9-5b7d-4835-88f6-4af67830c1e9",
"InDataRequestUrlOut": "/InDataRequests('eb36f8a9-5b7d-4835-88f6-4af67830c1e9')"
}
}
Now I am trying to hit another API request where my URL would be kind of
http://ev-qa02.zs.local/IncentiveManager/0002i1/WCF/V5.svc/InDataRequests('eb36f8a9-5b7d-4835-88f6-4af67830c1e9')/FileCreator
*InDataRequests('eb36f8a9-5b7d-4835-88f6-4af67830c1e9') This random number is generated from above response value "InDataRequestIdOut"
How can I append the URL taking previous API response and adding in my 2nd POST request.
I am not able to capture my response and used it in other API POST request? i would realy appreciate if you can help me here,been stuck in this issue since couple of days,I went through doc and examples too but couldn't resolve this.I have attached screenshot too.PostUrlFailureScreenshot
My main problem is line number 26 and 27 from eclipe screenshot
Scenario: Verify that JIM Idr request ofr Post
Given header Content-Type = 'Application/JSON'
And header Accept = 'Application/JSON'
And header Authorization = 'Basic
UUEwMl9JbmNlbnRpdmVNYW5hZ2VyXzAwMDJpMTpZWkxaRjlGclR1eWhlcVNJbXlkTlBR'
Given path 'InDataRequestCreators'
* def user =
"""
{
"ScenarioId":"9f31c6da-ec56-4360-8589-d21b6320f99b",
"AdapterId":"0fcbd8d2-f5cb-4e2a-bda8-bb37037b022d",
"DataExpiresOnUtc":"2100-01-18T00:00:00",
"CreateScenarioIfMissing":"false"
}
"""
And request user
When method post
Then status 201
* print 'the value of response is:', response
And def app = response
And path 'app.InDataRequestIdOut' + '/FileCreators'
* def body =
"""
{
"InDataRequestId": "1d6326a2-d25f-41d2-9303-8a6e6101efcc",
"ProcedureName": "",
"SourceWorkspacePath": ""
}
"""
And request body
When method post
Then status 201
First, it looks to me you are using the wrong Eclipse plugin for Cucumber, please refer to this issue and make sure: https://github.com/intuit/karate/issues/90
There are so many things you are doing wrong. For example it should be application/json (lowercase). There are many places you are mixing upper case and lower case in your above description, please take care.
And there is no way to understand how the URL is being set up, without this - I can't provide proper help.
You have a fundamental misunderstanding of how to use Karate expressions, for example this is just concatenating two strings:
And path 'app.InDataRequestIdOut' + '/FileCreators'
This may give you some hints, instead of the above:
When url baseUrl
And path "InDataRequests('" + response.InDataRequestIdOut + "')/FileCreator"
And is it FileCreator or FileCreators. You seem to be quite careless :(

StackExchange API authentication in Google Apps Script

I'm trying to use the V2.2 of StackExchange API in Google Apps Script.
The problem comes in the last step of the explicit OAuth 2.0 flow, when I try to send the POST request to obtain the access token. I receive a 404 error, but making the same request manually (using postman extension) everything is ok.
To simplify the problem, if I send this POST request with no payload I receive the same 404
var response = UrlFetchApp.fetch("https://stackexchange.com/oauth/access_token", {
method: 'post',
muteHttpExceptions: true
});
Logger.log(response);
while in postman I receive this 400:
{
"error": {
"type": "invalid_request",
"message": "client_id not provided"
}
}
I guess this will be a problem with UrlFetchApp, but does anyone know how to solve it? Thanks!
The problem is related with the Origin header.
You cannot remove from the header directly but you can perform the call via a proxy :)
You need to provide the data for the post by adding an 'option' object to the call.
For example:
var options = { "method" : "post", "payload" : payload };
UrlFetchApp.fetch("https://stackexchange.com/oauth/access_token", options);
Btw, have you tried you use the OAuth that UrlFetch got: https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#addOAuthService(String) - It might be better way.