cure-google-api-client 0.8.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +178 -0
  3. data/Gemfile +9 -0
  4. data/LICENSE +202 -0
  5. data/README.md +218 -0
  6. data/Rakefile +41 -0
  7. data/google-api-client.gemspec +43 -0
  8. data/lib/cacerts.pem +2183 -0
  9. data/lib/compat/multi_json.rb +19 -0
  10. data/lib/google/api_client.rb +750 -0
  11. data/lib/google/api_client/auth/compute_service_account.rb +28 -0
  12. data/lib/google/api_client/auth/file_storage.rb +59 -0
  13. data/lib/google/api_client/auth/installed_app.rb +126 -0
  14. data/lib/google/api_client/auth/jwt_asserter.rb +126 -0
  15. data/lib/google/api_client/auth/key_utils.rb +93 -0
  16. data/lib/google/api_client/auth/pkcs12.rb +41 -0
  17. data/lib/google/api_client/auth/storage.rb +102 -0
  18. data/lib/google/api_client/auth/storages/file_store.rb +58 -0
  19. data/lib/google/api_client/auth/storages/redis_store.rb +54 -0
  20. data/lib/google/api_client/batch.rb +326 -0
  21. data/lib/google/api_client/charset.rb +33 -0
  22. data/lib/google/api_client/client_secrets.rb +179 -0
  23. data/lib/google/api_client/discovery.rb +19 -0
  24. data/lib/google/api_client/discovery/api.rb +310 -0
  25. data/lib/google/api_client/discovery/media.rb +77 -0
  26. data/lib/google/api_client/discovery/method.rb +363 -0
  27. data/lib/google/api_client/discovery/resource.rb +156 -0
  28. data/lib/google/api_client/discovery/schema.rb +117 -0
  29. data/lib/google/api_client/environment.rb +42 -0
  30. data/lib/google/api_client/errors.rb +65 -0
  31. data/lib/google/api_client/gzip.rb +28 -0
  32. data/lib/google/api_client/logging.rb +32 -0
  33. data/lib/google/api_client/media.rb +259 -0
  34. data/lib/google/api_client/railtie.rb +18 -0
  35. data/lib/google/api_client/reference.rb +27 -0
  36. data/lib/google/api_client/request.rb +350 -0
  37. data/lib/google/api_client/result.rb +255 -0
  38. data/lib/google/api_client/service.rb +233 -0
  39. data/lib/google/api_client/service/batch.rb +110 -0
  40. data/lib/google/api_client/service/request.rb +144 -0
  41. data/lib/google/api_client/service/resource.rb +40 -0
  42. data/lib/google/api_client/service/result.rb +162 -0
  43. data/lib/google/api_client/service/simple_file_store.rb +151 -0
  44. data/lib/google/api_client/service/stub_generator.rb +61 -0
  45. data/lib/google/api_client/service_account.rb +21 -0
  46. data/lib/google/api_client/version.rb +26 -0
  47. data/spec/google/api_client/auth/storage_spec.rb +122 -0
  48. data/spec/google/api_client/auth/storages/file_store_spec.rb +40 -0
  49. data/spec/google/api_client/auth/storages/redis_store_spec.rb +70 -0
  50. data/spec/google/api_client/batch_spec.rb +248 -0
  51. data/spec/google/api_client/client_secrets_spec.rb +53 -0
  52. data/spec/google/api_client/discovery_spec.rb +708 -0
  53. data/spec/google/api_client/gzip_spec.rb +98 -0
  54. data/spec/google/api_client/media_spec.rb +178 -0
  55. data/spec/google/api_client/request_spec.rb +29 -0
  56. data/spec/google/api_client/result_spec.rb +207 -0
  57. data/spec/google/api_client/service_account_spec.rb +169 -0
  58. data/spec/google/api_client/service_spec.rb +618 -0
  59. data/spec/google/api_client/simple_file_store_spec.rb +133 -0
  60. data/spec/google/api_client_spec.rb +352 -0
  61. data/spec/spec_helper.rb +66 -0
  62. metadata +339 -0
@@ -0,0 +1,102 @@
1
+ # Copyright 2013 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'signet/oauth_2/client'
16
+
17
+ module Google
18
+ class APIClient
19
+ ##
20
+ # Represents cached OAuth 2 tokens stored on local disk in a
21
+ # JSON serialized file. Meant to resemble the serialized format
22
+ # http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.file.Storage-class.html
23
+ #
24
+ class Storage
25
+
26
+ AUTHORIZATION_URI = 'https://accounts.google.com/o/oauth2/auth'
27
+ TOKEN_CREDENTIAL_URI = 'https://accounts.google.com/o/oauth2/token'
28
+
29
+ # @return [Object] Storage object.
30
+ attr_accessor :store
31
+
32
+ # @return [Signet::OAuth2::Client]
33
+ attr_reader :authorization
34
+
35
+ ##
36
+ # Initializes the Storage object.
37
+ #
38
+ # @params [Object] Storage object
39
+ def initialize(store)
40
+ @store= store
41
+ @authorization = nil
42
+ end
43
+
44
+ ##
45
+ # Write the credentials to the specified store.
46
+ #
47
+ # @params [Signet::OAuth2::Client] authorization
48
+ # Optional authorization instance. If not provided, the authorization
49
+ # already associated with this instance will be written.
50
+ def write_credentials(authorization=nil)
51
+ @authorization = authorization if authorization
52
+ if @authorization.respond_to?(:refresh_token) && @authorization.refresh_token
53
+ store.write_credentials(credentials_hash)
54
+ end
55
+ end
56
+
57
+ ##
58
+ # Loads credentials and authorizes an client.
59
+ # @return [Object] Signet::OAuth2::Client or NIL
60
+ def authorize
61
+ @authorization = nil
62
+ cached_credentials = load_credentials
63
+ if cached_credentials && cached_credentials.size > 0
64
+ @authorization = Signet::OAuth2::Client.new(cached_credentials)
65
+ @authorization.issued_at = Time.at(cached_credentials['issued_at'].to_i)
66
+ self.refresh_authorization if @authorization.expired?
67
+ end
68
+ return @authorization
69
+ end
70
+
71
+ ##
72
+ # refresh credentials and save them to store
73
+ def refresh_authorization
74
+ authorization.refresh!
75
+ self.write_credentials
76
+ end
77
+
78
+ private
79
+
80
+ ##
81
+ # Attempt to read in credentials from the specified store.
82
+ def load_credentials
83
+ store.load_credentials
84
+ end
85
+
86
+ ##
87
+ # @return [Hash] with credentials
88
+ def credentials_hash
89
+ {
90
+ :access_token => authorization.access_token,
91
+ :authorization_uri => AUTHORIZATION_URI,
92
+ :client_id => authorization.client_id,
93
+ :client_secret => authorization.client_secret,
94
+ :expires_in => authorization.expires_in,
95
+ :refresh_token => authorization.refresh_token,
96
+ :token_credential_uri => TOKEN_CREDENTIAL_URI,
97
+ :issued_at => authorization.issued_at.to_i
98
+ }
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,58 @@
1
+ # Copyright 2013 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'json'
16
+
17
+ module Google
18
+ class APIClient
19
+ ##
20
+ # Represents cached OAuth 2 tokens stored on local disk in a
21
+ # JSON serialized file. Meant to resemble the serialized format
22
+ # http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.file.Storage-class.html
23
+ #
24
+ class FileStore
25
+
26
+ attr_accessor :path
27
+
28
+ ##
29
+ # Initializes the FileStorage object.
30
+ #
31
+ # @param [String] path
32
+ # Path to the credentials file.
33
+ def initialize(path)
34
+ @path= path
35
+ end
36
+
37
+ ##
38
+ # Attempt to read in credentials from the specified file.
39
+ def load_credentials
40
+ open(path, 'r') { |f| JSON.parse(f.read) }
41
+ rescue
42
+ nil
43
+ end
44
+
45
+ ##
46
+ # Write the credentials to the specified file.
47
+ #
48
+ # @param [Signet::OAuth2::Client] authorization
49
+ # Optional authorization instance. If not provided, the authorization
50
+ # already associated with this instance will be written.
51
+ def write_credentials(credentials_hash)
52
+ open(self.path, 'w+') do |f|
53
+ f.write(credentials_hash.to_json)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,54 @@
1
+ # Copyright 2013 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'json'
16
+
17
+ module Google
18
+ class APIClient
19
+ class RedisStore
20
+
21
+ DEFAULT_REDIS_CREDENTIALS_KEY = "google_api_credentials"
22
+
23
+ attr_accessor :redis
24
+
25
+ ##
26
+ # Initializes the RedisStore object.
27
+ #
28
+ # @params [Object] Redis instance
29
+ def initialize(redis, key = nil)
30
+ @redis= redis
31
+ @redis_credentials_key = key
32
+ end
33
+
34
+ ##
35
+ # Attempt to read in credentials from redis.
36
+ def load_credentials
37
+ credentials = redis.get redis_credentials_key
38
+ JSON.parse(credentials) if credentials
39
+ end
40
+
41
+ def redis_credentials_key
42
+ @redis_credentials_key || DEFAULT_REDIS_CREDENTIALS_KEY
43
+ end
44
+
45
+ ##
46
+ # Write the credentials to redis.
47
+ #
48
+ # @params [Hash] credentials
49
+ def write_credentials(credentials_hash)
50
+ redis.set(redis_credentials_key, credentials_hash.to_json)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,326 @@
1
+ # Copyright 2012 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'addressable/uri'
16
+ require 'google/api_client/reference'
17
+ require 'securerandom'
18
+
19
+ module Google
20
+ class APIClient
21
+
22
+ ##
23
+ # Helper class to contain a response to an individual batched call.
24
+ #
25
+ # @api private
26
+ class BatchedCallResponse
27
+ # @return [String] UUID of the call
28
+ attr_reader :call_id
29
+ # @return [Fixnum] HTTP status code
30
+ attr_accessor :status
31
+ # @return [Hash] HTTP response headers
32
+ attr_accessor :headers
33
+ # @return [String] HTTP response body
34
+ attr_accessor :body
35
+
36
+ ##
37
+ # Initialize the call response
38
+ #
39
+ # @param [String] call_id
40
+ # UUID of the original call
41
+ # @param [Fixnum] status
42
+ # HTTP status
43
+ # @param [Hash] headers
44
+ # HTTP response headers
45
+ # @param [#read, #to_str] body
46
+ # Response body
47
+ def initialize(call_id, status = nil, headers = nil, body = nil)
48
+ @call_id, @status, @headers, @body = call_id, status, headers, body
49
+ end
50
+ end
51
+
52
+ # Wraps multiple API calls into a single over-the-wire HTTP request.
53
+ #
54
+ # @example
55
+ #
56
+ # client = Google::APIClient.new
57
+ # urlshortener = client.discovered_api('urlshortener')
58
+ # batch = Google::APIClient::BatchRequest.new do |result|
59
+ # puts result.data
60
+ # end
61
+ #
62
+ # batch.add(:api_method => urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/foo' })
63
+ # batch.add(:api_method => urlshortener.url.insert, :body_object => { 'longUrl' => 'http://example.com/bar' })
64
+ #
65
+ # client.execute(batch)
66
+ #
67
+ class BatchRequest < Request
68
+ BATCH_BOUNDARY = "-----------RubyApiBatchRequest".freeze
69
+
70
+ # @api private
71
+ # @return [Array<(String,Google::APIClient::Request,Proc)] List of API calls in the batch
72
+ attr_reader :calls
73
+
74
+ ##
75
+ # Creates a new batch request.
76
+ #
77
+ # @param [Hash] options
78
+ # Set of options for this request.
79
+ # @param [Proc] block
80
+ # Callback for every call's response. Won't be called if a call defined
81
+ # a callback of its own.
82
+ #
83
+ # @return [Google::APIClient::BatchRequest]
84
+ # The constructed object.
85
+ #
86
+ # @yield [Google::APIClient::Result]
87
+ # block to be called when result ready
88
+ def initialize(options = {}, &block)
89
+ @calls = []
90
+ @global_callback = nil
91
+ @global_callback = block if block_given?
92
+ @last_auto_id = 0
93
+
94
+ @base_id = SecureRandom.uuid
95
+
96
+ options[:uri] ||= 'https://www.googleapis.com/batch'
97
+ options[:http_method] ||= 'POST'
98
+
99
+ super options
100
+ end
101
+
102
+ ##
103
+ # Add a new call to the batch request.
104
+ # Each call must have its own call ID; if not provided, one will
105
+ # automatically be generated, avoiding collisions. If duplicate call IDs
106
+ # are provided, an error will be thrown.
107
+ #
108
+ # @param [Hash, Google::APIClient::Request] call
109
+ # the call to be added.
110
+ # @param [String] call_id
111
+ # the ID to be used for this call. Must be unique
112
+ # @param [Proc] block
113
+ # callback for this call's response.
114
+ #
115
+ # @return [Google::APIClient::BatchRequest]
116
+ # the BatchRequest, for chaining
117
+ #
118
+ # @yield [Google::APIClient::Result]
119
+ # block to be called when result ready
120
+ def add(call, call_id = nil, &block)
121
+ unless call.kind_of?(Google::APIClient::Reference)
122
+ call = Google::APIClient::Reference.new(call)
123
+ end
124
+ call_id ||= new_id
125
+ if @calls.assoc(call_id)
126
+ raise BatchError,
127
+ 'A call with this ID already exists: %s' % call_id
128
+ end
129
+ callback = block_given? ? block : @global_callback
130
+ @calls << [call_id, call, callback]
131
+ return self
132
+ end
133
+
134
+ ##
135
+ # Processes the HTTP response to the batch request, issuing callbacks.
136
+ #
137
+ # @api private
138
+ #
139
+ # @param [Faraday::Response] response
140
+ # the HTTP response.
141
+ def process_http_response(response)
142
+ content_type = find_header('Content-Type', response.headers)
143
+ m = /.*boundary=(.+)/.match(content_type)
144
+ if m
145
+ boundary = m[1]
146
+ parts = response.body.split(/--#{Regexp.escape(boundary)}/)
147
+ parts = parts[1...-1]
148
+ parts.each do |part|
149
+ call_response = deserialize_call_response(part)
150
+ _, call, callback = @calls.assoc(call_response.call_id)
151
+ result = Google::APIClient::Result.new(call, call_response)
152
+ callback.call(result) if callback
153
+ end
154
+ end
155
+ Google::APIClient::Result.new(self, response)
156
+ end
157
+
158
+ ##
159
+ # Return the request body for the BatchRequest's HTTP request.
160
+ #
161
+ # @api private
162
+ #
163
+ # @return [String]
164
+ # the request body.
165
+ def to_http_request
166
+ if @calls.nil? || @calls.empty?
167
+ raise BatchError, 'Cannot make an empty batch request'
168
+ end
169
+ parts = @calls.map {|(call_id, call, _callback)| serialize_call(call_id, call)}
170
+ build_multipart(parts, 'multipart/mixed', BATCH_BOUNDARY)
171
+ super
172
+ end
173
+
174
+
175
+ protected
176
+
177
+ ##
178
+ # Helper method to find a header from its name, regardless of case.
179
+ #
180
+ # @api private
181
+ #
182
+ # @param [String] name
183
+ # the name of the header to find.
184
+ # @param [Hash] headers
185
+ # the hash of headers and their values.
186
+ #
187
+ # @return [String]
188
+ # the value of the desired header.
189
+ def find_header(name, headers)
190
+ _, header = headers.detect do |h, v|
191
+ h.downcase == name.downcase
192
+ end
193
+ return header
194
+ end
195
+
196
+ ##
197
+ # Create a new call ID. Uses an auto-incrementing, conflict-avoiding ID.
198
+ #
199
+ # @api private
200
+ #
201
+ # @return [String]
202
+ # the new, unique ID.
203
+ def new_id
204
+ @last_auto_id += 1
205
+ while @calls.assoc(@last_auto_id)
206
+ @last_auto_id += 1
207
+ end
208
+ return @last_auto_id.to_s
209
+ end
210
+
211
+ ##
212
+ # Convert a Content-ID header value to an id. Presumes the Content-ID
213
+ # header conforms to the format that id_to_header() returns.
214
+ #
215
+ # @api private
216
+ #
217
+ # @param [String] header
218
+ # Content-ID header value.
219
+ #
220
+ # @return [String]
221
+ # The extracted ID value.
222
+ def header_to_id(header)
223
+ if !header.start_with?('<') || !header.end_with?('>') ||
224
+ !header.include?('+')
225
+ raise BatchError, 'Invalid value for Content-ID: "%s"' % header
226
+ end
227
+
228
+ _base, call_id = header[1...-1].split('+')
229
+ return Addressable::URI.unencode(call_id)
230
+ end
231
+
232
+ ##
233
+ # Auxiliary method to split the headers from the body in an HTTP response.
234
+ #
235
+ # @api private
236
+ #
237
+ # @param [String] response
238
+ # the response to parse.
239
+ #
240
+ # @return [Array<Hash>, String]
241
+ # the headers and the body, separately.
242
+ def split_headers_and_body(response)
243
+ headers = {}
244
+ payload = response.lstrip
245
+ while payload
246
+ line, payload = payload.split("\n", 2)
247
+ line.sub!(/\s+\z/, '')
248
+ break if line.empty?
249
+ match = /\A([^:]+):\s*/.match(line)
250
+ if match
251
+ headers[match[1]] = match.post_match
252
+ else
253
+ raise BatchError, 'Invalid header line in response: %s' % line
254
+ end
255
+ end
256
+ return headers, payload
257
+ end
258
+
259
+ ##
260
+ # Convert a single batched response into a BatchedCallResponse object.
261
+ #
262
+ # @api private
263
+ #
264
+ # @param [String] call_response
265
+ # the request to deserialize.
266
+ #
267
+ # @return [Google::APIClient::BatchedCallResponse]
268
+ # the parsed and converted response.
269
+ def deserialize_call_response(call_response)
270
+ outer_headers, outer_body = split_headers_and_body(call_response)
271
+ status_line, payload = outer_body.split("\n", 2)
272
+ _protocol, status, _reason = status_line.split(' ', 3)
273
+
274
+ headers, body = split_headers_and_body(payload)
275
+ content_id = find_header('Content-ID', outer_headers)
276
+ call_id = header_to_id(content_id)
277
+ return BatchedCallResponse.new(call_id, status.to_i, headers, body)
278
+ end
279
+
280
+ ##
281
+ # Serialize a single batched call for assembling the multipart message
282
+ #
283
+ # @api private
284
+ #
285
+ # @param [Google::APIClient::Request] call
286
+ # the call to serialize.
287
+ #
288
+ # @return [Faraday::UploadIO]
289
+ # the serialized request
290
+ def serialize_call(call_id, call)
291
+ method, uri, headers, body = call.to_http_request
292
+ request = "#{method.to_s.upcase} #{Addressable::URI.parse(uri).request_uri} HTTP/1.1"
293
+ headers.each do |header, value|
294
+ request << "\r\n%s: %s" % [header, value]
295
+ end
296
+ if body
297
+ # TODO - CompositeIO if body is a stream
298
+ request << "\r\n\r\n"
299
+ if body.respond_to?(:read)
300
+ request << body.read
301
+ else
302
+ request << body.to_s
303
+ end
304
+ end
305
+ Faraday::UploadIO.new(StringIO.new(request), 'application/http', 'ruby-api-request', 'Content-ID' => id_to_header(call_id))
306
+ end
307
+
308
+ ##
309
+ # Convert an id to a Content-ID header value.
310
+ #
311
+ # @api private
312
+ #
313
+ # @param [String] call_id
314
+ # identifier of individual call.
315
+ #
316
+ # @return [String]
317
+ # A Content-ID header with the call_id encoded into it. A UUID is
318
+ # prepended to the value because Content-ID headers are supposed to be
319
+ # universally unique.
320
+ def id_to_header(call_id)
321
+ return '<%s+%s>' % [@base_id, Addressable::URI.encode(call_id)]
322
+ end
323
+
324
+ end
325
+ end
326
+ end