-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathv3.functions
243 lines (188 loc) · 8.84 KB
/
v3.functions
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# This file is part of shellfire github. It is subject to the licence terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/shellfire-dev/github/master/COPYRIGHT. No part of shellfire github, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
# Copyright © 2014-2015 The developers of shellfire github. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/shellfire-dev/github/master/COPYRIGHT.
# GitHub uses <Link>, pagination, Hypermedia, HATEOAS and URI templates as they're cool and fully RESTful
# This just increases the pain of using their API from a command line. Roy Fielding was very clever in defining REST, but not everything REST is pragmatic.
# Pagination is a horrid idea. What kind of server or client can't cope with a few 100 lines? It pushes state management all over the place. And it's usually broken - the ranges change between requests, so items move pages, and so disappear, or appear twice. It takes all of the worst ideas from database result set cursors, and pushes them into the web of 2014.
# A programmatically navigable API seems nice, until you realise that a client doesn't have a foggiest what all those link relations mean.
# Doesn't know what the data schema is for every resource. And lastly, why, is it good to allow 'URLs to change' if Roy Fielding's 'this allows people to do long-term design right' - "too many APIs are short-term", if in such omnipotent forethought one needs new URL structures. It the URL structure is now self-described, why change it? Frankly, every example I've seen is pretty moot. And the efficiency of parsing such designs, without allowing 'just go there' access is appalling, as well as convoluted and complex (and much more work, with a lot more parsing).
# URI templates are a nice idea, but the vagueness of the rules (eg variable names can also be percent encoded, if not, need percent encoding), the "don't output the prefix if all expansions are null" (which requires one to cache the entire output, just in case), the '+' operator (yuck, special encoding rules for literals which all of a sudden aren't quite literal, with a special case for % again requiring look-ahead), yuck and list / map explosion (depending on what the binder decides, not the template) are another mess - by the same crowd. It would have been VERY simple to just say "the rest of the URL is treated as already encoded. Variables should be encoded." HTTP with REST-like APIs won out because of simplicity and sanity. Frankly, the worst examples are the HTTP Accept-* and Content-* headers, which are badly implemented everywhere, not least in the one web server the web big wigs personally hack on - Apache. Frankly, %-encoding itself is another mess (at least 4 contradictory RFCs, special rules for form bodies, and no one even questions why it is necessary in 2014).
# It seems REST has now gained the "enterprise tooling" and "self describing schema stuff" we loathed about WS-DeathStar.
core_usesIn curl
core_usesIn urlencode template
core_usesIn jsonreader
core_usesIn jsonwriter
core_dependency_requires '*' cat
github_api_v3_initialise()
{
local tokenFilePath="$1"
if [ $# -eq 2 ]; then
# eg https://{server}/api/v3
github_api_v3_endpoint="$2"
else
github_api_v3_endpoint='https://api.github.com'
fi
# "$(<FILE)" doesn't work in all situations
local githubOAuth2PersonalAccessToken="$(cat "$tokenFilePath")"
github_api_v3_user="${githubOAuth2PersonalAccessToken}:x-oauth-basic"
local TMP_FILE
if core_variable_isUnset github_api_v3_requestFilePath; then
core_temporaryFiles_newFileToRemoveOnExit
github_api_v3_requestFilePath="$TMP_FILE"
fi
if core_variable_isUnset github_api_v3_responseFilePath; then
core_temporaryFiles_newFileToRemoveOnExit
github_api_v3_responseFilePath="$TMP_FILE"
fi
}
core_dependency_requires '*' date
github_api_v3_timestampToEpochSeconds()
{
date -d "$1" '+%s'
}
core_dependency_requires '*' cat
_github_api_v3_errorMessageExit()
{
local verb="$1"
local url="$2"
local errorMessage='(unknown)'
local requestId='(unknown)'
local resource=''
local code=''
local field=''
local errors
local errors_initialised
core_variable_array_initialise errors
# eg {"message":"Validation Failed","request_id":"d4cb5605-73cd-11e4-85aa-d38119fad9f5","documentation_url":"https://developer.github.com/v3","errors":[{"resource":"ReleaseAsset","code":"already_exists","field":"name"}]}
_github_api_v3_errorMessage_event()
{
if jsonreader_eventMatches '/message' string; then
errorMessage="$eventValue"
return 0
fi
if jsonreader_eventMatches '/request_id' string; then
requestId="$eventValue"
return 0
fi
if jsonreader_eventMatches "/errors:${jsonreader_path_index}/" start; then
resource=''
code=''
field=''
fi
if jsonreader_eventMatches "/errors:${jsonreader_path_index}/resource" string; then
resource="$eventValue"
fi
if jsonreader_eventMatches "/errors:${jsonreader_path_index}/code" string; then
code="$eventValue"
fi
if jsonreader_eventMatches "/errors:${jsonreader_path_index}/field" string; then
field="$eventValue"
fi
if jsonreader_eventMatches "/errors:${jsonreader_path_index}/" end; then
core_variable_array_append errors "There is the error (number $eventIndex) '$code' in resource '$resource's field '$field'."
fi
}
jsonreader_parse "$github_api_v3_responseFilePath" _github_api_v3_errorMessage_event
if [ "$(core_init_verbosity)" -gt 2 ]; then
cat "$github_api_v3_responseFilePath" 1>&2
fi
case $(core_variable_array_length errors) in
0)
core_exitError $core_commandLine_exitCode_DATAERR "GitHub error for verb '$verb' to URL '$url' for request '$requestId' ($curl_httpStatusCode) was '$errorMessage'."
;;
1)
core_exitError $core_commandLine_exitCode_DATAERR "GitHub error for verb '$verb' to URL '$url' for request '$requestId' ($curl_httpStatusCode) was '$errorMessage': $(core_variable_array_string errors "")"
;;
*)
core_exitError $core_commandLine_exitCode_DATAERR "GitHub errors for verb '$verb' to URL '$url' for request '$requestId' ($curl_httpStatusCode) was '$errorMessage' (Details: $(core_variable_array_string errors " "))"
;;
esac
}
_github_api_v3_extractHeader_Location()
{
if [ "$fieldName" = 'LOCATION' ]; then
github_api_v3_header_Location="$fieldValue"
fi
}
_github_api_v3_relative_GET()
{
local relativePath="$1"
shift 1
printf '' >"$github_api_v3_requestFilePath"
_github_api_v3_relative GET "$relativePath" "$@"
}
_github_api_v3_relative_POST()
{
local relativePath="$1"
shift 1
_github_api_v3_relative POST "$relativePath" 'Content-Type' 'application/json' "$@"
}
_github_api_v3_relative_PATCH()
{
local relativePath="$1"
shift 1
_github_api_v3_relative PATCH "$relativePath" 'Content-Type' 'application/json' "$@"
}
_github_api_v3_relative_PUT()
{
local relativePath="$1"
shift 1
_github_api_v3_relative PUT "$relativePath" "$@"
}
_github_api_v3_relative()
{
local verb="$1"
local relativePath="$2"
shift 2
if core_variable_isUnset github_api_v3_endpoint; then
core_exitError $core_commandLine_exitCode_SOFTWARE "Please initialise first using 'github_api_v3_initialise' (to set 'github_api_v3_endpoint')"
fi
_github_api_v3_absolute "$verb" "${github_api_v3_endpoint}${relativePath}" "$@"
}
_github_api_v3_absolute_PATCH()
{
local absoluteUrl="$1"
shift 1
_github_api_v3_absolute PATCH "$absoluteUrl" 'Content-Type' 'application/json' "$@"
}
_github_api_v3_absolute_PUT()
{
local absoluteUrl="$1"
shift 1
_github_api_v3_absolute PUT "$absoluteUrl" "$@"
}
_github_api_v3_absolute()
{
local verb="$1"
local url="$2"
shift 2
if core_variable_isUnset github_api_v3_user; then
core_exitError $core_commandLine_exitCode_SOFTWARE "Please initialise first using 'github_api_v3_initialise' (to set 'github_api_v3_user')"
fi
# As an alternative to setting user, we can set a header: 'Authorization' "token ${github_api_v3_oauth2Token}"
set -- "Accept" 'application/vnd.github.v3+json' "$@"
if core_variable_isUnset curl_uploadFile; then
local curl_uploadFile="$github_api_v3_requestFilePath"
fi
core_message INFO "Making GitHub API Call: $verb $url"
local curl_httpVersion curl_httpReasonPhrase
curl_http basic "$github_api_v3_user" "$verb" "$url" "$github_api_v3_responseFilePath" "$@"
unset curl_uploadFile
case $curl_httpStatusCode in
# Bad Request (we sent an invalid JSON)
400)
_github_api_v3_errorMessageExit "$verb" "$url"
;;
# Unprocessable Entity (we sent an invalid field in the body)
422)
_github_api_v3_errorMessageExit "$verb" "$url"
;;
# Unauthorized (not always)
401)
_github_api_v3_errorMessageExit "$verb" "$url"
;;
# Unauthorized or Rate-Limited
403)
_github_api_v3_errorMessageExit "$verb" "$url"
;;
esac
}