"Missing End Time" with Google Calendar and Racket Google Package
Asked Answered
G

1

1

I am using a Google API library for Racket to try to update a Google Calendar. (The API is incomplete so I'm extending it as I go.)

I seem to have trouble with adding events to a calendar using the events.insert put call. The code that does the put looks like:

(define (insert-events calendar-id
                       event
                       #:token [t #f]
                       #:max-attendees [max-attendees #f]
                       #:send-notifications [send-notifications #f]
                       #:supports-attachments [supports-attachments #f])
  (json-api-post (string->url
                  (format
                   "https://www.googleapis.com/calendar/v3/calendars/~a/events"
                   (form-urlencoded-encode calendar-id)))
                  event
                  #:token t))


(define (json-api-post u b
                       #:token [t #f]
                       #:headers [headers '()])
  (define b* (jsexpr->bytes b))
  (let retry ([t t]
              [retry-counter 0])
    (parse-json-response
     (POST-string u b* (if t (cons (token->authorization-header t) headers) headers))
     retry
     t
     retry-counter)))

(define (POST-string u b headers)
  (port->string (post-pure-port u b headers)))

However, no matter how I use the call, I always get back an error 400 with the message: "Missing End Time". I checked out this question to ensure that I was sending my request correctly. Which I appear to be. For reference, the JSON object I am sending is:

{
 "end": {
  "dateTime": "2016-05-30T14:00:00-04:00"
 },
 "start": {
  "dateTime": "2016-05-30T13:00:00-04:00"
 }
}

Also, to make sure I was properly accessing the correct key and calendar id, I setup up an echo server for my local machine, and changed the url from google.com to localhost, my response seems normal:

POST /<Calendar-Id-Redacted> HTTP/1.1
Host: localhost
User-Agent: Racket/6.5.0.5 (net/http-client)
Content-Length: 116
Authorization: Bearer <Key-Redacted>

{
 "end": {
  "dateTime": "2016-05-30T14:00:00-04:00"
 },
 "start": {
  "dateTime": "2016-05-30T13:00:00-04:00"
 }
}

I seem to be doing everything correct. And even if there was a bug in my Racket code, sending in the exact same JSON object via Google's developer web console seems to work as intended. So why does sending this particular POST not work?

Girand answered 31/5, 2016 at 1:35 Comment(0)
G
3

Close. But you are actually missing one piece of important data in the headers field of your POST request. Namely, the line:

Content-Type: application/json

Making your entire request to be:

POST /<Calendar-Id-Redacted> HTTP/1.1
Host: localhost
User-Agent: Racket/6.5.0.5 (net/http-client)
Content-Length: 116
Authorization: Bearer <Key-Redacted>
Content-Type: application/json

{
 "end": {
  "dateTime": "2016-05-30T14:00:00-04:00"
 },
 "start": {
  "dateTime": "2016-05-30T13:00:00-04:00"
 }
}

For some reason Google doesn't give you a nice error message if you are missing it, but if you do have it, Google will follow the API call.

This can be seen more clearly when you run the command in curl, if you run it like this (where you have a file called data.txt that has your json object in it:

curl -X POST -d @data.txt https://www.googleapis.com/calendar/v3/calendars/<Calendar-Id-Redacted>/events --header 'Authorization: Bearer <Key-Redacted>'

It will fail. But if you add the header with --header "Content-Type: application/json", the API will work as expected:

curl -X POST -d @data.txt https://www.googleapis.com/calendar/v3/calendars/<Calendar-Id-Redacted>/events --header 'Authorization: Bearer <Key-Redacted>' --header "Content-Type: application/json"

Adding that header to the library you have just requires one modification to the function that generates your post request:

(define (json-api-post u b
                       #:token [t #f]
                       #:headers [headers '("Content-Type: application/json")])
  ....)

It is exactly the same as your previous one, except we've modified the headers field to contain (by default), the list containing the string: "Content-Type: application/json". With this change, google should accept your API. (Note that there are many other ways to modify the headers function, this is just a simple way to do it.)

Putting it all together, your final code should look something like:

(define (insert-events calendar-id
                       event
                       #:token [t #f]
                       #:max-attendees [max-attendees #f]
                       #:send-notifications [send-notifications #f]
                       #:supports-attachments [supports-attachments #f])
  (json-api-post (string->url
                  (format
                   "https://www.googleapis.com/calendar/v3/calendars/~a/events"
                   (form-urlencoded-encode calendar-id)))
                  event
                  #:token t))


(define (json-api-post u b
                       #:token [t #f]
                       #:headers [headers '("Content-Type: application/json")])
  (define b* (jsexpr->bytes b))
  (let retry ([t t]
              [retry-counter 0])
    (parse-json-response
     (POST-string u b* (if t (cons (token->authorization-header t) headers) headers))
     retry
     t
     retry-counter)))

(define (POST-string u b headers)
  (port->string (post-pure-port u b headers)))

Hope that helps.

Girand answered 31/5, 2016 at 1:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.