cure-google-api-client 0.8.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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