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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +178 -0
- data/Gemfile +9 -0
- data/LICENSE +202 -0
- data/README.md +218 -0
- data/Rakefile +41 -0
- data/google-api-client.gemspec +43 -0
- data/lib/cacerts.pem +2183 -0
- data/lib/compat/multi_json.rb +19 -0
- data/lib/google/api_client.rb +750 -0
- data/lib/google/api_client/auth/compute_service_account.rb +28 -0
- data/lib/google/api_client/auth/file_storage.rb +59 -0
- data/lib/google/api_client/auth/installed_app.rb +126 -0
- data/lib/google/api_client/auth/jwt_asserter.rb +126 -0
- data/lib/google/api_client/auth/key_utils.rb +93 -0
- data/lib/google/api_client/auth/pkcs12.rb +41 -0
- data/lib/google/api_client/auth/storage.rb +102 -0
- data/lib/google/api_client/auth/storages/file_store.rb +58 -0
- data/lib/google/api_client/auth/storages/redis_store.rb +54 -0
- data/lib/google/api_client/batch.rb +326 -0
- data/lib/google/api_client/charset.rb +33 -0
- data/lib/google/api_client/client_secrets.rb +179 -0
- data/lib/google/api_client/discovery.rb +19 -0
- data/lib/google/api_client/discovery/api.rb +310 -0
- data/lib/google/api_client/discovery/media.rb +77 -0
- data/lib/google/api_client/discovery/method.rb +363 -0
- data/lib/google/api_client/discovery/resource.rb +156 -0
- data/lib/google/api_client/discovery/schema.rb +117 -0
- data/lib/google/api_client/environment.rb +42 -0
- data/lib/google/api_client/errors.rb +65 -0
- data/lib/google/api_client/gzip.rb +28 -0
- data/lib/google/api_client/logging.rb +32 -0
- data/lib/google/api_client/media.rb +259 -0
- data/lib/google/api_client/railtie.rb +18 -0
- data/lib/google/api_client/reference.rb +27 -0
- data/lib/google/api_client/request.rb +350 -0
- data/lib/google/api_client/result.rb +255 -0
- data/lib/google/api_client/service.rb +233 -0
- data/lib/google/api_client/service/batch.rb +110 -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 +61 -0
- data/lib/google/api_client/service_account.rb +21 -0
- data/lib/google/api_client/version.rb +26 -0
- data/spec/google/api_client/auth/storage_spec.rb +122 -0
- data/spec/google/api_client/auth/storages/file_store_spec.rb +40 -0
- data/spec/google/api_client/auth/storages/redis_store_spec.rb +70 -0
- data/spec/google/api_client/batch_spec.rb +248 -0
- data/spec/google/api_client/client_secrets_spec.rb +53 -0
- data/spec/google/api_client/discovery_spec.rb +708 -0
- data/spec/google/api_client/gzip_spec.rb +98 -0
- data/spec/google/api_client/media_spec.rb +178 -0
- data/spec/google/api_client/request_spec.rb +29 -0
- data/spec/google/api_client/result_spec.rb +207 -0
- data/spec/google/api_client/service_account_spec.rb +169 -0
- data/spec/google/api_client/service_spec.rb +618 -0
- data/spec/google/api_client/simple_file_store_spec.rb +133 -0
- data/spec/google/api_client_spec.rb +352 -0
- data/spec/spec_helper.rb +66 -0
- 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
|