google-api-client 0.6.4 → 0.7.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +13 -3
- data/README.md +133 -97
- data/Rakefile +1 -1
- data/lib/cacerts.pem +2183 -0
- data/lib/google/api_client/auth/compute_service_account.rb +28 -0
- data/lib/google/api_client/auth/file_storage.rb +87 -0
- data/lib/google/api_client/auth/installed_app.rb +8 -1
- data/lib/google/api_client/auth/jwt_asserter.rb +0 -11
- data/lib/google/api_client/client_secrets.rb +1 -1
- data/lib/google/api_client/discovery/method.rb +1 -0
- data/lib/google/api_client/errors.rb +11 -0
- data/lib/google/api_client/gzip.rb +28 -0
- data/lib/google/api_client/media.rb +80 -5
- data/lib/google/api_client/request.rb +15 -9
- data/lib/google/api_client/service/batch.rb +103 -0
- data/lib/google/api_client/service/request.rb +144 -0
- data/lib/google/api_client/service/resource.rb +40 -0
- data/lib/google/api_client/service/result.rb +162 -0
- data/lib/google/api_client/service/simple_file_store.rb +151 -0
- data/lib/google/api_client/service/stub_generator.rb +59 -0
- data/lib/google/api_client/service.rb +233 -0
- data/lib/google/api_client/service_account.rb +1 -0
- data/lib/google/api_client/version.rb +4 -3
- data/lib/google/api_client.rb +81 -42
- data/spec/google/api_client/batch_spec.rb +4 -4
- data/spec/google/api_client/discovery_spec.rb +60 -102
- data/spec/google/api_client/gzip_spec.rb +86 -0
- data/spec/google/api_client/media_spec.rb +49 -1
- data/spec/google/api_client/request_spec.rb +30 -0
- data/spec/google/api_client/result_spec.rb +3 -4
- data/spec/google/api_client/service_account_spec.rb +21 -0
- data/spec/google/api_client/service_spec.rb +586 -0
- data/spec/google/api_client/simple_file_store_spec.rb +137 -0
- data/spec/google/api_client_spec.rb +101 -3
- data/spec/spec_helper.rb +3 -0
- data/tasks/gem.rake +3 -4
- metadata +95 -50
- data/bin/google-api +0 -391
- /data/{CONTRIB.md → CONTRIBUTING.md} +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
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 'faraday'
|
|
16
|
+
require 'signet/oauth_2/client'
|
|
17
|
+
|
|
18
|
+
module Google
|
|
19
|
+
class APIClient
|
|
20
|
+
class ComputeServiceAccount < Signet::OAuth2::Client
|
|
21
|
+
def fetch_access_token(options={})
|
|
22
|
+
connection = options[:connection] || Faraday.default_connection
|
|
23
|
+
response = connection.get 'http://metadata/computeMetadata/v1beta1/instance/service-accounts/default/token'
|
|
24
|
+
Signet::OAuth2.parse_json_credentials(response.body)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
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
|
+
require 'signet/oauth_2/client'
|
|
17
|
+
|
|
18
|
+
module Google
|
|
19
|
+
class APIClient
|
|
20
|
+
##
|
|
21
|
+
# Represents cached OAuth 2 tokens stored on local disk in a
|
|
22
|
+
# JSON serialized file. Meant to resemble the serialized format
|
|
23
|
+
# http://google-api-python-client.googlecode.com/hg/docs/epy/oauth2client.file.Storage-class.html
|
|
24
|
+
#
|
|
25
|
+
class FileStorage
|
|
26
|
+
# @return [String] Path to the credentials file.
|
|
27
|
+
attr_accessor :path
|
|
28
|
+
|
|
29
|
+
# @return [Signet::OAuth2::Client] Path to the credentials file.
|
|
30
|
+
attr_reader :authorization
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Initializes the FileStorage object.
|
|
34
|
+
#
|
|
35
|
+
# @param [String] path
|
|
36
|
+
# Path to the credentials file.
|
|
37
|
+
def initialize(path)
|
|
38
|
+
@path = path
|
|
39
|
+
self.load_credentials
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
# Attempt to read in credentials from the specified file.
|
|
44
|
+
def load_credentials
|
|
45
|
+
if File.exist? self.path
|
|
46
|
+
File.open(self.path, 'r') do |file|
|
|
47
|
+
cached_credentials = JSON.load(file)
|
|
48
|
+
@authorization = Signet::OAuth2::Client.new(cached_credentials)
|
|
49
|
+
@authorization.issued_at = Time.at(cached_credentials['issued_at'])
|
|
50
|
+
if @authorization.expired?
|
|
51
|
+
@authorization.fetch_access_token!
|
|
52
|
+
self.write_credentials
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
# Write the credentials to the specified file.
|
|
60
|
+
#
|
|
61
|
+
# @param [Signet::OAuth2::Client] authorization
|
|
62
|
+
# Optional authorization instance. If not provided, the authorization
|
|
63
|
+
# already associated with this instance will be written.
|
|
64
|
+
def write_credentials(authorization=nil)
|
|
65
|
+
@authorization = authorization unless authorization.nil?
|
|
66
|
+
|
|
67
|
+
unless @authorization.refresh_token.nil?
|
|
68
|
+
hash = {}
|
|
69
|
+
%w'access_token
|
|
70
|
+
authorization_uri
|
|
71
|
+
client_id
|
|
72
|
+
client_secret
|
|
73
|
+
expires_in
|
|
74
|
+
refresh_token
|
|
75
|
+
token_credential_uri'.each do |var|
|
|
76
|
+
hash[var] = @authorization.instance_variable_get("@#{var}")
|
|
77
|
+
end
|
|
78
|
+
hash['issued_at'] = @authorization.issued_at.to_i
|
|
79
|
+
|
|
80
|
+
File.open(self.path, 'w', 0600) do |file|
|
|
81
|
+
file.write(hash.to_json)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -77,9 +77,13 @@ module Google
|
|
|
77
77
|
##
|
|
78
78
|
# Request authorization. Opens a browser and waits for response.
|
|
79
79
|
#
|
|
80
|
+
# @param [Google::APIClient::FileStorage] storage
|
|
81
|
+
# Optional object that responds to :write_credentials, used to serialize
|
|
82
|
+
# the OAuth 2 credentials after completing the flow.
|
|
83
|
+
#
|
|
80
84
|
# @return [Signet::OAuth2::Client]
|
|
81
85
|
# Authorization instance, nil if user cancelled.
|
|
82
|
-
def authorize
|
|
86
|
+
def authorize(storage=nil)
|
|
83
87
|
auth = @authorization
|
|
84
88
|
|
|
85
89
|
server = WEBrick::HTTPServer.new(
|
|
@@ -103,6 +107,9 @@ module Google
|
|
|
103
107
|
Launchy.open(auth.authorization_uri.to_s)
|
|
104
108
|
server.start
|
|
105
109
|
if @authorization.access_token
|
|
110
|
+
if storage.respond_to?(:write_credentials)
|
|
111
|
+
storage.write_credentials(@authorization)
|
|
112
|
+
end
|
|
106
113
|
return @authorization
|
|
107
114
|
else
|
|
108
115
|
return nil
|
|
@@ -34,17 +34,6 @@ module Google
|
|
|
34
34
|
# client.authorization.fetch_access_token!
|
|
35
35
|
# client.execute(...)
|
|
36
36
|
#
|
|
37
|
-
# @example Deprecated version
|
|
38
|
-
#
|
|
39
|
-
# client = Google::APIClient.new
|
|
40
|
-
# key = Google::APIClient::PKCS12.load_key('client.p12', 'notasecret')
|
|
41
|
-
# service_account = Google::APIClient::JWTAsserter.new(
|
|
42
|
-
# '123456-abcdef@developer.gserviceaccount.com',
|
|
43
|
-
# 'https://www.googleapis.com/auth/prediction',
|
|
44
|
-
# key)
|
|
45
|
-
# client.authorization = service_account.authorize
|
|
46
|
-
# client.execute(...)
|
|
47
|
-
#
|
|
48
37
|
# @deprecated
|
|
49
38
|
# Service accounts are now supported directly in Signet
|
|
50
39
|
# @see https://developers.google.com/accounts/docs/OAuth2ServiceAccount
|
|
@@ -146,7 +146,7 @@ module Google
|
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
def to_authorization
|
|
149
|
-
gem 'signet', '
|
|
149
|
+
gem 'signet', '>= 0.4.0'
|
|
150
150
|
require 'signet/oauth_2/client'
|
|
151
151
|
# NOTE: Do not rely on this default value, as it may change
|
|
152
152
|
new_authorization = Signet::OAuth2::Client.new
|
|
@@ -187,6 +187,7 @@ module Google
|
|
|
187
187
|
# @return [Addressable::URI] The URI after expansion.
|
|
188
188
|
def generate_uri(parameters={})
|
|
189
189
|
parameters = self.normalize_parameters(parameters)
|
|
190
|
+
|
|
190
191
|
self.validate_parameters(parameters)
|
|
191
192
|
template_variables = self.uri_template.variables
|
|
192
193
|
upload_type = parameters.assoc('uploadType') || parameters.assoc('upload_type')
|
|
@@ -19,6 +19,17 @@ module Google
|
|
|
19
19
|
# An error which is raised when there is an unexpected response or other
|
|
20
20
|
# transport error that prevents an operation from succeeding.
|
|
21
21
|
class TransmissionError < StandardError
|
|
22
|
+
attr_reader :result
|
|
23
|
+
def initialize(message = nil, result = nil)
|
|
24
|
+
super(message)
|
|
25
|
+
@result = result
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# An exception that is raised if a redirect is required
|
|
31
|
+
#
|
|
32
|
+
class RedirectError < TransmissionError
|
|
22
33
|
end
|
|
23
34
|
|
|
24
35
|
##
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'zlib'
|
|
3
|
+
|
|
4
|
+
module Google
|
|
5
|
+
class APIClient
|
|
6
|
+
class Gzip < Faraday::Response::Middleware
|
|
7
|
+
include Google::APIClient::Logging
|
|
8
|
+
|
|
9
|
+
def on_complete(env)
|
|
10
|
+
encoding = env[:response_headers]['content-encoding'].to_s.downcase
|
|
11
|
+
case encoding
|
|
12
|
+
when 'gzip'
|
|
13
|
+
logger.debug { "Decompressing gzip encoded response (#{env[:body].length} bytes)" }
|
|
14
|
+
env[:body] = Zlib::GzipReader.new(StringIO.new(env[:body])).read
|
|
15
|
+
env[:response_headers].delete('content-encoding')
|
|
16
|
+
logger.debug { "Decompressed (#{env[:body].length} bytes)" }
|
|
17
|
+
when 'deflate'
|
|
18
|
+
logger.debug{ "Decompressing deflate encoded response (#{env[:body].length} bytes)" }
|
|
19
|
+
env[:body] = Zlib::Inflate.inflate(env[:body])
|
|
20
|
+
env[:response_headers].delete('content-encoding')
|
|
21
|
+
logger.debug { "Decompressed (#{env[:body].length} bytes)" }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Faraday::Response.register_middleware :gzip => Google::APIClient::Gzip
|
|
@@ -21,7 +21,11 @@ module Google
|
|
|
21
21
|
# @see Faraday::UploadIO
|
|
22
22
|
# @example
|
|
23
23
|
# media = Google::APIClient::UploadIO.new('mymovie.m4v', 'video/mp4')
|
|
24
|
-
class UploadIO < Faraday::UploadIO
|
|
24
|
+
class UploadIO < Faraday::UploadIO
|
|
25
|
+
|
|
26
|
+
# @return [Fixnum] Size of chunks to upload. Default is nil, meaning upload the entire file in a single request
|
|
27
|
+
attr_accessor :chunk_size
|
|
28
|
+
|
|
25
29
|
##
|
|
26
30
|
# Get the length of the stream
|
|
27
31
|
#
|
|
@@ -32,6 +36,77 @@ module Google
|
|
|
32
36
|
end
|
|
33
37
|
end
|
|
34
38
|
|
|
39
|
+
##
|
|
40
|
+
# Wraps an input stream and limits data to a given range
|
|
41
|
+
#
|
|
42
|
+
# @example
|
|
43
|
+
# chunk = Google::APIClient::RangedIO.new(io, 0, 1000)
|
|
44
|
+
class RangedIO
|
|
45
|
+
##
|
|
46
|
+
# Bind an input stream to a specific range.
|
|
47
|
+
#
|
|
48
|
+
# @param [IO] io
|
|
49
|
+
# Source input stream
|
|
50
|
+
# @param [Fixnum] offset
|
|
51
|
+
# Starting offset of the range
|
|
52
|
+
# @param [Fixnum] length
|
|
53
|
+
# Length of range
|
|
54
|
+
def initialize(io, offset, length)
|
|
55
|
+
@io = io
|
|
56
|
+
@offset = offset
|
|
57
|
+
@length = length
|
|
58
|
+
self.rewind
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# @see IO#read
|
|
63
|
+
def read(amount = nil, buf = nil)
|
|
64
|
+
buffer = buf || ''
|
|
65
|
+
if amount.nil?
|
|
66
|
+
size = @length - @pos
|
|
67
|
+
done = ''
|
|
68
|
+
elsif amount == 0
|
|
69
|
+
size = 0
|
|
70
|
+
done = ''
|
|
71
|
+
else
|
|
72
|
+
size = [@length - @pos, amount].min
|
|
73
|
+
done = nil
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if size > 0
|
|
77
|
+
result = @io.read(size)
|
|
78
|
+
result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
|
|
79
|
+
buffer << result if result
|
|
80
|
+
@pos = @pos + size
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
if buffer.length > 0
|
|
84
|
+
buffer
|
|
85
|
+
else
|
|
86
|
+
done
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# @see IO#rewind
|
|
92
|
+
def rewind
|
|
93
|
+
self.pos = 0
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
##
|
|
97
|
+
# @see IO#pos
|
|
98
|
+
def pos
|
|
99
|
+
@pos
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# @see IO#pos=
|
|
104
|
+
def pos=(pos)
|
|
105
|
+
@pos = pos
|
|
106
|
+
@io.pos = @offset + pos
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
35
110
|
##
|
|
36
111
|
# Resumable uploader.
|
|
37
112
|
#
|
|
@@ -124,11 +199,11 @@ module Google
|
|
|
124
199
|
'Content-Range' => "bytes */#{media.length}" })
|
|
125
200
|
else
|
|
126
201
|
start_offset = @offset
|
|
127
|
-
self.media.
|
|
128
|
-
|
|
129
|
-
content_length =
|
|
202
|
+
remaining = self.media.length - start_offset
|
|
203
|
+
chunk_size = self.media.chunk_size || self.chunk_size || self.media.length
|
|
204
|
+
content_length = [remaining, chunk_size].min
|
|
205
|
+
chunk = RangedIO.new(self.media.io, start_offset, content_length)
|
|
130
206
|
end_offset = start_offset + content_length - 1
|
|
131
|
-
|
|
132
207
|
self.headers.update({
|
|
133
208
|
'Content-Length' => "#{content_length}",
|
|
134
209
|
'Content-Type' => self.media.content_type,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
require 'faraday'
|
|
16
|
-
require 'faraday/
|
|
16
|
+
require 'faraday/request/multipart'
|
|
17
17
|
require 'multi_json'
|
|
18
18
|
require 'compat/multi_json'
|
|
19
19
|
require 'addressable/uri'
|
|
@@ -71,8 +71,10 @@ module Google
|
|
|
71
71
|
# @option options [String, Symbol] :http_method
|
|
72
72
|
# HTTP method when requesting a URI
|
|
73
73
|
def initialize(options={})
|
|
74
|
-
@parameters =
|
|
74
|
+
@parameters = Faraday::Utils::ParamsHash.new
|
|
75
75
|
@headers = Faraday::Utils::Headers.new
|
|
76
|
+
|
|
77
|
+
self.parameters.merge!(options[:parameters]) unless options[:parameters].nil?
|
|
76
78
|
self.headers.merge!(options[:headers]) unless options[:headers].nil?
|
|
77
79
|
self.api_method = options[:api_method]
|
|
78
80
|
self.authenticated = options[:authenticated]
|
|
@@ -150,10 +152,13 @@ module Google
|
|
|
150
152
|
#
|
|
151
153
|
# @param [Faraday::Connection] connection
|
|
152
154
|
# the connection to transmit with
|
|
155
|
+
# @param [TrueValue,FalseValue] is_retry
|
|
156
|
+
# True if request has been previous sent
|
|
153
157
|
#
|
|
154
158
|
# @return [Google::APIClient::Result]
|
|
155
159
|
# result of API request
|
|
156
|
-
def send(connection)
|
|
160
|
+
def send(connection, is_retry = false)
|
|
161
|
+
self.body.rewind if is_retry && self.body.respond_to?(:rewind)
|
|
157
162
|
env = self.to_env(connection)
|
|
158
163
|
logger.debug { "#{self.class} Sending API request #{env[:method]} #{env[:url].to_s} #{env[:request_headers]}" }
|
|
159
164
|
http_response = connection.app.call(env)
|
|
@@ -163,8 +168,8 @@ module Google
|
|
|
163
168
|
|
|
164
169
|
# Resumamble slightly different than other upload protocols in that it requires at least
|
|
165
170
|
# 2 requests.
|
|
166
|
-
if self.upload_type == 'resumable'
|
|
167
|
-
upload =
|
|
171
|
+
if result.status == 200 && self.upload_type == 'resumable'
|
|
172
|
+
upload = result.resumable_upload
|
|
168
173
|
unless upload.complete?
|
|
169
174
|
logger.debug { "#{self.class} Sending upload body" }
|
|
170
175
|
result = upload.send(connection)
|
|
@@ -282,6 +287,7 @@ module Google
|
|
|
282
287
|
raise ArgumentError, "Can not specify body & body object for simple uploads"
|
|
283
288
|
end
|
|
284
289
|
self.headers['Content-Type'] ||= self.media.content_type
|
|
290
|
+
self.headers['Content-Length'] ||= self.media.length.to_s
|
|
285
291
|
self.body = self.media
|
|
286
292
|
when "multipart"
|
|
287
293
|
unless options[:body_object]
|
|
@@ -314,10 +320,10 @@ module Google
|
|
|
314
320
|
# @param [String] boundary
|
|
315
321
|
# Boundary for separating each part of the message
|
|
316
322
|
def build_multipart(parts, mime_type = 'multipart/related', boundary = MULTIPART_BOUNDARY)
|
|
317
|
-
env =
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
323
|
+
env = Faraday::Env.new
|
|
324
|
+
env.request = Faraday::RequestOptions.new
|
|
325
|
+
env.request.boundary = boundary
|
|
326
|
+
env.request_headers = {'Content-Type' => "#{mime_type};boundary=#{boundary}"}
|
|
321
327
|
multipart = Faraday::Request::Multipart.new
|
|
322
328
|
self.body = multipart.create_multipart(env, parts.map {|part| [nil, part]})
|
|
323
329
|
self.headers.update(env[:request_headers])
|
|
@@ -0,0 +1,103 @@
|
|
|
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 'google/api_client/service/result'
|
|
16
|
+
require 'google/api_client/batch'
|
|
17
|
+
|
|
18
|
+
module Google
|
|
19
|
+
class APIClient
|
|
20
|
+
class Service
|
|
21
|
+
|
|
22
|
+
##
|
|
23
|
+
# Helper class to contain the result of an individual batched call.
|
|
24
|
+
#
|
|
25
|
+
class BatchedCallResult < Result
|
|
26
|
+
# @return [Fixnum] Index of the call
|
|
27
|
+
def call_index
|
|
28
|
+
return @base_result.response.call_id.to_i - 1
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
#
|
|
34
|
+
#
|
|
35
|
+
class BatchRequest
|
|
36
|
+
##
|
|
37
|
+
# Creates a new batch request.
|
|
38
|
+
# This class shouldn't be instantiated directly, but rather through
|
|
39
|
+
# Service.batch.
|
|
40
|
+
#
|
|
41
|
+
# @param [Array] calls
|
|
42
|
+
# List of Google::APIClient::Service::Request to be made.
|
|
43
|
+
# @param [Proc] block
|
|
44
|
+
# Callback for every call's response. Won't be called if a call
|
|
45
|
+
# defined a callback of its own.
|
|
46
|
+
#
|
|
47
|
+
# @yield [Google::APIClient::Service::Result]
|
|
48
|
+
# block to be called when result ready
|
|
49
|
+
def initialize(service, calls, &block)
|
|
50
|
+
@service = service
|
|
51
|
+
@base_batch = Google::APIClient::BatchRequest.new
|
|
52
|
+
@global_callback = block if block_given?
|
|
53
|
+
|
|
54
|
+
if calls && calls.length > 0
|
|
55
|
+
calls.each do |call|
|
|
56
|
+
add(call)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Add a new call to the batch request.
|
|
63
|
+
#
|
|
64
|
+
# @param [Google::APIClient::Service::Request] call
|
|
65
|
+
# the call to be added.
|
|
66
|
+
# @param [Proc] block
|
|
67
|
+
# callback for this call's response.
|
|
68
|
+
#
|
|
69
|
+
# @return [Google::APIClient::Service::BatchRequest]
|
|
70
|
+
# the BatchRequest, for chaining
|
|
71
|
+
#
|
|
72
|
+
# @yield [Google::APIClient::Service::Result]
|
|
73
|
+
# block to be called when result ready
|
|
74
|
+
def add(call, &block)
|
|
75
|
+
if !block_given? && @global_callback.nil?
|
|
76
|
+
raise BatchError, 'Request needs a block'
|
|
77
|
+
end
|
|
78
|
+
callback = block || @global_callback
|
|
79
|
+
base_call = {
|
|
80
|
+
:api_method => call.method,
|
|
81
|
+
:parameters => call.parameters
|
|
82
|
+
}
|
|
83
|
+
@base_batch.add(base_call) do |base_result|
|
|
84
|
+
result = Google::APIClient::Service::BatchedCallResult.new(
|
|
85
|
+
call, base_result)
|
|
86
|
+
callback.call(result)
|
|
87
|
+
end
|
|
88
|
+
return self
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
##
|
|
92
|
+
# Executes the batch request.
|
|
93
|
+
def execute
|
|
94
|
+
@service.execute(self)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
attr_reader :base_batch
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
module Google
|
|
16
|
+
class APIClient
|
|
17
|
+
class Service
|
|
18
|
+
##
|
|
19
|
+
# Handles an API request.
|
|
20
|
+
# This contains a full definition of the request to be made (including
|
|
21
|
+
# method name, parameters, body and media). The remote API call can be
|
|
22
|
+
# invoked with execute().
|
|
23
|
+
class Request
|
|
24
|
+
##
|
|
25
|
+
# Build a request.
|
|
26
|
+
# This class should not be directly instantiated in user code;
|
|
27
|
+
# instantiation is handled by the stub methods created on Service and
|
|
28
|
+
# Resource objects.
|
|
29
|
+
#
|
|
30
|
+
# @param [Google::APIClient::Service] service
|
|
31
|
+
# The parent Service instance that will execute the request.
|
|
32
|
+
# @param [Google::APIClient::Method] method
|
|
33
|
+
# The Method instance that describes the API method invoked by the
|
|
34
|
+
# request.
|
|
35
|
+
# @param [Hash] parameters
|
|
36
|
+
# A Hash of parameter names and values to be sent in the API call.
|
|
37
|
+
def initialize(service, method, parameters)
|
|
38
|
+
@service = service
|
|
39
|
+
@method = method
|
|
40
|
+
@parameters = parameters
|
|
41
|
+
@body = nil
|
|
42
|
+
@media = nil
|
|
43
|
+
|
|
44
|
+
metaclass = (class << self; self; end)
|
|
45
|
+
|
|
46
|
+
# If applicable, add "body", "body=" and resource-named methods for
|
|
47
|
+
# retrieving and setting the HTTP body for this request.
|
|
48
|
+
# Examples of setting the body for files.insert in the Drive API:
|
|
49
|
+
# request.body = object
|
|
50
|
+
# request.execute
|
|
51
|
+
# OR
|
|
52
|
+
# request.file = object
|
|
53
|
+
# request.execute
|
|
54
|
+
# OR
|
|
55
|
+
# request.body(object).execute
|
|
56
|
+
# OR
|
|
57
|
+
# request.file(object).execute
|
|
58
|
+
# Examples of retrieving the body for files.insert in the Drive API:
|
|
59
|
+
# object = request.body
|
|
60
|
+
# OR
|
|
61
|
+
# object = request.file
|
|
62
|
+
if method.request_schema
|
|
63
|
+
body_name = method.request_schema.data['id'].dup
|
|
64
|
+
body_name[0] = body_name[0].chr.downcase
|
|
65
|
+
body_name_equals = (body_name + '=').to_sym
|
|
66
|
+
body_name = body_name.to_sym
|
|
67
|
+
|
|
68
|
+
metaclass.send(:define_method, :body) do |*args|
|
|
69
|
+
if args.length == 1
|
|
70
|
+
@body = args.first
|
|
71
|
+
return self
|
|
72
|
+
elsif args.length == 0
|
|
73
|
+
return @body
|
|
74
|
+
else
|
|
75
|
+
raise ArgumentError,
|
|
76
|
+
"wrong number of arguments (#{args.length}; expecting 0 or 1)"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
metaclass.send(:define_method, :body=) do |body|
|
|
81
|
+
@body = body
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
metaclass.send(:alias_method, body_name, :body)
|
|
85
|
+
metaclass.send(:alias_method, body_name_equals, :body=)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# If applicable, add "media" and "media=" for retrieving and setting
|
|
89
|
+
# the media object for this request.
|
|
90
|
+
# Examples of setting the media object:
|
|
91
|
+
# request.media = object
|
|
92
|
+
# request.execute
|
|
93
|
+
# OR
|
|
94
|
+
# request.media(object).execute
|
|
95
|
+
# Example of retrieving the media object:
|
|
96
|
+
# object = request.media
|
|
97
|
+
if method.media_upload
|
|
98
|
+
metaclass.send(:define_method, :media) do |*args|
|
|
99
|
+
if args.length == 1
|
|
100
|
+
@media = args.first
|
|
101
|
+
return self
|
|
102
|
+
elsif args.length == 0
|
|
103
|
+
return @media
|
|
104
|
+
else
|
|
105
|
+
raise ArgumentError,
|
|
106
|
+
"wrong number of arguments (#{args.length}; expecting 0 or 1)"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
metaclass.send(:define_method, :media=) do |media|
|
|
111
|
+
@media = media
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
##
|
|
117
|
+
# Returns the parent service capable of executing this request.
|
|
118
|
+
#
|
|
119
|
+
# @return [Google::APIClient::Service] The parent service.
|
|
120
|
+
attr_reader :service
|
|
121
|
+
|
|
122
|
+
##
|
|
123
|
+
# Returns the Method instance that describes the API method invoked by
|
|
124
|
+
# the request.
|
|
125
|
+
#
|
|
126
|
+
# @return [Google::APIClient::Method] The API method description.
|
|
127
|
+
attr_reader :method
|
|
128
|
+
|
|
129
|
+
##
|
|
130
|
+
# Contains the Hash of parameter names and values to be sent as the
|
|
131
|
+
# parameters for the API call.
|
|
132
|
+
#
|
|
133
|
+
# @return [Hash] The request parameters.
|
|
134
|
+
attr_accessor :parameters
|
|
135
|
+
|
|
136
|
+
##
|
|
137
|
+
# Executes the request.
|
|
138
|
+
def execute
|
|
139
|
+
@service.execute(self)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
module Google
|
|
16
|
+
class APIClient
|
|
17
|
+
class Service
|
|
18
|
+
##
|
|
19
|
+
# Handles an API resource.
|
|
20
|
+
# Simple class that contains API methods and/or child resources.
|
|
21
|
+
class Resource
|
|
22
|
+
include Google::APIClient::Service::StubGenerator
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Build a resource.
|
|
26
|
+
# This class should not be directly instantiated in user code; resources
|
|
27
|
+
# are instantiated by the stub generation mechanism on Service creation.
|
|
28
|
+
#
|
|
29
|
+
# @param [Google::APIClient::Service] service
|
|
30
|
+
# The Service instance this resource belongs to.
|
|
31
|
+
# @param [Google::APIClient::API, Google::APIClient::Resource] root
|
|
32
|
+
# The node corresponding to this resource.
|
|
33
|
+
def initialize(service, root)
|
|
34
|
+
@service = service
|
|
35
|
+
generate_call_stubs(service, root)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|