google-api-client 0.4.7 → 0.5.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.
- data/CHANGELOG.md +11 -0
- data/Gemfile +6 -1
- data/Gemfile.lock +80 -0
- data/README.md +152 -45
- data/Rakefile +2 -2
- data/bin/google-api +2 -9
- data/lib/compat/multi_json.rb +2 -3
- data/lib/google/api_client.rb +87 -278
- data/lib/google/api_client/auth/jwt_asserter.rb +139 -0
- data/lib/google/api_client/auth/pkcs12.rb +48 -0
- data/lib/google/api_client/batch.rb +164 -136
- data/lib/google/api_client/client_secrets.rb +45 -1
- data/lib/google/api_client/discovery/api.rb +7 -8
- data/lib/google/api_client/discovery/method.rb +20 -27
- data/lib/google/api_client/discovery/resource.rb +16 -10
- data/lib/google/api_client/discovery/schema.rb +2 -0
- data/lib/google/api_client/media.rb +76 -64
- data/lib/google/api_client/reference.rb +7 -285
- data/lib/google/api_client/request.rb +336 -0
- data/lib/google/api_client/result.rb +147 -55
- data/lib/google/api_client/service_account.rb +2 -120
- data/lib/google/api_client/version.rb +2 -3
- data/spec/google/api_client/batch_spec.rb +9 -10
- data/spec/google/api_client/discovery_spec.rb +184 -114
- data/spec/google/api_client/media_spec.rb +27 -39
- data/spec/google/api_client/result_spec.rb +30 -11
- data/spec/google/api_client/service_account_spec.rb +38 -6
- data/spec/google/api_client_spec.rb +48 -32
- data/spec/spec_helper.rb +46 -0
- data/tasks/gem.rake +1 -0
- metadata +36 -70
@@ -20,8 +20,42 @@ require 'compat/multi_json'
|
|
20
20
|
module Google
|
21
21
|
class APIClient
|
22
22
|
##
|
23
|
-
# Manages the persistence of client configuration data and secrets.
|
23
|
+
# Manages the persistence of client configuration data and secrets. Format
|
24
|
+
# inspired by the Google API Python client.
|
25
|
+
#
|
26
|
+
# @see https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# {
|
30
|
+
# "web": {
|
31
|
+
# "client_id": "asdfjasdljfasdkjf",
|
32
|
+
# "client_secret": "1912308409123890",
|
33
|
+
# "redirect_uris": ["https://www.example.com/oauth2callback"],
|
34
|
+
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
35
|
+
# "token_uri": "https://accounts.google.com/o/oauth2/token"
|
36
|
+
# }
|
37
|
+
# }
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# {
|
41
|
+
# "installed": {
|
42
|
+
# "client_id": "837647042410-75ifg...usercontent.com",
|
43
|
+
# "client_secret":"asdlkfjaskd",
|
44
|
+
# "redirect_uris": ["http://localhost", "urn:ietf:oauth:2.0:oob"],
|
45
|
+
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
46
|
+
# "token_uri": "https://accounts.google.com/o/oauth2/token"
|
47
|
+
# }
|
48
|
+
# }
|
24
49
|
class ClientSecrets
|
50
|
+
|
51
|
+
##
|
52
|
+
# Reads client configuration from a file
|
53
|
+
#
|
54
|
+
# @param [String] filename
|
55
|
+
# Path to file to load
|
56
|
+
#
|
57
|
+
# @return [Google::APIClient::ClientSecrets]
|
58
|
+
# OAuth client settings
|
25
59
|
def self.load(filename=nil)
|
26
60
|
if filename && File.directory?(filename)
|
27
61
|
search_path = File.expand_path(filename)
|
@@ -44,6 +78,11 @@ module Google
|
|
44
78
|
return self.new(data)
|
45
79
|
end
|
46
80
|
|
81
|
+
##
|
82
|
+
# Intialize OAuth client settings.
|
83
|
+
#
|
84
|
+
# @param [Hash] options
|
85
|
+
# Parsed client secrets files
|
47
86
|
def initialize(options={})
|
48
87
|
# Client auth configuration
|
49
88
|
@flow = options[:flow] || options.keys.first.to_s || 'web'
|
@@ -77,6 +116,11 @@ module Google
|
|
77
116
|
:refresh_token, :id_token, :expires_in, :expires_at, :issued_at
|
78
117
|
)
|
79
118
|
|
119
|
+
##
|
120
|
+
# Serialize back to the original JSON form
|
121
|
+
#
|
122
|
+
# @return [String]
|
123
|
+
# JSON
|
80
124
|
def to_json
|
81
125
|
return MultiJson.dump({
|
82
126
|
self.flow => ({
|
@@ -29,13 +29,9 @@ module Google
|
|
29
29
|
##
|
30
30
|
# Creates a description of a particular version of a service.
|
31
31
|
#
|
32
|
-
# @param [String]
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# should not be assumed. There is no requirement that these match.
|
36
|
-
# @param [String] version
|
37
|
-
# The identifier for the service version.
|
38
|
-
# @param [Hash] api_description
|
32
|
+
# @param [String] document_base
|
33
|
+
# Base URI for the service
|
34
|
+
# @param [Hash] discovery_document
|
39
35
|
# The section of the discovery document that applies to this service
|
40
36
|
# version.
|
41
37
|
#
|
@@ -57,6 +53,9 @@ module Google
|
|
57
53
|
end
|
58
54
|
end
|
59
55
|
end
|
56
|
+
|
57
|
+
# @return [String] unparsed discovery document for the API
|
58
|
+
attr_reader :discovery_document
|
60
59
|
|
61
60
|
##
|
62
61
|
# Returns the id of the service.
|
@@ -159,7 +158,7 @@ module Google
|
|
159
158
|
##
|
160
159
|
# Updates the hierarchy of resources and methods with the new base.
|
161
160
|
#
|
162
|
-
# @param [Addressable::URI, #to_str, String]
|
161
|
+
# @param [Addressable::URI, #to_str, String] new_method_base
|
163
162
|
# The new base URI to use for the service.
|
164
163
|
def method_base=(new_method_base)
|
165
164
|
@method_base = Addressable::URI.parse(new_method_base)
|
@@ -34,7 +34,7 @@ module Google
|
|
34
34
|
# The base URI for the service.
|
35
35
|
# @param [String] method_name
|
36
36
|
# The identifier for the method.
|
37
|
-
# @param [Hash]
|
37
|
+
# @param [Hash] discovery_document
|
38
38
|
# The section of the discovery document that applies to this method.
|
39
39
|
#
|
40
40
|
# @return [Google::APIClient::Method] The constructed method object.
|
@@ -45,6 +45,9 @@ module Google
|
|
45
45
|
@discovery_document = discovery_document
|
46
46
|
end
|
47
47
|
|
48
|
+
# @return [String] unparsed discovery document for the method
|
49
|
+
attr_reader :discovery_document
|
50
|
+
|
48
51
|
##
|
49
52
|
# Returns the API this method belongs to.
|
50
53
|
#
|
@@ -57,13 +60,6 @@ module Google
|
|
57
60
|
# @return [String] The method identifier.
|
58
61
|
attr_reader :name
|
59
62
|
|
60
|
-
##
|
61
|
-
# Returns the parsed section of the discovery document that applies to
|
62
|
-
# this method.
|
63
|
-
#
|
64
|
-
# @return [Hash] The method description.
|
65
|
-
attr_reader :description
|
66
|
-
|
67
63
|
##
|
68
64
|
# Returns the base URI for the method.
|
69
65
|
#
|
@@ -74,13 +70,21 @@ module Google
|
|
74
70
|
##
|
75
71
|
# Updates the method with the new base.
|
76
72
|
#
|
77
|
-
# @param [Addressable::URI, #to_str, String]
|
73
|
+
# @param [Addressable::URI, #to_str, String] new_method_base
|
78
74
|
# The new base URI to use for the method.
|
79
75
|
def method_base=(new_method_base)
|
80
76
|
@method_base = Addressable::URI.parse(new_method_base)
|
81
77
|
@uri_template = nil
|
82
78
|
end
|
83
79
|
|
80
|
+
##
|
81
|
+
# Returns a human-readable description of the method.
|
82
|
+
#
|
83
|
+
# @return [Hash] The API description.
|
84
|
+
def description
|
85
|
+
return @discovery_document['description']
|
86
|
+
end
|
87
|
+
|
84
88
|
##
|
85
89
|
# Returns the method ID.
|
86
90
|
#
|
@@ -176,6 +180,7 @@ module Google
|
|
176
180
|
##
|
177
181
|
# Expands the method's URI template using a parameter list.
|
178
182
|
#
|
183
|
+
# @api private
|
179
184
|
# @param [Hash, Array] parameters
|
180
185
|
# The parameter list to use.
|
181
186
|
#
|
@@ -214,6 +219,7 @@ module Google
|
|
214
219
|
##
|
215
220
|
# Generates an HTTP request for this method.
|
216
221
|
#
|
222
|
+
# @api private
|
217
223
|
# @param [Hash, Array] parameters
|
218
224
|
# The parameters to send.
|
219
225
|
# @param [String, StringIO] body The body for the HTTP request.
|
@@ -222,28 +228,14 @@ module Google
|
|
222
228
|
# The HTTP connection to use.
|
223
229
|
#
|
224
230
|
# @return [Array] The generated HTTP request.
|
225
|
-
def generate_request(parameters={}, body='', headers=
|
226
|
-
options[:connection] ||= Faraday.default_connection
|
227
|
-
if body.respond_to?(:string)
|
228
|
-
body = body.string
|
229
|
-
elsif body.respond_to?(:to_str)
|
230
|
-
body = body.to_str
|
231
|
-
else
|
232
|
-
raise TypeError, "Expected String or StringIO, got #{body.class}."
|
233
|
-
end
|
231
|
+
def generate_request(parameters={}, body='', headers={}, options={})
|
234
232
|
if !headers.kind_of?(Array) && !headers.kind_of?(Hash)
|
235
233
|
raise TypeError, "Expected Hash or Array, got #{headers.class}."
|
236
234
|
end
|
237
|
-
method = self.http_method
|
235
|
+
method = self.http_method.to_s.downcase.to_sym
|
238
236
|
uri = self.generate_uri(parameters)
|
239
|
-
headers =
|
240
|
-
return
|
241
|
-
method.to_s.downcase.to_sym
|
242
|
-
) do |req|
|
243
|
-
req.url(Addressable::URI.parse(uri).normalize.to_s)
|
244
|
-
req.headers = Faraday::Utils::Headers.new(headers)
|
245
|
-
req.body = body
|
246
|
-
end
|
237
|
+
headers = Faraday::Utils::Headers.new(headers)
|
238
|
+
return [method, uri, headers, body]
|
247
239
|
end
|
248
240
|
|
249
241
|
|
@@ -302,6 +294,7 @@ module Google
|
|
302
294
|
# Verifies that the parameters are valid for this method. Raises an
|
303
295
|
# exception if validation fails.
|
304
296
|
#
|
297
|
+
# @api private
|
305
298
|
# @param [Hash, Array] parameters
|
306
299
|
# The parameters to verify.
|
307
300
|
#
|
@@ -28,11 +28,13 @@ module Google
|
|
28
28
|
##
|
29
29
|
# Creates a description of a particular version of a resource.
|
30
30
|
#
|
31
|
-
# @param [
|
31
|
+
# @param [Google::APIClient::API] api
|
32
|
+
# The API this resource belongs to.
|
33
|
+
# @param [Addressable::URI] method_base
|
32
34
|
# The base URI for the service.
|
33
35
|
# @param [String] resource_name
|
34
36
|
# The identifier for the resource.
|
35
|
-
# @param [Hash]
|
37
|
+
# @param [Hash] discovery_document
|
36
38
|
# The section of the discovery document that applies to this resource.
|
37
39
|
#
|
38
40
|
# @return [Google::APIClient::Resource] The constructed resource object.
|
@@ -56,29 +58,33 @@ module Google
|
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
61
|
+
# @return [String] unparsed discovery document for the resource
|
62
|
+
attr_reader :discovery_document
|
63
|
+
|
59
64
|
##
|
60
65
|
# Returns the identifier for the resource.
|
61
66
|
#
|
62
67
|
# @return [String] The resource identifier.
|
63
68
|
attr_reader :name
|
64
69
|
|
65
|
-
##
|
66
|
-
# Returns the parsed section of the discovery document that applies to
|
67
|
-
# this resource.
|
68
|
-
#
|
69
|
-
# @return [Hash] The resource description.
|
70
|
-
attr_reader :description
|
71
|
-
|
72
70
|
##
|
73
71
|
# Returns the base URI for this resource.
|
74
72
|
#
|
75
73
|
# @return [Addressable::URI] The base URI that methods are joined to.
|
76
74
|
attr_reader :method_base
|
77
75
|
|
76
|
+
##
|
77
|
+
# Returns a human-readable description of the resource.
|
78
|
+
#
|
79
|
+
# @return [Hash] The API description.
|
80
|
+
def description
|
81
|
+
return @discovery_document['description']
|
82
|
+
end
|
83
|
+
|
78
84
|
##
|
79
85
|
# Updates the hierarchy of resources and methods with the new base.
|
80
86
|
#
|
81
|
-
# @param [Addressable::URI, #to_str, String]
|
87
|
+
# @param [Addressable::URI, #to_str, String] new_method_base
|
82
88
|
# The new base URI to use for the resource.
|
83
89
|
def method_base=(new_method_base)
|
84
90
|
@method_base = Addressable::URI.parse(new_method_base)
|
@@ -11,6 +11,7 @@
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
|
+
require 'google/api_client/reference'
|
14
15
|
|
15
16
|
module Google
|
16
17
|
class APIClient
|
@@ -23,7 +24,8 @@ module Google
|
|
23
24
|
class UploadIO < Faraday::UploadIO
|
24
25
|
##
|
25
26
|
# Get the length of the stream
|
26
|
-
#
|
27
|
+
#
|
28
|
+
# @return [Fixnum]
|
27
29
|
# Length of stream, in bytes
|
28
30
|
def length
|
29
31
|
io.respond_to?(:length) ? io.length : File.size(local_path)
|
@@ -33,41 +35,35 @@ module Google
|
|
33
35
|
##
|
34
36
|
# Resumable uploader.
|
35
37
|
#
|
36
|
-
class ResumableUpload
|
37
|
-
|
38
|
-
attr_accessor :client
|
38
|
+
class ResumableUpload < Request
|
39
|
+
# @return [Fixnum] Max bytes to send in a single request
|
39
40
|
attr_accessor :chunk_size
|
40
|
-
attr_accessor :media
|
41
|
-
attr_accessor :location
|
42
41
|
|
43
42
|
##
|
44
43
|
# Creates a new uploader.
|
45
44
|
#
|
46
|
-
# @param [
|
47
|
-
#
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
self.media = media
|
54
|
-
self.location = location
|
55
|
-
self.chunk_size = 256 * 1024
|
56
|
-
|
57
|
-
@api_method = result.reference.api_method
|
58
|
-
@result = result
|
59
|
-
@offset = 0
|
45
|
+
# @param [Hash] options
|
46
|
+
# Request options
|
47
|
+
def initialize(options={})
|
48
|
+
super options
|
49
|
+
self.uri = options[:uri]
|
50
|
+
self.http_method = :put
|
51
|
+
@offset = options[:offset] || 0
|
60
52
|
@complete = false
|
53
|
+
@expired = false
|
61
54
|
end
|
62
55
|
|
63
56
|
##
|
64
57
|
# Sends all remaining chunks to the server
|
65
58
|
#
|
59
|
+
# @deprecated Pass the instance to {Google::APIClient#execute} instead
|
60
|
+
#
|
66
61
|
# @param [Google::APIClient] api_client
|
67
62
|
# API Client instance to use for sending
|
68
63
|
def send_all(api_client)
|
64
|
+
result = nil
|
69
65
|
until complete?
|
70
|
-
send_chunk(api_client)
|
66
|
+
result = send_chunk(api_client)
|
71
67
|
break unless result.status == 308
|
72
68
|
end
|
73
69
|
return result
|
@@ -77,28 +73,12 @@ module Google
|
|
77
73
|
##
|
78
74
|
# Sends the next chunk to the server
|
79
75
|
#
|
76
|
+
# @deprecated Pass the instance to {Google::APIClient#execute} instead
|
77
|
+
#
|
80
78
|
# @param [Google::APIClient] api_client
|
81
79
|
# API Client instance to use for sending
|
82
80
|
def send_chunk(api_client)
|
83
|
-
|
84
|
-
return resync_range(api_client)
|
85
|
-
end
|
86
|
-
|
87
|
-
start_offset = @offset
|
88
|
-
self.media.io.pos = start_offset
|
89
|
-
chunk = self.media.io.read(chunk_size)
|
90
|
-
content_length = chunk.bytesize
|
91
|
-
|
92
|
-
end_offset = start_offset + content_length - 1
|
93
|
-
@result = api_client.execute(
|
94
|
-
:uri => self.location,
|
95
|
-
:http_method => :put,
|
96
|
-
:headers => {
|
97
|
-
'Content-Length' => "#{content_length}",
|
98
|
-
'Content-Type' => self.media.content_type,
|
99
|
-
'Content-Range' => "bytes #{start_offset}-#{end_offset}/#{media.length}" },
|
100
|
-
:body => chunk)
|
101
|
-
return process_result(@result)
|
81
|
+
return api_client.execute(self)
|
102
82
|
end
|
103
83
|
|
104
84
|
##
|
@@ -117,54 +97,86 @@ module Google
|
|
117
97
|
# @return [TrueClass, FalseClass]
|
118
98
|
# Whether or not the upload has expired and can not be resumed
|
119
99
|
def expired?
|
120
|
-
return @
|
100
|
+
return @expired
|
121
101
|
end
|
122
102
|
|
123
103
|
##
|
124
|
-
#
|
125
|
-
# and the offset is not known.
|
104
|
+
# Check if upload is resumable. That is, neither complete nor expired
|
126
105
|
#
|
127
|
-
# @
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
106
|
+
# @return [TrueClass, FalseClass] True if upload can be resumed
|
107
|
+
def resumable?
|
108
|
+
return !(self.complete? or self.expired?)
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Convert to an HTTP request. Returns components in order of method, URI,
|
113
|
+
# request headers, and body
|
114
|
+
#
|
115
|
+
# @api private
|
116
|
+
#
|
117
|
+
# @return [Array<(Symbol, Addressable::URI, Hash, [#read,#to_str])>]
|
118
|
+
def to_http_request
|
119
|
+
if @complete
|
120
|
+
raise Google::APIClient::ClientError, "Upload already complete"
|
121
|
+
elsif @offset.nil?
|
122
|
+
self.headers.update({
|
134
123
|
'Content-Length' => "0",
|
135
124
|
'Content-Range' => "bytes */#{media.length}" })
|
136
|
-
|
125
|
+
else
|
126
|
+
start_offset = @offset
|
127
|
+
self.media.io.pos = start_offset
|
128
|
+
chunk = self.media.io.read(chunk_size)
|
129
|
+
content_length = chunk.bytesize
|
130
|
+
end_offset = start_offset + content_length - 1
|
131
|
+
|
132
|
+
self.headers.update({
|
133
|
+
'Content-Length' => "#{content_length}",
|
134
|
+
'Content-Type' => self.media.content_type,
|
135
|
+
'Content-Range' => "bytes #{start_offset}-#{end_offset}/#{media.length}" })
|
136
|
+
self.body = chunk
|
137
|
+
end
|
138
|
+
super
|
137
139
|
end
|
138
140
|
|
139
141
|
##
|
140
142
|
# Check the result from the server, updating the offset and/or location
|
141
143
|
# if available.
|
142
144
|
#
|
143
|
-
# @
|
144
|
-
#
|
145
|
-
|
146
|
-
|
145
|
+
# @api private
|
146
|
+
#
|
147
|
+
# @param [Faraday::Response] response
|
148
|
+
# HTTP response
|
149
|
+
#
|
150
|
+
# @return [Google::APIClient::Result]
|
151
|
+
# Processed API response
|
152
|
+
def process_http_response(response)
|
153
|
+
case response.status
|
147
154
|
when 200...299
|
148
155
|
@complete = true
|
149
|
-
if @api_method
|
150
|
-
# Inject the original API method so data is parsed correctly
|
151
|
-
result.reference.api_method = @api_method
|
152
|
-
end
|
153
|
-
return result
|
154
156
|
when 308
|
155
|
-
range =
|
157
|
+
range = response.headers['range']
|
156
158
|
if range
|
157
159
|
@offset = range.scan(/\d+/).collect{|x| Integer(x)}.last + 1
|
158
160
|
end
|
159
|
-
if
|
160
|
-
self.
|
161
|
+
if response.headers['location']
|
162
|
+
self.uri = response.headers['location']
|
161
163
|
end
|
164
|
+
when 400...499
|
165
|
+
@expired = true
|
162
166
|
when 500...599
|
163
167
|
# Invalidate the offset to mark it needs to be queried on the
|
164
168
|
# next request
|
165
169
|
@offset = nil
|
166
170
|
end
|
167
|
-
return
|
171
|
+
return Google::APIClient::Result.new(self, response)
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# Hashified verison of the API request
|
176
|
+
#
|
177
|
+
# @return [Hash]
|
178
|
+
def to_hash
|
179
|
+
super.merge(:offset => @offset)
|
168
180
|
end
|
169
181
|
|
170
182
|
end
|