google-api-client 0.4.3 → 0.4.4
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.
- data/CHANGELOG.md +6 -0
- data/Gemfile +28 -0
- data/Gemfile.lock +61 -0
- data/README.md +3 -0
- data/Rakefile +1 -1
- data/lib/google/api_client.rb +93 -40
- data/lib/google/api_client/batch.rb +296 -0
- data/lib/google/api_client/client_secrets.rb +2 -2
- data/lib/google/api_client/discovery/api.rb +15 -0
- data/lib/google/api_client/discovery/method.rb +40 -16
- data/lib/google/api_client/errors.rb +4 -0
- data/lib/google/api_client/reference.rb +12 -2
- data/lib/google/api_client/result.rb +1 -1
- data/lib/google/api_client/service_account.rb +134 -0
- data/lib/google/api_client/version.rb +1 -1
- data/spec/fixtures/files/sample.txt +33 -0
- data/spec/google/api_client/batch_spec.rb +237 -0
- data/spec/google/api_client/discovery_spec.rb +189 -1
- data/spec/google/api_client/media_spec.rb +136 -0
- data/spec/google/api_client/result_spec.rb +150 -0
- data/spec/google/api_client/service_account_spec.rb +58 -0
- data/spec/google/api_client_spec.rb +30 -0
- data/tasks/gem.rake +3 -3
- data/tasks/spec.rake +3 -3
- data/tasks/wiki.rake +1 -1
- metadata +122 -117
- data/tasks/rdoc.rake +0 -30
data/CHANGELOG.md
CHANGED
data/Gemfile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gem 'signet', '>= 0.3.4'
|
4
|
+
gem 'addressable', '>= 2.2.3'
|
5
|
+
gem 'uuidtools', '>= 2.1.0'
|
6
|
+
gem 'autoparse', '>= 0.3.1'
|
7
|
+
gem 'faraday', '~> 0.7.0'
|
8
|
+
gem 'multi_json', '>= 1.3.0'
|
9
|
+
gem 'extlib', '>= 0.9.15'
|
10
|
+
gem 'jruby-openssl', :platforms => :jruby
|
11
|
+
|
12
|
+
group :development do
|
13
|
+
gem 'launchy'
|
14
|
+
gem 'yard'
|
15
|
+
gem 'redcarpet'
|
16
|
+
end
|
17
|
+
|
18
|
+
group :examples do
|
19
|
+
gem 'sinatra'
|
20
|
+
end
|
21
|
+
|
22
|
+
group :test, :development do
|
23
|
+
gem 'rake', '>= 0.9.0'
|
24
|
+
gem 'rspec', '~> 1.2.9'
|
25
|
+
gem 'rcov', '>= 0.9.9', :platform => :mri_18
|
26
|
+
end
|
27
|
+
|
28
|
+
gem 'idn', :platform => :mri_18
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.2.8)
|
5
|
+
autoparse (0.3.1)
|
6
|
+
addressable (~> 2.2.3)
|
7
|
+
extlib (>= 0.9.15)
|
8
|
+
multi_json (>= 1.0.0)
|
9
|
+
extlib (0.9.15)
|
10
|
+
faraday (0.7.6)
|
11
|
+
addressable (~> 2.2)
|
12
|
+
multipart-post (~> 1.1)
|
13
|
+
rack (~> 1.1)
|
14
|
+
idn (0.0.2)
|
15
|
+
json (1.7.3)
|
16
|
+
jwt (0.1.4)
|
17
|
+
json (>= 1.2.4)
|
18
|
+
launchy (2.1.0)
|
19
|
+
addressable (~> 2.2.6)
|
20
|
+
multi_json (1.3.6)
|
21
|
+
multipart-post (1.1.5)
|
22
|
+
rack (1.4.1)
|
23
|
+
rack-protection (1.2.0)
|
24
|
+
rack
|
25
|
+
rake (0.9.2.2)
|
26
|
+
rcov (1.0.0)
|
27
|
+
redcarpet (2.1.1)
|
28
|
+
rspec (1.2.9)
|
29
|
+
signet (0.3.4)
|
30
|
+
addressable (~> 2.2.3)
|
31
|
+
faraday (~> 0.7.0)
|
32
|
+
jwt (>= 0.1.4)
|
33
|
+
multi_json (>= 1.0.0)
|
34
|
+
sinatra (1.3.2)
|
35
|
+
rack (~> 1.3, >= 1.3.6)
|
36
|
+
rack-protection (~> 1.2)
|
37
|
+
tilt (~> 1.3, >= 1.3.3)
|
38
|
+
tilt (1.3.3)
|
39
|
+
uuidtools (2.1.2)
|
40
|
+
yard (0.8.1)
|
41
|
+
|
42
|
+
PLATFORMS
|
43
|
+
ruby
|
44
|
+
|
45
|
+
DEPENDENCIES
|
46
|
+
addressable (>= 2.2.3)
|
47
|
+
autoparse (>= 0.3.1)
|
48
|
+
extlib (>= 0.9.15)
|
49
|
+
faraday (~> 0.7.0)
|
50
|
+
idn
|
51
|
+
jruby-openssl
|
52
|
+
launchy
|
53
|
+
multi_json (>= 1.3.0)
|
54
|
+
rake (>= 0.9.0)
|
55
|
+
rcov (>= 0.9.9)
|
56
|
+
redcarpet
|
57
|
+
rspec (~> 1.2.9)
|
58
|
+
signet (>= 0.3.4)
|
59
|
+
sinatra
|
60
|
+
uuidtools (>= 2.1.0)
|
61
|
+
yard
|
data/README.md
CHANGED
@@ -7,6 +7,9 @@
|
|
7
7
|
<dt>License</dt><dd>Apache 2.0</dd>
|
8
8
|
</dl>
|
9
9
|
|
10
|
+
[](http://travis-ci.org/google/google-api-ruby-client)
|
11
|
+
[](https://gemnasium.com/google/google-api-ruby-client)
|
12
|
+
|
10
13
|
# Description
|
11
14
|
|
12
15
|
The Google API Ruby Client makes it trivial to discover and access supported
|
data/Rakefile
CHANGED
data/lib/google/api_client.rb
CHANGED
@@ -26,6 +26,8 @@ require 'google/api_client/discovery'
|
|
26
26
|
require 'google/api_client/reference'
|
27
27
|
require 'google/api_client/result'
|
28
28
|
require 'google/api_client/media'
|
29
|
+
require 'google/api_client/service_account'
|
30
|
+
require 'google/api_client/batch'
|
29
31
|
|
30
32
|
module Google
|
31
33
|
# TODO(bobaman): Document all this stuff.
|
@@ -281,7 +283,7 @@ module Google
|
|
281
283
|
"Expected String or StringIO, got #{discovery_document.class}."
|
282
284
|
end
|
283
285
|
@discovery_documents["#{api}:#{version}"] =
|
284
|
-
MultiJson.
|
286
|
+
MultiJson.load(discovery_document)
|
285
287
|
end
|
286
288
|
|
287
289
|
##
|
@@ -297,7 +299,7 @@ module Google
|
|
297
299
|
)
|
298
300
|
response = self.transmit(:request => request)
|
299
301
|
if response.status >= 200 && response.status < 300
|
300
|
-
MultiJson.
|
302
|
+
MultiJson.load(response.body)
|
301
303
|
elsif response.status >= 400
|
302
304
|
case response.status
|
303
305
|
when 400...500
|
@@ -331,7 +333,7 @@ module Google
|
|
331
333
|
)
|
332
334
|
response = self.transmit(:request => request)
|
333
335
|
if response.status >= 200 && response.status < 300
|
334
|
-
MultiJson.
|
336
|
+
MultiJson.load(response.body)
|
335
337
|
elsif response.status >= 400
|
336
338
|
case response.status
|
337
339
|
when 400...500
|
@@ -484,7 +486,7 @@ module Google
|
|
484
486
|
response = self.transmit(:request => request)
|
485
487
|
if response.status >= 200 && response.status < 300
|
486
488
|
@certificates.merge!(
|
487
|
-
Hash[MultiJson.
|
489
|
+
Hash[MultiJson.load(response.body).map do |key, cert|
|
488
490
|
[key, OpenSSL::X509::Certificate.new(cert)]
|
489
491
|
end]
|
490
492
|
)
|
@@ -540,6 +542,7 @@ module Google
|
|
540
542
|
def generate_request(options={})
|
541
543
|
# Note: The merge method on a Hash object will coerce an API Reference
|
542
544
|
# object into a Hash and merge with the default options.
|
545
|
+
|
543
546
|
options={
|
544
547
|
:version => 'v1',
|
545
548
|
:authorization => self.authorization,
|
@@ -547,6 +550,7 @@ module Google
|
|
547
550
|
:user_ip => self.user_ip,
|
548
551
|
:connection => Faraday.default_connection
|
549
552
|
}.merge(options)
|
553
|
+
|
550
554
|
# The Reference object is going to need this to do method ID lookups.
|
551
555
|
options[:client] = self
|
552
556
|
# The default value for the :authenticated option depends on whether an
|
@@ -559,7 +563,7 @@ module Google
|
|
559
563
|
reference = Google::APIClient::Reference.new(options)
|
560
564
|
request = reference.to_request
|
561
565
|
if options[:authenticated]
|
562
|
-
request =
|
566
|
+
request = options[:authorization].generate_authenticated_request(
|
563
567
|
:request => request,
|
564
568
|
:connection => options[:connection]
|
565
569
|
)
|
@@ -573,6 +577,7 @@ module Google
|
|
573
577
|
# @param [Hash] options a customizable set of options
|
574
578
|
#
|
575
579
|
# @return [Faraday::Request] The signed or otherwise authenticated request.
|
580
|
+
# @deprecated No longer used internally
|
576
581
|
def generate_authenticated_request(options={})
|
577
582
|
return authorization.generate_authenticated_request(options)
|
578
583
|
end
|
@@ -663,22 +668,34 @@ module Google
|
|
663
668
|
##
|
664
669
|
# Executes a request, wrapping it in a Result object.
|
665
670
|
#
|
666
|
-
# @param [Google::APIClient::
|
667
|
-
#
|
668
|
-
#
|
669
|
-
#
|
670
|
-
#
|
671
|
-
#
|
672
|
-
#
|
673
|
-
# The service version. Only used if `api_method` is a `String`.
|
674
|
-
# @option options [#generate_authenticated_request] :authorization
|
675
|
-
# The authorization mechanism for the response. Used only if
|
676
|
-
# `:authenticated` is `true`.
|
677
|
-
# @option options [TrueClass, FalseClass] :authenticated (true)
|
678
|
-
# `true` if the request must be signed or somehow
|
679
|
-
# authenticated, `false` otherwise.
|
671
|
+
# @param [Google::APIClient::BatchRequest, Hash, Array] params
|
672
|
+
# Either a Google::APIClient::BatchRequest, a Hash, or an Array.
|
673
|
+
#
|
674
|
+
# If a Google::APIClient::BatchRequest, no other parameters are expected.
|
675
|
+
#
|
676
|
+
# If a Hash, the below parameters are handled. If an Array, the
|
677
|
+
# parameters are assumed to be in the below order:
|
680
678
|
#
|
681
|
-
#
|
679
|
+
# - (Google::APIClient::Method, String) api_method:
|
680
|
+
# The method object or the RPC name of the method being executed.
|
681
|
+
# - (Hash, Array) parameters:
|
682
|
+
# The parameters to send to the method.
|
683
|
+
# - (String) body: The body of the request.
|
684
|
+
# - (Hash, Array) headers: The HTTP headers for the request.
|
685
|
+
# - (Hash) options: A set of options for the request, of which:
|
686
|
+
# - (String) :version (default: "v1") -
|
687
|
+
# The service version. Only used if `api_method` is a `String`.
|
688
|
+
# - (#generate_authenticated_request) :authorization (default: true) -
|
689
|
+
# The authorization mechanism for the response. Used only if
|
690
|
+
# `:authenticated` is `true`.
|
691
|
+
# - (TrueClass, FalseClass) :authenticated (default: true) -
|
692
|
+
# `true` if the request must be signed or somehow
|
693
|
+
# authenticated, `false` otherwise.
|
694
|
+
#
|
695
|
+
# @return [Google::APIClient::Result] The result from the API, nil if batch.
|
696
|
+
#
|
697
|
+
# @example
|
698
|
+
# result = client.execute(batch_request)
|
682
699
|
#
|
683
700
|
# @example
|
684
701
|
# result = client.execute(
|
@@ -688,28 +705,64 @@ module Google
|
|
688
705
|
#
|
689
706
|
# @see Google::APIClient#generate_request
|
690
707
|
def execute(*params)
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
708
|
+
if params.last.kind_of?(Google::APIClient::BatchRequest) &&
|
709
|
+
params.size == 1
|
710
|
+
batch = params.pop
|
711
|
+
options = batch.options
|
712
|
+
http_request = batch.to_http_request
|
713
|
+
request = nil
|
714
|
+
|
715
|
+
if @authorization
|
716
|
+
method, uri, headers, body = http_request
|
717
|
+
method = method.to_s.downcase.to_sym
|
718
|
+
|
719
|
+
faraday_request = Faraday::Request.create(method) do |req|
|
720
|
+
req.url(uri.to_s)
|
721
|
+
req.headers = Faraday::Utils::Headers.new(headers)
|
722
|
+
req.body = body
|
723
|
+
end
|
724
|
+
|
725
|
+
request = {
|
726
|
+
:request => self.generate_authenticated_request(
|
727
|
+
:request => faraday_request,
|
728
|
+
:connection => options[:connection]
|
729
|
+
),
|
730
|
+
:connection => options[:connection]
|
731
|
+
}
|
732
|
+
else
|
733
|
+
request = {
|
734
|
+
:request => http_request,
|
735
|
+
:connection => options[:connection]
|
736
|
+
}
|
737
|
+
end
|
738
|
+
|
739
|
+
response = self.transmit(request)
|
740
|
+
batch.process_response(response)
|
741
|
+
return nil
|
697
742
|
else
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
743
|
+
# This block of code allows us to accept multiple parameter passing
|
744
|
+
# styles, and maintaining some backwards compatibility.
|
745
|
+
#
|
746
|
+
# Note: I'm extremely tempted to deprecate this style of execute call.
|
747
|
+
if params.last.respond_to?(:to_hash) && params.size == 1
|
748
|
+
options = params.pop
|
749
|
+
else
|
750
|
+
options = {}
|
751
|
+
end
|
705
752
|
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
:
|
710
|
-
|
711
|
-
|
712
|
-
|
753
|
+
options[:api_method] = params.shift if params.size > 0
|
754
|
+
options[:parameters] = params.shift if params.size > 0
|
755
|
+
options[:body] = params.shift if params.size > 0
|
756
|
+
options[:headers] = params.shift if params.size > 0
|
757
|
+
options[:client] = self
|
758
|
+
reference = Google::APIClient::Reference.new(options)
|
759
|
+
request = self.generate_request(reference)
|
760
|
+
response = self.transmit(
|
761
|
+
:request => request,
|
762
|
+
:connection => options[:connection]
|
763
|
+
)
|
764
|
+
return Google::APIClient::Result.new(reference, request, response)
|
765
|
+
end
|
713
766
|
end
|
714
767
|
|
715
768
|
##
|
@@ -0,0 +1,296 @@
|
|
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 'uuidtools'
|
17
|
+
|
18
|
+
module Google
|
19
|
+
class APIClient
|
20
|
+
|
21
|
+
# Helper class to contain a response to an individual batched call.
|
22
|
+
class BatchedCallResponse
|
23
|
+
attr_reader :call_id
|
24
|
+
attr_accessor :status, :headers, :body
|
25
|
+
|
26
|
+
def initialize(call_id, status = nil, headers = nil, body = nil)
|
27
|
+
@call_id, @status, @headers, @body = call_id, status, headers, body
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Wraps multiple API calls into a single over-the-wire HTTP request.
|
33
|
+
class BatchRequest
|
34
|
+
|
35
|
+
BATCH_BOUNDARY = "-----------RubyApiBatchRequest".freeze
|
36
|
+
|
37
|
+
attr_accessor :options
|
38
|
+
attr_reader :calls, :callbacks
|
39
|
+
|
40
|
+
##
|
41
|
+
# Creates a new batch request.
|
42
|
+
#
|
43
|
+
# @param [Hash] options
|
44
|
+
# Set of options for this request, the only important one being
|
45
|
+
# :connection, which specifies an HTTP connection to use.
|
46
|
+
# @param [Proc] block
|
47
|
+
# Callback for every call's response. Won't be called if a call defined
|
48
|
+
# a callback of its own.
|
49
|
+
#
|
50
|
+
# @return [Google::APIClient::BatchRequest] The constructed object.
|
51
|
+
def initialize(options = {}, &block)
|
52
|
+
# Request options, ignoring method and parameters.
|
53
|
+
@options = options
|
54
|
+
# Batched calls to be made, indexed by call ID.
|
55
|
+
@calls = {}
|
56
|
+
# Callbacks per batched call, indexed by call ID.
|
57
|
+
@callbacks = {}
|
58
|
+
# Order for the call IDs, since Ruby 1.8 hashes are unordered.
|
59
|
+
@order = []
|
60
|
+
# Global callback to be used for every call. If a specific callback
|
61
|
+
# has been defined for a request, this won't be called.
|
62
|
+
@global_callback = block if block_given?
|
63
|
+
# The last auto generated ID.
|
64
|
+
@last_auto_id = 0
|
65
|
+
# Base ID for the batch request.
|
66
|
+
@base_id = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Add a new call to the batch request.
|
71
|
+
# Each call must have its own call ID; if not provided, one will
|
72
|
+
# automatically be generated, avoiding collisions. If duplicate call IDs
|
73
|
+
# are provided, an error will be thrown.
|
74
|
+
#
|
75
|
+
# @param [Hash, Google::APIClient::Reference] call: the call to be added.
|
76
|
+
# @param [String] call_id: the ID to be used for this call. Must be unique
|
77
|
+
# @param [Proc] block: callback for this call's response.
|
78
|
+
#
|
79
|
+
# @return [Google::APIClient::BatchRequest] The BatchRequest, for chaining
|
80
|
+
def add(call, call_id = nil, &block)
|
81
|
+
unless call.kind_of?(Google::APIClient::Reference)
|
82
|
+
call = Google::APIClient::Reference.new(call)
|
83
|
+
end
|
84
|
+
if call_id.nil?
|
85
|
+
call_id = new_id
|
86
|
+
end
|
87
|
+
if @calls.include?(call_id)
|
88
|
+
raise BatchError,
|
89
|
+
'A call with this ID already exists: %s' % call_id
|
90
|
+
end
|
91
|
+
@calls[call_id] = call
|
92
|
+
@order << call_id
|
93
|
+
if block_given?
|
94
|
+
@callbacks[call_id] = block
|
95
|
+
elsif @global_callback
|
96
|
+
@callbacks[call_id] = @global_callback
|
97
|
+
end
|
98
|
+
return self
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# Convert this batch request into an HTTP request.
|
103
|
+
#
|
104
|
+
# @return [Array<String, String, Hash, String>]
|
105
|
+
# An array consisting of, in order: HTTP method, request path, request
|
106
|
+
# headers and request body.
|
107
|
+
def to_http_request
|
108
|
+
return ['POST', request_uri, request_headers, request_body]
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Processes the HTTP response to the batch request, issuing callbacks.
|
113
|
+
#
|
114
|
+
# @param [Faraday::Response] response: the HTTP response.
|
115
|
+
def process_response(response)
|
116
|
+
content_type = find_header('Content-Type', response.headers)
|
117
|
+
boundary = /.*boundary=(.+)/.match(content_type)[1]
|
118
|
+
parts = response.body.split(/--#{Regexp.escape(boundary)}/)
|
119
|
+
parts = parts[1...-1]
|
120
|
+
parts.each do |part|
|
121
|
+
call_response = deserialize_call_response(part)
|
122
|
+
callback = @callbacks[call_response.call_id]
|
123
|
+
call = @calls[call_response.call_id]
|
124
|
+
result = Google::APIClient::Result.new(call, nil, call_response)
|
125
|
+
callback.call(result) if callback
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
##
|
132
|
+
# Helper method to find a header from its name, regardless of case.
|
133
|
+
#
|
134
|
+
# @param [String] name: The name of the header to find.
|
135
|
+
# @param [Hash] headers: The hash of headers and their values.
|
136
|
+
#
|
137
|
+
# @return [String] The value of the desired header.
|
138
|
+
def find_header(name, headers)
|
139
|
+
_, header = headers.detect do |h, v|
|
140
|
+
h.downcase == name.downcase
|
141
|
+
end
|
142
|
+
return header
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Create a new call ID. Uses an auto-incrementing, conflict-avoiding ID.
|
147
|
+
#
|
148
|
+
# @return [String] the new, unique ID.
|
149
|
+
def new_id
|
150
|
+
@last_auto_id += 1
|
151
|
+
while @calls.include?(@last_auto_id)
|
152
|
+
@last_auto_id += 1
|
153
|
+
end
|
154
|
+
return @last_auto_id.to_s
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Convert an id to a Content-ID header value.
|
159
|
+
#
|
160
|
+
# @param [String] call_id: identifier of individual call.
|
161
|
+
#
|
162
|
+
# @return [String]
|
163
|
+
# A Content-ID header with the call_id encoded into it. A UUID is
|
164
|
+
# prepended to the value because Content-ID headers are supposed to be
|
165
|
+
# universally unique.
|
166
|
+
def id_to_header(call_id)
|
167
|
+
if @base_id.nil?
|
168
|
+
# TODO(sgomes): Use SecureRandom.uuid, drop UUIDTools when we drop 1.8
|
169
|
+
@base_id = UUIDTools::UUID.random_create.to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
return '<%s+%s>' % [@base_id, Addressable::URI.encode(call_id)]
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# Convert a Content-ID header value to an id. Presumes the Content-ID
|
177
|
+
# header conforms to the format that id_to_header() returns.
|
178
|
+
#
|
179
|
+
# @param [String] header: Content-ID header value.
|
180
|
+
#
|
181
|
+
# @return [String] The extracted ID value.
|
182
|
+
def header_to_id(header)
|
183
|
+
if !header.start_with?('<') || !header.end_with?('>') ||
|
184
|
+
!header.include?('+')
|
185
|
+
raise BatchError, 'Invalid value for Content-ID: "%s"' % header
|
186
|
+
end
|
187
|
+
|
188
|
+
base, call_id = header[1...-1].split('+')
|
189
|
+
return Addressable::URI.unencode(call_id)
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Convert a single batched call into a string.
|
194
|
+
#
|
195
|
+
# @param [Google::APIClient::Reference] call: the call to serialize.
|
196
|
+
#
|
197
|
+
# @return [String] The request as a string in application/http format.
|
198
|
+
def serialize_call(call)
|
199
|
+
http_request = call.to_request
|
200
|
+
method = http_request.method.to_s.upcase
|
201
|
+
path = http_request.path.to_s
|
202
|
+
status_line = method + " " + path + " HTTP/1.1"
|
203
|
+
serialized_call = status_line
|
204
|
+
if http_request.headers
|
205
|
+
http_request.headers.each do |header, value|
|
206
|
+
serialized_call << "\r\n%s: %s" % [header, value]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
if http_request.body
|
210
|
+
serialized_call << "\r\n\r\n"
|
211
|
+
serialized_call << http_request.body
|
212
|
+
end
|
213
|
+
return serialized_call
|
214
|
+
end
|
215
|
+
|
216
|
+
##
|
217
|
+
# Auxiliary method to split the headers from the body in an HTTP response.
|
218
|
+
#
|
219
|
+
# @param [String] response: the response to parse.
|
220
|
+
#
|
221
|
+
# @return [Array<Hash>, String] The headers and the body, separately.
|
222
|
+
def split_headers_and_body(response)
|
223
|
+
headers = {}
|
224
|
+
payload = response.lstrip
|
225
|
+
while payload
|
226
|
+
line, payload = payload.split("\n", 2)
|
227
|
+
line.sub!(/\s+\z/, '')
|
228
|
+
break if line.empty?
|
229
|
+
match = /\A([^:]+):\s*/.match(line)
|
230
|
+
if match
|
231
|
+
headers[match[1]] = match.post_match
|
232
|
+
else
|
233
|
+
raise BatchError, 'Invalid header line in response: %s' % line
|
234
|
+
end
|
235
|
+
end
|
236
|
+
return headers, payload
|
237
|
+
end
|
238
|
+
|
239
|
+
##
|
240
|
+
# Convert a single batched response into a BatchedCallResponse object.
|
241
|
+
#
|
242
|
+
# @param [Google::APIClient::Reference] response:
|
243
|
+
# the request to deserialize.
|
244
|
+
#
|
245
|
+
# @return [BatchedCallResponse] The parsed and converted response.
|
246
|
+
def deserialize_call_response(call_response)
|
247
|
+
outer_headers, outer_body = split_headers_and_body(call_response)
|
248
|
+
status_line, payload = outer_body.split("\n", 2)
|
249
|
+
protocol, status, reason = status_line.split(' ', 3)
|
250
|
+
|
251
|
+
headers, body = split_headers_and_body(payload)
|
252
|
+
content_id = find_header('Content-ID', outer_headers)
|
253
|
+
call_id = header_to_id(content_id)
|
254
|
+
return BatchedCallResponse.new(call_id, status.to_i, headers, body)
|
255
|
+
end
|
256
|
+
|
257
|
+
##
|
258
|
+
# Return the request headers for the BatchRequest's HTTP request.
|
259
|
+
#
|
260
|
+
# @return [Hash] The HTTP headers.
|
261
|
+
def request_headers
|
262
|
+
return {
|
263
|
+
'Content-Type' => 'multipart/mixed; boundary=%s' % BATCH_BOUNDARY
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
##
|
268
|
+
# Return the request path for the BatchRequest's HTTP request.
|
269
|
+
#
|
270
|
+
# @return [String] The request path.
|
271
|
+
def request_uri
|
272
|
+
if @calls.nil? || @calls.empty?
|
273
|
+
raise BatchError, 'Cannot make an empty batch request'
|
274
|
+
end
|
275
|
+
# All APIs have the same batch path, so just get the first one.
|
276
|
+
return @calls.first[1].api_method.api.batch_path
|
277
|
+
end
|
278
|
+
|
279
|
+
##
|
280
|
+
# Return the request body for the BatchRequest's HTTP request.
|
281
|
+
#
|
282
|
+
# @return [String] The request body.
|
283
|
+
def request_body
|
284
|
+
body = ""
|
285
|
+
@order.each do |call_id|
|
286
|
+
body << "--" + BATCH_BOUNDARY + "\r\n"
|
287
|
+
body << "Content-Type: application/http\r\n"
|
288
|
+
body << "Content-ID: %s\r\n\r\n" % id_to_header(call_id)
|
289
|
+
body << serialize_call(@calls[call_id]) + "\r\n\r\n"
|
290
|
+
end
|
291
|
+
body << "--" + BATCH_BOUNDARY + "--"
|
292
|
+
return body
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|