Unexpected Error on UrlFetchApp - google-apps-script

I'm trying to call my ServiceNow JSON web service. I'm getting an unexpected error when I execute URLFetchApp. I'm guessing I'm passing in the authorization headers in the wrong way but both the GAS and ServiceNow documentation is beyond terrible. I've seen some of the other SO questions similar to this but none have worked. Any help would be appreciated.
function getOpenTickets(){
var headers = {
"Authorization":"Basic RgRJ5U6EsxHt00229KX5Hj0WV1z18q08==",
"Content-Type":"application/json",
"Username":"myusername",
"Password":"mypassword"
}
var url = "https://mysninstance.service-now.com/u_equipment_repair.do?JSONv2=&sysparm_view=vendor&displayvalue=true&sysparm_action=getRecords&sysparm_query=state=500^assignment_group.name=MyGroup^ORDERBYDESCnumber";
var url = encodeURIComponent(url);
var options = {
"method":"get",
"headers":headers
}
var result = UrlFetchApp.fetch(url,options);
Logger.log(result.getContentText());
}

OK so I found the solution. There were actually two problems.
The first was with the way I was passing the authorization headers. I was passing the basic authentication as an already encoded base64 string, on top of which I was still passing the username and password which was redundant. For whatever reason Google Apps Script (GAS) doesn't like this. Once I changed the headers and the options as shown below it was fine.
The second problem was the the URI encoding. The query string did need to be encoded because of the caret "^" symbols, but for whatever reason GAS's encodeURIComponent was not encoding it properly. As soon as I manually replaced the caret symbols with their URL encoded equivalents , which is "%5E", everything worked fine and I was able to retrieve my ServiceNow data via Google Apps Script.
function getOpenTickets3(){
var headers =
{
Authorization : "Basic " + Utilities.base64Encode('myusername:mypassword'),
"Content-Type":"application/json"
}
var options =
{
"method" : "get",
"headers": headers
};
var url = "https://mysninstance.service-now.com/u_equipment_repair.do?JSONv2=&sysparm_view=vendor&displayvalue=true&sysparm_action=getRecords&sysparm_query=state=500%5Eassignment_group.name=Somevendor%5EORDERBYDESCnumber";
var result = UrlFetchApp.fetch(url,options);
Logger.log(result.getContentText());
}

You are URI encoding your entire URL in this line:
var url = encodeURIComponent(url);
In your URL, the base path needs to be unescaped when passed to fetch(...):
https://mysninstance.service-now.com/u_equipment_repair.do
Each parameter following the ? is a URI component, like:
sysparm_view=vendor
In this case, the parameter name is sysparm_view and the value is vendor, you would need to URI encode the value (vendor) if it contained special characters like one of /?&.
In the static URL you provide, there's actually nothing that needs to be encoded, so removing that call to encodeURIComponent(url) should work.
If you are dealing with dynamic values for your URL parameters, then you'd want to URI encode each parameter value separately, before concatenating onto the main string.

Related

How to pull data from Toggl API with Power Query?

First timer when it comes to connecting to API. I'm trying to pull data from Toggl using my API token but I can't get credentials working. I tried to replicate the method by Chris Webb (https://blog.crossjoin.co.uk/2014/03/26/working-with-web-services-in-power-query/) but I can't get it working. Here's my M code:
let
Source = Web.Contents(
"https://toggl.com/reports/api/v2/details?workspace_id=xxxxx&client=xxxxxx6&billable=yes&user_agent=xxxxxxx",
[
Query=[ #"filter"="", #"orderBy"=""],
ApiKeyName="api-token"
])
in
Source
After that I'm inputting my API Token into Web API method in Access Web content windows but I get an error that credentials could not be authenticated. Here's Toggl API specification:
https://github.com/toggl/toggl_api_docs/blob/master/reports.md
Web.Contents function receives two parameters: url + options
Inside options, you define the headers and the api_key, and other queryable properties, such as:
let
baseUrl = "https://toggl.com/",
// the token part can vary depending on the requisites of the API
accessToken = "Bearer" & "insert api token here"
options = [
Headers = [Authorization = accessToken, #"Content-Type" =
"application/Json"], RelativePath ="reports/api/v2/details", Query =
[workspace_id=xxxxx, client=xxxxxx6 , billable=yes, user_agent=xxxxxxx]
]
Source = Web.Contents(baseUrl, options)
// since Web.Contents() doesn't parse the binaries it fetches, you must use another
// function to see if the data was retreived, based on the datatype of the data
parsedData = Json.Document(Source)
in
parsedData
The baseUrl is the smallest url that works and never changes;
The RelativePath is the next part of the url before the first "?".
The Query record is where you define all the attributes to query as a record.
This is usually the format, but check the documentation of the API you're querying to see if it is similar.

NativeScript Throwing Error Response with status: 200 for URL: null

I am using Angular4 with TypeScript version 2.2.2
My web app is running fine when I call JSON with Filters but my NativeScript app fails when I call the Filter Values as an Object but works fine when I call filter values as a string.
Error Response with status: 200 for URL: null
THIS WORKS
https://domainname.com/api/v1/searchevents?token=057001a78b8a7e5f38aaf8a682c05c414de4eb20&filter=text&search=upcoming
If the filter value and search value is STRING it works whereas if they are objects as below, it does not work
THIS DOES NOT WORK
https://api.domainname.com/api/v1/searchevents?token=057001a78b8a7e5f38aaf8a682c05c414de4eb20&filter={"limit":"12","skip":"0"}&search={"search":"","latitude":"","longitude":"","categories":"","address":"","type":"upcoming"}
The Code I used is below
getData(serverUrl, type, skip_limit) {
console.log(serverUrl);
let headers = this.createRequestHeader();
let token_value = localStorage.getItem('access_token')
let url;
var filter;
filter = '{"limit":"10","skip":"0"}'
url = this.apiUrl + serverUrl + '?token=' + token_value + '&filter=' + filter
return this.http.get(url, { headers: headers })
.map(res => res.json());
}
The URL as formed above for the API is fine and works fine. Yet the error comes Error Response with status: 200 for URL: null
CAN ANYONE HELP ME SOLVE THIS?
Looks like the issue is the "filter" values are of different type and from what you mentioned as what worked, your service is expecting a string and not an object/array. So it fails to send the proper response when it gets one. With an object in the URL, you may have to rewrite the service to read it as an object (parse the two attributes and get them individually)
To make it simple, you can make these two as two different variables in the URL. like below,
https://api.domainName.in/api/v1/oauth/token?limit=10&skip=0
Be more precise in whats happening in your question,
1) Log the exact URL and post it in the question. No one can guess what goes in "text" in your first URL.
2) Your URL which you mentioned as worked have "token" as part of path, but in the code, its a variable which will have a dynamic value from "token_value".
3) Post your service code. Especially the signature and input parsing part.
Got the solution:
All you have to do is encode the Filter and Search Parameters if it is an Object or Array using Typescript encodeURI()
var filter = '{"limit":"12","skip":"0"}'
var search = '{"search":"","latitude":"","longitude":"","categories":"","address":"","type":"upcoming"}'
var encoded_filter = encodeURI(filter);
var encoded_search = encodeURI(search);
url = this.apiUrl+serverUrl+'?token='+token_value+'&filter='+encoded_filter+'&search='+encoded_search

Invalid Argument on basic API call using UrlFetchApp.fetch(url)

I'm new to Google Apps Scripts and I've been trying to make a simple Get call to a URL. I make this call from my browser: https://accounting.sageone.co.za/api/1.1.1/Supplier/Get?apikey={4CFEEFE1-CE04-425F-82C3-DCB179C817D5}&companyid=164740 and get the respons I'm looking for. I now try to make the call from Google Apps Scripts using the following code:
function myFunction() {
var url = 'https://accounting.sageone.co.za/api/1.1.1/Supplier/Get?apikey={4CFEEFE1-CE04-425F-82C3-DCB179C817D5}&companyid=164740
var response = UrlFetchApp.fetch(url);
Logger.log(response);
}'
I get a respons stating '
Message details
Invalid argument: https://accounting.sageone.co.za/api/1.1.1/Supplier/Get?apikey={4CFEEFE1-CE04-425F-82C3-DCB179C817D5}&companyid=164740 (line 4, file "Code")'
I've tried a whole bunch of permutations with no luck...
When using UrlFetchApp, you need to enter your URL parameters as part of a request parameters rather than in the URL itself. For a GET request these go directy as part of the parameters, for a POST request the parameters would be part of a payload object. Reference Documentation
Here is a modified function:
function myFunction() {
var url = 'https://accounting.sageone.co.za/api/1.1.1/Supplier/Get'
var params = {
"method": 'GET',
"apikey": "{4CFEEFE1-CE04-425F-82C3-DCB179C817D5}",
"companyid": "164740"
}
var response = UrlFetchApp.fetch(url, params);
Logger.log(response)
}
Note: This corrects your method, however, the server still requires further authentication. If you run into issues with that, ask another questions specific to that issue as well.

JSON.parse returning undefined object

Blizzard just shut down their old API, and made a change so you need an apikey. I changed the URL to the new api, and added the API key. I know that the URL is valid.
var toonJSON = UrlFetchApp.fetch("eu.api.battle.net/wow/character/"+toonRealm+"/"+toonName+"?fields=items,statistics,progression,talents,audit&apikey="+apiKey, {muteHttpExceptions: true})
var toon = JSON.parse(toonJSON.getContentText())
JSON.pase returns just an empty object
return toon.toSorce() // retuned ({})
I used alot of time to see if i could find the problem. have come up empty. Think it has something to do with the "responce headers".
Responce headers: http://pastebin.com/t30giRK1 (i got them from dev.battle.net (blizzards api site)
JSON: http://pastebin.com/CPam4syG
I think it is the code you're using.
I was able to Parse it by opening the raw url of your pastebin JSON http://pastebin.com/raw/CPam4syG
And using the following code
var text = document.getElementsByTagName('pre')[0].innerHTML;
var parse = JSON.parse(text);
So to conclude I think it is the UrlFetchApp.fetch that's returning {}
So i found the problems:
I needed https:// in the URL since i found after some hours that i had an SSL error
If you just use toString instead of getContentText it works. Thow why getContentText do not work, i am not sure of.
was same problem, this works for me (dont forget to paste your key)
var toonJSON = UrlFetchApp.fetch("https://eu.api.battle.net/wow/character/"+toonRealm+"/"+toonName+"?fields=items%2Cstatistics%2Cprogression%2Caudit&locale=en_GB&apikey= ... ")

GDocs Script to fetch web info behind a form using UrlFetchApp

I want to fetch some web data using GDocs but the data is behind a form so I need to post some data to the form to get the result. (So I cant use ImportXML etc)
The function that I am trying to use is https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app but I dont really know where to start since I don't have much java script experience.
Is there anyone that has a script that takes an url, form name & the data to post that can be used in GDocs?
There's an example of such a script here: How do I use Google Apps Script to change a CSS page to one with inline styles?.
It's a different application, but the method applies. Here's a skeleton function to get you started.
The payload object should contain the name / value pairs of the form data you want to simulate. The url should be changed to match the site you're submitting the form to. Once you've made the POST request by the call to UrlFetchApp.fetch(), you should be able to parse the response using the getElementByVal() utility from Does Google Apps Script have something like getElementById?.
function postForm() {
// Generate a POST request with form data.
var payload =
{
"name" : 'Anonymous Person', // Replace with relevant name / value pairs
"town" : "Springfield"
};
// Because payload is a JavaScript object, it will be interpreted as
// an HTML form. (We do not need to specify contentType; it will
// automatically default to either 'application/x-www-form-urlencoded'
// or 'multipart/form-data')
var options =
{
"method" : "post",
"payload" : payload,
"muteHttpExceptions" : true
};
var url = "http://form.somewhere.com"; // Replace as appropriate
var response = UrlFetchApp.fetch(url, options);
// Put the receieved xml response into XMLdocument format
var xml = response.getContentText();
var doc = XmlService.parse(xml);
// Extract the details you want...
var someData = getElementByVal( doc, 'textarea', 'name', 'text' );
...
}