-
Notifications
You must be signed in to change notification settings - Fork 1
/
README
305 lines (218 loc) · 11.7 KB
/
README
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
NAME
presto - Command-line interface for RESTful web services
VERSION
version 0.009
SYNOPSIS
Invoke from the shell:
bash$ presto http://my-server.com
Very basic usage:
http://my-server.com> GET /product/1.json
{"id":1,"name":"My Product"}
http://my-server.com> HEAD /product/1.json
HTTP/1.1 200 OK
Connection: close
Date: Thu, 28 Jun 2012 21:05:33 GMT
Content-Length: 0
Content-Type: application/json
Client-Date: Thu, 28 Jun 2012 21:05:44 GMT
Client-Response-Num: 1
DESCRIPTION
"App::Presto" provides a command-line interface (CLI) for RESTful web
services. When looking for a way to interact with RESTful services
answers typically point to some horrible GUI or (on the complete
opposite end of the spectrum) just using "curl" directly on the
command-line. This tool attempts to find some sort of middle ground by
providing a quasi-DSL for interacting with a RESTful service in an
interactive way.
FEATURES
Basic HTTP methods
All HTTP methods are implemented as commands in presto. The URL that is
given is appended to the endpoint specified when presto is invoked as
shown in the SYNOPSIS above.
Request Building
If the endpoint contains a "*" character the URL fragment specified in
the GET/POST/etc command is inserted at that point. This allows you to
do things like auto-append a file extension to all URLs. For instance:
bash$ presto http://my-server.com*.json
http://my-server.com> GET /product/1
In this case, the full URL would be
"http://my-server.com/product/1.json". If no "*" is found in the URL,
the URL fragment is simply appended at the end of the endpoint.
All arguments after the first will be treated as query parameters (for
GET/HEAD/DELETE requests) or request content (for POST/PUT requests).
For instance:
http://my-server.com> GET /products limit=10 offset=20
# request goes to http://my-sever.com/products?limit=10&offset=20
http://my-server.com> POST /products '{"name":"A New Product"}'
# request goes to http://my-sever.com/products with the body as specified
You can also specify additional headers you would like included in the
request:
# the ":" is optional
http://my-server.com> header Accept: application/json
# shortcut for "header Content-Type application/json"
http://my-server.com> type application/json
# shortcut for "header Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
http://my-server.com> authorization Aladdin 'open sesame'
# view all headers
http://my-server.com> headers
# view specific header
http://my-server.com> header Authorization
# clear all headers
http://my-server.com> headers --clear
# unset specific header
http://my-server.com> header --unset Authorization
If you are creating form URL-encoded data, a shortcut has been made to
avoid having to manually URL-encode everything manually:
http://my-server.com> form foo=bar baz=1,2,3
# outputs
foo=bar&baz=1%2C2%2C3
Response Handling
By default, presto will just dump the response body to the screen after
a request is completed. There are additional options, however:
# dump full request/response to the screen (exactly as transmitted over the wire)
http://my-server.com> config verbose 1
# parse the response according to the content-type and use
# Data::Dumper to display it
http://my-server.com> config deserialize_response 1
# use something other than Data::Dumper to dump a parsed
# response body
http://my-server.com> config pretty_printer JSON
http://my-server.com> config pretty_printer Data::Dump
# send the output to a file (the '>' must not be followed by any white-space!)
http://my-server.com> GET /some-image.png >some-image.png
Pretty-printing can be especially helpful for making XML or JSON
response bodies more human-readable.
When "deserialize_response" is set, if the content-type of the response
is "text/html", the HTML is automatically stripped with
HTML::FormatText::WithLinks and displayed as formatted text.
If the request or response body is binary (using a simple heuristic like
the "-B" file-test operator), the output is not printed to STDOUT.
Instead, you may want to use output redirection as show above and send
the response body to a file.
http://my-server.com> GET /some-image.jpg >foo.jpg
Persistent Configuration
As demonstrated above, you can use the "config" command to change the
behavior of presto. These configuration options are persisted in a
config file specific to the endpoint provided at the command-line and
will be reloaded the next time you invoke presto with the same endpoint.
Current valid config keys are:
* verbose
Boolean, when enabled, dumps request/response to STDOUT (defaults to
"0")
* deserialize_response
Boolean, when enabled response body is parsed based on the
"Content-Type" header (defaults to "1")
* pretty_printer
Must be one of the supported modules (i.e. Data::Dumper or JSON).
Use tab completion to see currently supported values (defaults to
"JSON").
* binmode
Used to set encoding of STDIN and STDOUT handles (defaults to
"utf8")
TODO: provide a means for aliasing endpoints so that configuration is
shared across multiple endpoints.
History and Scripting
Just like configuration, command history is maintained separately for
each endpoint specified on the command-line and is persisted across
sessions (assuming you have a capable Term::Readline library installed).
You can interrogate the history using the (surprisingly named) "history"
command. It supports a small subset of the "bash" history command:
# dump all history
http://my-server.com> history
# dump last 5 entries
http://my-server.com> history 5
# delete specific history entries
http://my-server.com> history -d 4
# clear history
http://my-server.com> history -c
Presto also provides a way of saving and replaying bits of your command
history. Here are some examples:
# save all history to script file "my-script"
http://my-server.com> save my-script
# save the last 5 history entries
http://my-server.com> save my-script 5
# save entries 3-7
http://my-server.com> save my-script 3..7
To replay scripts:
http://my-server.com> source my-script
# prompt before each command
http://my-server.com> source -i my-script
Variable interpolation
At times (especially when working with scripts) it might be handy to use
elements from a previous response to affect a subsequent request.
Anything inside a balanced "$(...)" will be interpolated for you. For
instance, a very contrived example:
# hypothetical authentication protocal that returns a token in the response headers
http://my-server.com> POST /auth.json username=jdoe&password=s3cr3t
{"authenticated":true}
# see the authentication token
http://my-server.com> echo $(HEADER[X-Auth-Token])
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
If you need to include that in subsequent request, you can use the
"stash" feature:
# store the value
http://my-server.com> stash auth-token $(HEADER[X-Auth-Token])
# use the value later
http://my-server.com> header X-Auth-Token $(STASH[auth-token])
Those variable substitutions can be used anywhere in a command. "HEADER"
and "BODY" always refer to the most recent request while the "STASH" is
a persisted for the life of the process.
One useful feature for scripting is to prompt for user input. You can do
this by using the "PROMPT" pseudo-variable. The first set of brackets
specify the prompt value. The second (optional) set of brackets specify
the initial value. An example:
# collect the username/password from the user
http://my-server.com> stash username $(PROMPT[username:])
http://my-server.com> stash password $(PROMPT[password:])
# use the stashed values
http://my-server.com> authorization $(STASH[username]) $(STASH[password])
http://my-server.com> GET /$(STASH[username])/profile
# or use a value that was prompted for directly (without stashing it)
http://my-server.com> GET /products 'created_on=$(PROMPT[Created on (YYYY-MM-DD):])'
# you can also specify initial values
http://my-server.com> GET /products 'status=$(PROMPT[Product status:][active])'
You may also specify a local file to use as an argument to a command. An
example:
http://my-server.com> POST /products $(FILE[my-product.xml])
The file is assumed to be in the same encoding as the "binmode"
configuration. If it is using a different character set, you can specify
that in a second bracketed parameter:
http://my-server.com> POST /products $(FILE[my-product.xml][latin-1])
The contents of the file will be slurped, decoded and included as an
argument to the command as if you had typed it on the command-line
directly.
TODO: Allow data structure references (from "STASH" or even "BODY") to
be passed to a POST or PUT command which is then serialized based on the
content-type of the request before being sent over the wire.
(EXPERIMENTAL) Data::DPath integration
As an add-on to the variable interpolated described above, you can use
dpath expressions to further process the data returned from the REST
service. Another very contrived example:
http://my-server.com> GET /products.json
[{"id":"1","name":"My Product"},{"id":"2","name":"Another Product"}]
# issue a request to /product/2.json
http://my-server.com> GET /product/$(BODY/id[-1]).json
{"id":2,"name":"Another Product"}
In this example, anything after "BODY" (including the "/") is passed to
Data::DPath and the result is then injected in it's place (the target
data for "BODY" being the previous request's response data).
This feature will work on "$(STASH)" values as well.
CAVEAT EMPTOR
This is beta-quality code and while I use it in my own daily workflow,
it is likely riddled with horribly obvious bugs and missing
functionality (let alone undocumented features).
ACKNOWLEDGEMENTS
Much of this was inspired by resty <https://github.com/micha/resty>
which is a rather magical (aka convoluted) set of bash functions (at
least for this occassional bash programmer). After attempting to
understand and enhance resty <https://github.com/micha/resty>, I decided
to try my hand at creating something a little more perlish.
A big thank you to Shutterstock Images <http://shutterstock.com> for
allowing me to work on this on company time and release it to the CPAN.
AUTHOR
Brian Phillips <[email protected]>
COPYRIGHT AND LICENSE
This software is copyright (c) 2012 by Brian Phillips and Shutterstock
Images (http://shutterstock.com).
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.