4

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());


}
Mogsdad
  • 44,709
  • 21
  • 151
  • 275

1 Answers1

5

When debugging external host communication with UrlFetchApp, there are a few tools available to you.

  1. 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.

  2. 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'.

    debugger view

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);
  }

}
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
  • 1
    Wow, this is a very thoughtful and well-researched answer. – Shotgun Ninja Aug 27 '15 at 16:24
  • 1
    Thanks @ShotgunNinja! Similar questions keep coming up, so I figured it was time that someone explained how to figure these problems out. I've decided to turn it into a blog post, as well. – Mogsdad Aug 27 '15 at 16:29
  • 1
    Ha, figures it'd be someone involved in FIRST Robotics to write a question like this. #1652 and #2970 alum! – Shotgun Ninja Aug 27 '15 at 16:33