API with Google Script Apps - javascript

I'm having trouble using Google Apps to interact with a management software called Kissflow.
function fun2() {
var id = "yyy";
var apisecretkey = "xxx";
var url ='https://'+id+'.kissflow.com/api/1/verify';
var options = {
method: 'post',
headers : {"Authorization" : " Basic " + Utilities.base64Encode(id + ":" + apisecretkey)},
payload: {
"grant_type": "client_credentials",
"scope": "basic+user"
},
muteHttpExceptions: true
};
var response = JSON.parse(UrlFetchApp.fetch(url, options).getContentText());
}
I would like to run this simple example of the API documentation, the goal is for me to be able to send data to the software through my interactions in a spreadsheet, for example. If you can help me in this I will be very grateful, I am new with API's :)
The following error appears: SyntaxError: unexpected token <in JSON at position 0 (line 30, file "Code") I don't know if I'm using this function correctly.
Kissflow API Documentation

JSON.parse(UrlFetchApp.fetch(url, options).getContentText())
Remove JSON.parse() from above line and output the response on your console using Logger.log() or to your browser log using console.log() and see the result. If there are errors it'll show more user friendly error message.

While I'm not familiar with the API, I suspect the issue is related to this line:
var response = JSON.parse(UrlFetchApp.fetch(url, options).getContentText());
You're fetching the response (which is probably JSON), then getting the content text of that response, then trying to parse that text as if it were JSON.
So (solution 1) if you set your response variable equal to
var response = UrlFetchApp.fetch(url, options).getContentText();
and try printing that variable to the console, you may find you have something you can work with.
Alternatively (solution 2), you might try:
var response = JSON.parse(UrlFetchApp.fetch(url, options));
if you'd rather have a JavaScript object to work with instead of a string (and assuming the .fetch method you're calling does indeed provide a JSON-formatted response.)

Related

Autodesk 2 Legged Authentication in javascript, two different errors

I get two different errors depending on if I GET or POST, but my autodesk rep assures me that using postman (not javascript) the resource does exist and he can get an authentication token.
If I do:
var url = "https://developer.api.autodesk.com/authentication/v1/authenticate";
var options = {
"method": "GET",
"headers":{"Content-Type": "application/x-www-form-urlencoded",},
"body": {
"client_id" : "Z---F",
"client_secret" : "m---8",
"grant_type": "client_credentials",
"scope": "data:read"
}
}
;
console.log(url);
console.log("Options:\n"+JSON.stringify(options));
var res = UrlFetchApp.fetch(url, options).getContentText();
Logger.log(res);
}
I get
Exception: Request failed for https://developer.api.autodesk.com returned code 404. Truncated server >response: { "developerMessage":"The requested resource does not exist.", "moreInfo": >https://forge.autodesk.com/en/docs/oauth/v2/developers_guide/error_hand... (use muteHttpExceptions >option to examine full response)
if I do
var url = "https://developer.api.autodesk.com/authentication/v1/authenticate";
var options = {
"method": "POST",
"headers":{"Content-Type": "application/x-www-form-urlencoded",},
"body": {
"client_id" : "Z---F",
"client_secret" : "m---8",
"grant_type": "client_credentials",
"scope": "data:read"
}
}
;
console.log(url);
console.log("Options:\n"+JSON.stringify(options));
var res = UrlFetchApp.fetch(url, options).getContentText();
Logger.log(res);
}
I get
Exception: Request failed for https://developer.api.autodesk.com returned code 400. Truncated server >response: {"developerMessage":"The required parameter(s) client_id,client_secret,grant_type not present >in the request","errorCode":"AUTH-008","more info":"h... (use muteHttpExceptions option to examine full >response)
Any thoughts why it would say a resource doesn't exist that does exist? There is no personal info on here so the fact that he is doing it versus me doing it shouldn't matter. All the posts I could find on this issue were dealing with the curl version of this call rather than a javascript with options bundle
The URL is definitely valid (here's the API reference for this endpoint), so I would recommend debugging the actual code that's making the request, for example:
debug the UrlFetchApp class (where is it coming from btw?) and its fetch method, making sure that it's no modifying the url you're passing in
if the code is running in a browser, try looking at the DevTools Network tab and see if the request actually goes out to https://developer.api.autodesk.com/authentication/v1/authenticate
if the code is running in a browser, try using the built-in Fetch API instead
Also, I'm not sure if this is something handled automatically by the UrlFetchApp but specifying the content type of your request as application/x-www-form-urlencoded and then passing in a JSON object as the body doesn't seem right. As you can see in the documentation the request body should look more like this:
client_id=<your client id>&
client_secret=<your client secret>&
grant_type=client_credentials&
scope=data:read

How to convert a cURL request into a Google Apps Script UrlFetchApp

I'm aware this is probably basic stuff.
I've read a lot of the other questions on here around converting cURL into a Google Apps Script UrlFetchApp request however, I'm unable to get it to work. I'm a marketer, not a developer, so I have limited knowledge of Javascript but I'm usually able to manipulate reference scripts for my own needs. This one just won't work for me.
All I get is "Authentication has failed" as a response.
My current code is:
function wisepopsData() {
var API_KEY = 'APIKEYHERE';
var root = 'https://app.wisepops.com/api1/wisepops';
var params = {
'headers': {
'Authorization': 'WISEPOPS-API ' + Utilities.Base64Encode(API_KEY)
}
};
var response = UrlFetchApp.fetch(root, params);
var data = response.getContentText();
var json = JSON.parse(data);
Logger.log(json);
}
The documentation I'm using is below. I'm trying to implement the performance data part --> https://support.wisepops.com/en/articles/572165-wisepops-api-basics#performance-data-on-your-wisepops
The part I can't get my head around is 'Authorization: WISEPOPS-API key="YOUR_API_KEY_HERE"'. I've tried lots of combinations in the header of the request to get this bit to work and still nothing.
Try changing params variable intialization.
something like :
var params = {
'headers': {
'Authorization': 'WISEPOPS-API key="' + API_KEY + '"'
}};

Why is Kimono API responding with a 404?

I am trying to update my kimono API via Google Script in Sheets. There are many urls in the sheet, but I've only shown 2 for this example.
I am receiving HTTP error 404. I've checked, and the apikey and id are fine.
How can I determine what's really wrong?
function PostParameters2() {
var parameters = {
apikey: "--apikey--",
urls: [
"https://twitter.com/search?q=%23running",
"https://twitter.com/search?q=%23swimming"
]
};
var data = JSON.stringify(parameters);
var url = 'https://kimonolabs.com/kimonoapis/--apiId--/update';
var options = {
'method': 'POST',
'content-Type': 'application/json',
'payload': data
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getResponseCode());
}
When debugging external host communication with UrlFetchApp, there are a few tools available to you.
It helps to muteHttpExceptions so you can view them. (In fact, you can simply write your code to handle them yourself, and only throw for exceptions you really don't expect.)
This is done by adding 'muteHttpExceptions' : true to your fetch parameters.
With exceptions muted, fetch won't throw an exception, instead it will pass failure response codes in the HTTPResponse. From there, you can extract the response code and content text for debugging (or automatic handling).
Modify your code like this, to log errors:
var response = UrlFetchApp.fetch(url, options);
var rc = response.getResponseCode();
if (rc !== 200) {
// HTTP Error
Logger.log("Response (%s) %s",
rc,
response.getContentText() );
// Could throw an exception yourself, if appropriate
}
Run, and here's what we see:
[15-08-27 11:18:06:688 EDT] Response (404.0) {
"error": true,
"message": "Could not find API"
}
Some APIs give very rich error messages. This one, not so much. But it does tell us that we have the URL correct, but that the service couldn't find the API we want. Next, dig into why that is so.
We can examine the fetch options and parameters by using getRequest(). Add this line just above the existing fetch() call, and put a breakpoint on the fetch().
var test = UrlFetchApp.getRequest(url, options);
Start the function in the debugger, and when the breakpoint is hit, examine the contents of test carefully.
A common problem is with the encoding of the POST payload. You hand-encoded # to %23 and used JSON.stringify(), so no problem there.
Checking the remaining options, we see that the contentType isn't 'application/json'.
So now you look at your code and find that the name for contentType was mis-typed as content-Type. Remove the hyphen, and try again.
Keep going until you've identified and fixed any other bugs.
Another tip is to use encodeURIComponent() to escape restricted characters in your fetch parameters, rather than hand-encoding them. It simplifies your code, because you can write the "real" characters like # instead of their UTF-8 escape sequences like %23.
Updated code:
function PostParameters2() {
var parameters = {
apikey: "--apikey--",
urls: [
encodeURIComponent("https://twitter.com/search?q=#running"),
encodeURIComponent("https://twitter.com/search?q=#swimming")
]
};
var data = JSON.stringify(parameters);
var url = 'https://kimonolabs.com/kimonoapis/--apiId--/update';
var options = {
'method': 'POST',
'contentType': 'application/json',
'payload': data,
'muteHttpExceptions' : true
};
var test = UrlFetchApp.getRequest(url, options);
var response = UrlFetchApp.fetch(url, options);
var rc = response.getResponseCode();
if (rc !== 200) {
// HTTP Error
Logger.log("Response (%s) %s",
rc,
response.getContentText() );
// Could throw an exception yourself, if appropriate
}
else {
// Successful POST, handle response normally
var responseText = response.getContentText();
Logger.log(responseText);
}
}

Flickr API throws up "Invalid API Key (Key has invalid format)" when POSTing data with oauth signing

I can't seem to find this answered anywhere on SO or even Google - I have an oauth-signed call to the Flickr upload API, and following the docs I've signed the POST operation the usual oauth way (but without the photo data). For testing purposes I've only passed along a title and the photo data, which means I end up a var flickrURI that contains the following url for POSTing to:
https://api.flickr.com/services/upload/
? format=json
& oauth_consumer_key=...
& oauth_nonce=2e57b73fec6630a30588e22383cc3b25
& oauth_signature_method=HMAC-SHA1
& oauth_timestamp=1411933792346
& oauth_token=...
& title=test
& oauth_signature=O7JPn1m06vl5Rl95Z2in32YWp7Q%3D
(split over multiple lines for legibility in this question, the actual URL has no whitespacing around the ? and & for obvious reasons).
The oauth signing itself is quite correct, and code used for accessing the not-upload-API all over the place with correct behaviour, so it seems pretty much impossible for that to get the signing wrong, other than perhaps signing with "not enough data" or perhaps signing with "too much data".
The auth signing first forms the auth query string, in this case:
oauth_consumer_key=...
&oauth_nonce=60028905f65cf9d7649b3bce98f718f8
&oauth_signature_method=HMAC-SHA1
&oauth_timestamp=1411939726691
&oauth_token=...
&title=test
which is then used to form the verb + address + encoded base string:
POST&https%3A%2F%2Fapi.flickr.com%2Fservices%2Fupload%2F&oauth_consumer_key%3D...%26oauth_nonce%3D60028905f65cf9d7649b3bce98f718f8%26oauth_signature_method%3DHMAC
-SHA1%26oauth_timestamp%3D1411939726691%26oauth_token%3D...%26title%3Dtest
This is then HMAC-SHA1 digested using the Flickr and oauth secrets:
function sign = (data, key, secret) {
var hmacKey = key + "&" + (secret ? secret : ''),
hmac = crypto.createHmac("SHA1", hmacKey);
hmac.update(data);
var digest = hmac.digest("base64");
return encodeURIComponent(digest);
}
And for GET requests, this works perfectly fine. For POST requests things seem to be difference, despite the docs not explain which part is supposedly different, so I the tried to use the Nodejs request package to perform the POST action in what seemed a normal way, using:
uploadOptions = {
oauth_consumer_key = auth.api_key,
oauth_nonce = auth.oauth_nonce,
oauth_timestamp = auth.oauth_timestamp,
oauth_token = auth.access_token,
oauth_signature_method = "HMAC-SHA1",
title: title,
photo: <binary data buffer>
};
flickrURL = formSignedURL(auth);
request.post({
url: flickrURI,
headers: {
"Authorization": 'oauth_consumer_key="...",oauth_token="...",oauth_signature_method="HMAC-SHA1",oauth_signature="...",oauth_timestamp="...",oauth_nonce="...",oauth_version="1.0"'
},
multipart: [{
'content-type': 'application/json',
body: JSON.stringify(signOptions)
}]
},function(error, response, body) {
console.log("error");
console.log(error);
console.log("body");
console.log(body);
}
);
which yields a body that contains:
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="fail">
<err code="100" msg="Invalid API Key (Key has invalid format)" />
</rsp>
As the oauth signing doesn't really give me a choice in which API key to provide (there is only one) and the signing works just fine for the not-upload API, I can't figure out what this error message is trying to tell me. The key is definitely the right format because that's the format Flickr gives you, and it's the correct value, because it works just fine outside of uploading. I also made sure to get the oauth token and secret for that key with "delete" permission (widest possible permissions) so the included access token and access token secret should pass the "does this token for this key have permissions to write" test.
What obvious thing am I missing here that's preventing the upload to go through?
It looks like you're using the https://up.flickr.com/services/upload/ endpoint, which uses the old authentication scheme.
For OAuth, it should be https://api.flickr.com/services/upload/. Make sure the endpoint is included in signing process.
I don't think it's documented anywhere, but I remember having same issue a while back.
It turns out adding the data as request.post multipart information isn't good enough, and will make the Flickr API throw an "Invalid API Key (Key has invalid format)" error instead of saying what's actually wrong. The following request call, with exactly the same data, works:
var uploadOptions = ...
var flickrURL = ...;
var req = request.post(flickrURL, function(error, response, body) {
callback(error, body);
});
var form = req.form();
uploadOptions.photo = fs.createReadStream(...);
Object.keys(photoOptions).forEach(function(prop) {
form.append(prop, photoOptions[prop]);
});
Despite not making all that much sense call wise (why would the POST not already be done by the time we call form = req.form()?) this is request's "proper" way to send the POST payload over the wire, and makes the Flickr API process the photo upload just fine.

Javascript + MailChimp API subscribe

When making this request:
// Subscribe a new account holder to a MailChimp list
function subscribeSomeoneToMailChimpList()
{
var options =
{
"apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"id": "xxxxxx",
"email":
{
"email": "me#example.com"
},
"send_welcome": false
};
var mcSubscribeRequest = UrlFetchApp.fetch("https://us4.api.mailchimp.com/2.0/lists/subscribe.json", options);
var mcListObject = Utilities.jsonParse(mcSubscribeRequest.getContentText());
}
This response is returned:
Request failed for https://us4.api.mailchimp.com/2.0/lists/subscribe.json returned code 500. Truncated server response: {"status":"error","code":-100,"name":"ValidationError","error":"You must specify a apikey value"} (use muteHttpExceptions option to examine full response) (line 120, file "v2")
Line 120 is the line on which UrlFetchApp.fetch is called.
The API key is valid (I have tested with simpler API calls that don't include associative arrays). When I append the API key directly to the base URL and remove it from the options, I get an error saying that the list ID is invalid. When I then append the list ID directly to the base URL and remove it from options, I get an error saying that the email address must be in associative array form.
My question is: Using the above format, how does one send requests that contain associative arrays?
The relevant API documentation can be found here.
After further research & tinkering, I was able to solve this:
https://<dc>.api.mailchimp.com/2.0/lists/subscribe.json?apikey=<my_api_key>&id=<my_list_id>&email[email]=test#test.com&merge_vars[FNAME]=John&merge_vars[LNAME]=Doe&double_optin=false&send_welcome=false
Where <dc> should be replaced with the portion after the dash in your API Key. e.g. "us1", "us2", "uk1", etc.
Doing this in javascript exposes your API key to the world. If someone has your key, he/she can make changes to or gain access to your account.
I think I figured out what is going on after reading through the UrlFetchApp.fetch Docs.
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app?csw=1#fetch(String)
It looks like you should be using some of the extra params to do the request such as payload and method. Your options variable should look like this.
var payload = {
"apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"id": "xxxxxx",
"email": {
"email": "me#example.com"
},
"send_welcome": false
};
payload = Utilities.jsonStringify(payload); // the payload needs to be sent as a string, so we need this
var options = {
method: "post",
contentType: "application/json", // contentType property was mistyped as ContentType - case matters
payload: payload
};
var result = UrlFetchApp.fetch("https://<dc>.api.mailchimp.com/2.0/lists/subscribe.json", options);
Where <dc> should be replaced with the portion after the dash in your API Key. e.g. "us1", "us2", "uk1", etc.
The issue is that your options variable is suppose to be used as JSON and not as a GET url parameter. Also mailchimp specifies that it is better to use POST instead of GET. So over all you should make sure to set you method to "post" and make sure your payload is valid JSON.

Categories