#lang racket
(require json
net/url
net/uri-codec
planet/planet-archives
planet/version)
(provide list-services
download-discovery-document
load-discovery-document
get-discovery-document
api-parameters
resources
resource-names
resource
methods
method-names
method
method-proc
method-parameters
schemas
schema-names
schema
api-key
paged
this-package-cache-dir
)
(define (list-services #:name [name 'N/A]
#:label [label 'N/A]
#:only-preferred? [preferred #t])
(define base "https://www.googleapis.com/discovery/v1/apis")
(define qps (alist->form-urlencoded
(filter-map (lambda (k v)
(cond [(eq? v 'N/A) #f]
[(eq? v #t) (cons k "true")]
[(eq? v #f) (cons k "false")]
[else (cons k v)]))
(list 'name 'label 'preferred)
(list name label preferred))))
(define u (string-append base (if (equal? qps "") "" "?") qps))
(call/input-url (string->url u)
get-pure-port
(compose1 bytes->jsexpr port->bytes)))
(define (discovery-url name ver)
(string->url
(format "https://www.googleapis.com/discovery/v1/apis/~a/~a/rest"
name ver)))
(define (download-discovery-document name ver [path (string-append name ".js")])
(call-with-output-file* path
(lambda (out)
(call/input-url (discovery-url name ver)
get-pure-port
(lambda (in)
(copy-port in out))))
#:mode 'text
#:exists 'replace))
(define/contract (get-discovery-document name ver)
(string? string? . -> . jsexpr?)
(call/input-url (discovery-url name ver)
get-pure-port
(compose1 bytes->jsexpr port->bytes)))
(define/contract (load-discovery-document doc)
((or/c symbol? path-string?) . -> . jsexpr?)
(define path
(cond [(symbol? doc)
(build-path (this-package-cache-dir) "vendor" (symbol->string doc))]
[else doc]))
(bytes->jsexpr (file->bytes path)))
(define/contract (this-package-cache-dir)
(-> (or/c #f path?))
(for/or ([x (get-all-planet-packages)])
(match-define (list path owner name _ maj min) x)
(and (equal? owner (this-package-version-owner))
(equal? name (this-package-version-name))
(equal? maj (this-package-version-maj))
(equal? min (this-package-version-min))
path)))
(define (api-parameters s)
(dict-ref s 'parameters))
(define (resources s)
(dict-ref s 'resources '()))
(define (resource-names s)
(dict-keys (resources s)))
(define (resource s rn)
(dict-ref (resources s) rn '()))
(define methods
(case-lambda
[(r) (dict-ref r 'methods '())]
[(s rn) (methods (resource s rn))]))
(define method-names
(case-lambda
[(r) (dict-keys (methods r))]
[(s rn) (method-names (resource s rn))]))
(define method
(case-lambda
[(r mn) (dict-ref (methods r) mn '())]
[(s rn mn) (method (resource s rn) mn)]))
(define method-proc
(case-lambda
[(r mn) (dict-ref (method r mn) 'proc '())]
[(s rn mn) (method-proc (resource s rn) mn)]))
(define method-parameters
(case-lambda
[(r mn) (dict-ref (method r mn) 'parameters '())]
[(s rn mn) (method-parameters (resource s rn) mn)]))
(define (schemas s)
(dict-ref s 'schemas '()))
(define (schema-names s)
(dict-keys (schemas s)))
(define (schema s sn)
(dict-ref (schemas s) sn '()))
(define (read-api-key [file (build-path (find-system-path 'home-dir)
".google-api-key")])
(cond [(file-exists? file)
(match (file->string file #:mode 'text)
[(regexp "^\\s*(.*?)\\s*(?:[\r\n]*)$" (list _ k)) k]
[else (error 'read-api-key "Bad format for ~a" file)])]
[else (error 'read-api-key "Put your Google API Key in ~a" file)]))
(define api-key (make-parameter (read-api-key)))
(define-syntax-rule (paged (func args ...))
(let loop ([js (func args ...)])
(define page-token (hash-ref js 'nextPageToken #f))
(cond [(and page-token
(hash-has-key? js 'items))
(hash-update js
'items
(lambda (xs)
(append xs
(hash-ref (loop (func args ...
#:pageToken
page-token))
'items))))]
[else js])))