This repository has been archived by the owner on Aug 23, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
doc.rkt
162 lines (143 loc) · 5.48 KB
/
doc.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#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)))
;; If `doc' is a path-string? then load that file. Otherwise if `doc'
;; is a symbol? then convert that to a string and append that to this
;; package's cache directory and the vendor subdirectory.
(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)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Some accessors for the discovery document jsexpr.
;;
;; The case-lambdas provide a shortcut for resources that aren't nested.
(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 '()))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Read an API key from a dot file, defaulting to ~/.google-api-key
(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)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Convenience when you don't want to process results in "pages", but
;; rather want one -- albeit potentially huge -- list of items.
(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])))