google-api-client 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.1.1
2
+
3
+ * substantial improvements to the command line interface
4
+
1
5
  == 0.1.0
2
6
 
3
7
  * initial release
data/bin/google-api CHANGED
@@ -10,96 +10,28 @@ OAUTH_SERVER_PORT = 12736
10
10
 
11
11
  require 'rubygems'
12
12
  require 'optparse'
13
+ require 'httpadapter'
14
+ require 'webrick'
13
15
  require 'google/api_client/version'
14
16
  require 'google/api_client'
15
17
 
16
18
  ARGV.unshift('--help') if ARGV.empty?
17
19
 
18
- command = 'execute'
19
- options = {}
20
- OptionParser.new do |opts|
21
- opts.banner =
22
- "Usage: google-api <rpcname> [options] -- <parameters>\n" +
23
- " or: google-api --oauth-login=<scope> [options]\n" +
24
- " or: google-api --interactive=<service> [options]\n" +
25
- " or: google-api --fuzz [options]"
20
+ module Google
21
+ class APIClient
22
+ class CLI
23
+ # Used for oauth login
24
+ class OAuthVerifierServlet < WEBrick::HTTPServlet::AbstractServlet
25
+ attr_reader :verifier
26
26
 
27
- opts.separator ""
28
-
29
- opts.on(
30
- "--oauth-login <scope>", String, "Authorize for the scope") do |s|
31
- if command != 'execute'
32
- STDERR.puts("Ambiguous command: #{command}")
33
- exit(1)
34
- end
35
- command = 'oauth-login'
36
- options[:scope] = s
37
- end
38
- opts.on(
39
- "-s", "--service <name>", String, "Perform discovery on service") do |s|
40
- options[:service_name] = s
41
- end
42
- opts.on(
43
- "-i", "--interactive <name>", String, "Start interactive session") do |s|
44
- if command != 'execute'
45
- STDERR.puts("Ambiguous command: #{command}")
46
- exit(1)
47
- end
48
- command = 'interactive'
49
- options[:service_name] = s
50
- end
51
- opts.on(
52
- "--service-version <id>", String, "Select service version") do |id|
53
- options[:service_version] = id
54
- end
55
- opts.on(
56
- "--content-type <format>", String, "Content-Type for request") do |f|
57
- # Resolve content type shortcuts
58
- case f
59
- when 'json'
60
- f = 'application/json'
61
- when 'xml'
62
- f = 'application/xml'
63
- when 'atom'
64
- f = 'application/atom+xml'
65
- when 'rss'
66
- f = 'application/rss+xml'
67
- end
68
- options[:content_type] = f
69
- end
70
- opts.on("--fuzz [rpcname]", String, "Fuzz an API or endpoint") do |rpcname|
71
- if command != 'execute'
72
- STDERR.puts("Ambiguous command: #{command}")
73
- exit(1)
74
- end
75
- command = 'fuzz'
76
- options[:fuzz] = rpcname
77
- end
78
-
79
- opts.on_tail("-v", "--verbose", "Run verbosely") do |v|
80
- options[:verbose] = v
81
- end
82
- opts.on_tail("-h", "--help", "Show this message") do
83
- puts opts
84
- exit
85
- end
86
- opts.on_tail("--version", "Show version") do
87
- puts "google-api-client (#{Google::APIClient::VERSION::STRING})"
88
- exit
89
- end
90
- end.parse!
91
-
92
- if command == 'oauth-login' # Guard to keep start-up time short
93
- require 'webrick'
94
- # Used for oauth login
95
- class OAuthVerifierServlet < WEBrick::HTTPServlet::AbstractServlet
96
- def do_GET(request, response)
97
- $verifier ||= Addressable::URI.unencode_component(
98
- request.request_uri.to_s[/\?.*oauth_verifier=([^&$]+)(&|$)/, 1]
99
- )
100
- response.status = WEBrick::HTTPStatus::RC_ACCEPTED
101
- # This javascript will auto-close the tab after the verifier is obtained.
102
- response.body = <<-HTML
27
+ def do_GET(request, response)
28
+ $verifier ||= Addressable::URI.unencode_component(
29
+ request.request_uri.to_s[/\?.*oauth_verifier=([^&$]+)(&|$)/, 1]
30
+ )
31
+ response.status = WEBrick::HTTPStatus::RC_ACCEPTED
32
+ # This javascript will auto-close the tab after the
33
+ # verifier is obtained.
34
+ response.body = <<-HTML
103
35
  <html>
104
36
  <head>
105
37
  <script>
@@ -115,181 +47,303 @@ if command == 'oauth-login' # Guard to keep start-up time short
115
47
  </body>
116
48
  </html>
117
49
  HTML
118
- self.instance_variable_get('@server').stop
119
- end
120
- end
121
- end
50
+ # Eww, hack!
51
+ server = self.instance_variable_get('@server')
52
+ server.stop if server
53
+ end
54
+ end
122
55
 
123
- def oauth_login(options={})
124
- require 'signet/oauth_1/client'
125
- require 'launchy'
126
- require 'yaml'
127
- $verifier = nil
128
- logger = WEBrick::Log.new('/dev/null') # TODO(bobaman): Cross-platform?
129
- server = WEBrick::HTTPServer.new(
130
- :Port => OAUTH_SERVER_PORT,
131
- :Logger => logger,
132
- :AccessLog => logger
133
- )
134
- trap("INT") { server.shutdown }
56
+ # Initialize with default parameter values
57
+ def initialize(argv)
58
+ @options = {
59
+ :command => 'execute',
60
+ :rpcname => nil,
61
+ :verbose => false
62
+ }
63
+ @argv = argv.clone
64
+ if @argv.first =~ /^[a-z0-9][a-z0-9_-]*$/i
65
+ self.options[:command] = @argv.shift
66
+ end
67
+ if @argv.first =~ /^[a-z0-9_-]+\.[a-z0-9_\.-]+$/i
68
+ self.options[:rpcname] = @argv.shift
69
+ end
70
+ end
135
71
 
136
- server.mount("/", OAuthVerifierServlet)
72
+ attr_reader :options
73
+ attr_reader :argv
137
74
 
138
- oauth_client = Signet::OAuth1::Client.new(
139
- :temporary_credential_uri =>
140
- 'https://www.google.com/accounts/OAuthGetRequestToken',
141
- :authorization_uri =>
142
- 'https://www.google.com/accounts/OAuthAuthorizeToken',
143
- :token_credential_uri =>
144
- 'https://www.google.com/accounts/OAuthGetAccessToken',
145
- :client_credential_key => 'anonymous',
146
- :client_credential_secret => 'anonymous',
147
- :callback => "http://localhost:#{OAUTH_SERVER_PORT}/"
148
- )
149
- scope = options[:scope]
150
- # Special cases
151
- case scope
152
- when "https://www.googleapis.com/auth/buzz",
153
- "https://www.googleapis.com/auth/buzz.readonly"
154
- oauth_client.authorization_uri =
155
- 'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?' +
156
- "domain=#{oauth_client.client_credential_key}&" +
157
- "scope=#{scope}&" +
158
- "xoauth_displayname=Google%20API%20Client"
159
- end
160
- oauth_client.fetch_temporary_credential!(:additional_parameters => {
161
- :scope => scope,
162
- :xoauth_displayname => 'Google API Client'
163
- })
75
+ def command
76
+ return self.options[:command]
77
+ end
164
78
 
165
- # Launch browser
166
- Launchy::Browser.run(oauth_client.authorization_uri.to_s)
79
+ def rpcname
80
+ return self.options[:rpcname]
81
+ end
167
82
 
168
- server.start
169
- oauth_client.fetch_token_credential!(:verifier => $verifier)
170
- config = {
171
- "scope" => scope,
172
- "client_credential_key" => oauth_client.client_credential_key,
173
- "client_credential_secret" => oauth_client.client_credential_secret,
174
- "token_credential_key" => oauth_client.token_credential_key,
175
- "token_credential_secret" => oauth_client.token_credential_secret
176
- }
177
- config_file = File.expand_path('~/.google-api.yaml')
178
- open(config_file, 'w') { |file| file.write(YAML.dump(config)) }
179
- exit(0)
180
- end
83
+ def parser
84
+ @parser ||= OptionParser.new do |opts|
85
+ opts.banner = "Usage: google-api " +
86
+ "(execute <rpcname> | [command]) [options] [-- <parameters>]"
181
87
 
182
- def execute(options={})
183
- require 'signet/oauth_1/client'
184
- require 'yaml'
185
- config_file = File.expand_path('~/.google-api.yaml')
186
- signed = File.exist?(config_file)
187
- rpcname = ARGV.detect { |p| p =~ /^[a-z0-9_-]+\.[a-z0-9_\.-]+$/i }
188
- if rpcname
189
- ARGV.delete(rpcname)
190
- else
191
- STDERR.puts('Could not find rpcname.')
192
- exit(1)
193
- end
194
- service_name = options[:service_name] || rpcname[/^([^\.]+)\./, 1]
195
- client = Google::APIClient.new(
196
- :service => service_name,
197
- :authorization => :oauth_1
198
- )
199
- if signed
200
- if !client.authorization.kind_of?(Signet::OAuth1::Client)
201
- STDERR.puts(
202
- "Unexpected authorization mechanism: #{client.authorization.class}"
203
- )
204
- exit(1)
205
- end
206
- config = open(config_file, 'r') { |file| YAML.load(file.read) }
207
- client.authorization.client_credential_key =
208
- config["client_credential_key"]
209
- client.authorization.client_credential_secret =
210
- config["client_credential_secret"]
211
- client.authorization.token_credential_key =
212
- config["token_credential_key"]
213
- client.authorization.token_credential_secret =
214
- config["token_credential_secret"]
215
- end
216
- service_version =
217
- options[:service_version] ||
218
- client.latest_service_version(service_name).version
219
- service = client.discovered_service(service_name, service_version)
220
- method = service.to_h[rpcname]
221
- if !method
222
- STDERR.puts(
223
- "Method #{rpcname} does not exist for " +
224
- "#{service_name}-#{service_version}."
225
- )
226
- exit(1)
227
- end
228
- parameters = ARGV.inject({}) do |accu, pair|
229
- name, value = pair.split('=', 2)
230
- accu[name] = value
231
- accu
232
- end
233
- request_body = ''
234
- input_streams, _, _ = IO.select([STDIN], [], [], 0)
235
- request_body = STDIN.read || '' if input_streams
236
- headers = []
237
- if options[:content_type]
238
- headers << ['Content-Type', options[:content_type]]
239
- elsif request_body
240
- # Default to JSON
241
- headers << ['Content-Type', 'application/json']
242
- end
243
- response = client.execute(
244
- method, parameters, request_body, headers, {:signed => signed}
245
- )
246
- status, headers, body = response
247
- puts body
248
- exit(0)
249
- end
88
+ opts.separator "\nAvailable options:"
250
89
 
251
- def interactive(options={})
252
- require 'signet/oauth_1/client'
253
- require 'yaml'
254
- config_file = File.expand_path('~/.google-api.yaml')
255
- signed = File.exist?(config_file)
90
+ opts.on(
91
+ "--scope <scope>", String, "Set the OAuth scope") do |s|
92
+ options[:scope] = s
93
+ end
94
+ opts.on(
95
+ "-s", "--service <name>", String,
96
+ "Perform discovery on service") do |s|
97
+ options[:service_name] = s
98
+ end
99
+ opts.on(
100
+ "--service-version <id>", String,
101
+ "Select service version") do |id|
102
+ options[:service_version] = id
103
+ end
104
+ opts.on(
105
+ "--content-type <format>", String,
106
+ "Content-Type for request") do |f|
107
+ # Resolve content type shortcuts
108
+ case f
109
+ when 'json'
110
+ f = 'application/json'
111
+ when 'xml'
112
+ f = 'application/xml'
113
+ when 'atom'
114
+ f = 'application/atom+xml'
115
+ when 'rss'
116
+ f = 'application/rss+xml'
117
+ end
118
+ options[:content_type] = f
119
+ end
256
120
 
257
- $client = Google::APIClient.new(
258
- :service => options[:service_name],
259
- :authorization => (signed ? :oauth_1 : nil)
260
- )
121
+ opts.on("-v", "--verbose", "Run verbosely") do |v|
122
+ options[:verbose] = v
123
+ end
124
+ opts.on("-h", "--help", "Show this message") do
125
+ puts opts
126
+ exit
127
+ end
128
+ opts.on("--version", "Show version") do
129
+ puts "google-api-client (#{Google::APIClient::VERSION::STRING})"
130
+ exit
131
+ end
261
132
 
262
- if signed
263
- if $client.authorization &&
264
- !$client.authorization.kind_of?(Signet::OAuth1::Client)
265
- STDERR.puts(
266
- "Unexpected authorization mechanism: #{$client.authorization.class}"
267
- )
268
- exit(1)
269
- end
270
- config = open(config_file, 'r') { |file| YAML.load(file.read) }
271
- $client.authorization.client_credential_key =
272
- config["client_credential_key"]
273
- $client.authorization.client_credential_secret =
274
- config["client_credential_secret"]
275
- $client.authorization.token_credential_key =
276
- config["token_credential_key"]
277
- $client.authorization.token_credential_secret =
278
- config["token_credential_secret"]
279
- end
133
+ opts.separator(
134
+ "\nAvailable commands:\n" +
135
+ " oauth-login Log a user into an API\n" +
136
+ " list List the methods available for a service\n" +
137
+ " execute Execute a method on the API\n" +
138
+ " irb Start an interactive client session"
139
+ )
140
+ end
141
+ end
280
142
 
281
- require 'irb'
282
- IRB.start(__FILE__)
283
- end
143
+ def parse!
144
+ self.parser.parse!(self.argv)
145
+ self.send(self.command.gsub(/-/, "_").to_sym)
146
+ end
147
+
148
+ def oauth_login
149
+ require 'signet/oauth_1/client'
150
+ require 'launchy'
151
+ require 'yaml'
152
+ $verifier = nil
153
+ logger = WEBrick::Log.new('/dev/null') # TODO(bobaman): Cross-platform?
154
+ server = WEBrick::HTTPServer.new(
155
+ :Port => OAUTH_SERVER_PORT,
156
+ :Logger => logger,
157
+ :AccessLog => logger
158
+ )
159
+ trap("INT") { server.shutdown }
160
+
161
+ server.mount("/", OAuthVerifierServlet)
284
162
 
285
- def fuzz(options={})
286
- STDERR.puts('API fuzzing not yet supported.')
287
- if rpcname
288
- # Fuzz just one method
289
- else
290
- # Fuzz the entire API
163
+ oauth_client = Signet::OAuth1::Client.new(
164
+ :temporary_credential_uri =>
165
+ 'https://www.google.com/accounts/OAuthGetRequestToken',
166
+ :authorization_uri =>
167
+ 'https://www.google.com/accounts/OAuthAuthorizeToken',
168
+ :token_credential_uri =>
169
+ 'https://www.google.com/accounts/OAuthGetAccessToken',
170
+ :client_credential_key => 'anonymous',
171
+ :client_credential_secret => 'anonymous',
172
+ :callback => "http://localhost:#{OAUTH_SERVER_PORT}/"
173
+ )
174
+ scope = options[:scope]
175
+ # Special cases
176
+ case scope
177
+ when "https://www.googleapis.com/auth/buzz",
178
+ "https://www.googleapis.com/auth/buzz.readonly"
179
+ oauth_client.authorization_uri =
180
+ 'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?' +
181
+ "domain=#{oauth_client.client_credential_key}&" +
182
+ "scope=#{scope}&" +
183
+ "xoauth_displayname=Google%20API%20Client"
184
+ end
185
+ oauth_client.fetch_temporary_credential!(:additional_parameters => {
186
+ :scope => scope,
187
+ :xoauth_displayname => 'Google API Client'
188
+ })
189
+
190
+ # Launch browser
191
+ Launchy::Browser.run(oauth_client.authorization_uri.to_s)
192
+
193
+ server.start
194
+ oauth_client.fetch_token_credential!(:verifier => $verifier)
195
+ config = {
196
+ "scope" => scope,
197
+ "client_credential_key" => oauth_client.client_credential_key,
198
+ "client_credential_secret" => oauth_client.client_credential_secret,
199
+ "token_credential_key" => oauth_client.token_credential_key,
200
+ "token_credential_secret" => oauth_client.token_credential_secret
201
+ }
202
+ config_file = File.expand_path('~/.google-api.yaml')
203
+ open(config_file, 'w') { |file| file.write(YAML.dump(config)) }
204
+ exit(0)
205
+ end
206
+
207
+ def list
208
+ service_name = options[:service_name]
209
+ client = Google::APIClient.new(
210
+ :service => service_name,
211
+ :authorization => nil
212
+ )
213
+ service_version =
214
+ options[:service_version] ||
215
+ client.latest_service_version(service_name).version
216
+ service = client.discovered_service(service_name, service_version)
217
+ rpcnames = service.to_h.keys
218
+ puts rpcnames.sort.join("\n")
219
+ exit(0)
220
+ end
221
+
222
+ def execute
223
+ require 'signet/oauth_1/client'
224
+ require 'yaml'
225
+ config_file = File.expand_path('~/.google-api.yaml')
226
+ signed = File.exist?(config_file)
227
+ if !self.rpcname
228
+ STDERR.puts('No rpcname supplied.')
229
+ exit(1)
230
+ end
231
+ service_name = options[:service_name] || self.rpcname[/^([^\.]+)\./, 1]
232
+ client = Google::APIClient.new(
233
+ :service => service_name,
234
+ :authorization => :oauth_1
235
+ )
236
+ if signed
237
+ if !client.authorization.kind_of?(Signet::OAuth1::Client)
238
+ STDERR.puts(
239
+ "Unexpected authorization mechanism: " +
240
+ "#{client.authorization.class}"
241
+ )
242
+ exit(1)
243
+ end
244
+ config = open(config_file, 'r') { |file| YAML.load(file.read) }
245
+ client.authorization.client_credential_key =
246
+ config["client_credential_key"]
247
+ client.authorization.client_credential_secret =
248
+ config["client_credential_secret"]
249
+ client.authorization.token_credential_key =
250
+ config["token_credential_key"]
251
+ client.authorization.token_credential_secret =
252
+ config["token_credential_secret"]
253
+ end
254
+ service_version =
255
+ options[:service_version] ||
256
+ client.latest_service_version(service_name).version
257
+ service = client.discovered_service(service_name, service_version)
258
+ method = service.to_h[self.rpcname]
259
+ if !method
260
+ STDERR.puts(
261
+ "Method #{self.rpcname} does not exist for " +
262
+ "#{service_name}-#{service_version}."
263
+ )
264
+ exit(1)
265
+ end
266
+ parameters = self.argv.inject({}) do |accu, pair|
267
+ name, value = pair.split('=', 2)
268
+ accu[name] = value
269
+ accu
270
+ end
271
+ request_body = ''
272
+ input_streams, _, _ = IO.select([STDIN], [], [], 0)
273
+ request_body = STDIN.read || '' if input_streams
274
+ headers = []
275
+ if options[:content_type]
276
+ headers << ['Content-Type', options[:content_type]]
277
+ elsif request_body
278
+ # Default to JSON
279
+ headers << ['Content-Type', 'application/json']
280
+ end
281
+ begin
282
+ response = client.execute(
283
+ method, parameters, request_body, headers, {:signed => signed}
284
+ )
285
+ status, headers, body = response
286
+ puts body
287
+ exit(0)
288
+ rescue ArgumentError => e
289
+ puts e.message
290
+ exit(1)
291
+ end
292
+ end
293
+
294
+ def irb
295
+ require 'signet/oauth_1/client'
296
+ require 'yaml'
297
+ require 'irb'
298
+ config_file = File.expand_path('~/.google-api.yaml')
299
+ signed = File.exist?(config_file)
300
+
301
+ $client = Google::APIClient.new(
302
+ :service => options[:service_name],
303
+ :authorization => (signed ? :oauth_1 : nil)
304
+ )
305
+
306
+ if signed
307
+ if $client.authorization &&
308
+ !$client.authorization.kind_of?(Signet::OAuth1::Client)
309
+ STDERR.puts(
310
+ "Unexpected authorization mechanism: " +
311
+ "#{$client.authorization.class}"
312
+ )
313
+ exit(1)
314
+ end
315
+ config = open(config_file, 'r') { |file| YAML.load(file.read) }
316
+ $client.authorization.client_credential_key =
317
+ config["client_credential_key"]
318
+ $client.authorization.client_credential_secret =
319
+ config["client_credential_secret"]
320
+ $client.authorization.token_credential_key =
321
+ config["token_credential_key"]
322
+ $client.authorization.token_credential_secret =
323
+ config["token_credential_secret"]
324
+ end
325
+
326
+ # Otherwise IRB will misinterpret command-line options
327
+ ARGV.clear
328
+ IRB.start(__FILE__)
329
+ end
330
+
331
+ def fuzz
332
+ STDERR.puts('API fuzzing not yet supported.')
333
+ if self.rpcname
334
+ # Fuzz just one method
335
+ else
336
+ # Fuzz the entire API
337
+ end
338
+ exit(1)
339
+ end
340
+
341
+ def help
342
+ puts self.parser
343
+ exit(0)
344
+ end
345
+ end
291
346
  end
292
- exit(1)
293
347
  end
294
348
 
295
- self.send(command.gsub(/-/, "_").to_sym, options)
349
+ Google::APIClient::CLI.new(ARGV).parse!
@@ -17,7 +17,7 @@ module Google
17
17
  module VERSION
18
18
  MAJOR = 0
19
19
  MINOR = 1
20
- TINY = 0
20
+ TINY = 1
21
21
 
22
22
  STRING = [MAJOR, MINOR, TINY].join('.')
23
23
  end
@@ -14,6 +14,7 @@
14
14
 
15
15
  require 'httpadapter'
16
16
  require 'json'
17
+ require 'stringio'
17
18
 
18
19
  require 'google/api_client/discovery'
19
20
 
data/tasks/spec.rake CHANGED
@@ -18,6 +18,7 @@ namespace :spec do
18
18
  end
19
19
  t.rcov_opts = [
20
20
  '--exclude', 'spec',
21
+ '--exclude', '\\.rvm\\/gems',
21
22
  '--exclude', '1\\.8\\/gems',
22
23
  '--exclude', '1\\.9\\/gems'
23
24
  ]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google-api-client
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 0
10
- version: 0.1.0
9
+ - 1
10
+ version: 0.1.1
11
11
  platform: ruby
12
12
  authors: []
13
13
 
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-13 00:00:00 -07:00
18
+ date: 2010-10-21 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency