google-api-client 0.4.7 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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] api
33
- # The identifier for the service. Note that while this frequently
34
- # matches the first segment of all of the service's RPC names, this
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] new_base
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] method_description
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] new_base
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=[], options={})
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 = headers.to_a if headers.kind_of?(Hash)
240
- return options[:connection].build_request(
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 [Addressable::URI] base
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] resource_description
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] new_base
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)
@@ -27,6 +27,8 @@ require 'google/api_client/errors'
27
27
 
28
28
  module Google
29
29
  class APIClient
30
+ ##
31
+ # @api private
30
32
  module Schema
31
33
  def self.parse(api, schema_data)
32
34
  # This method is super-long, but hard to break up due to the
@@ -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
- # @return [Integer]
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
- attr_reader :result
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 [Google::APIClient::Result] result
47
- # Result of the initial request that started the upload
48
- # @param [Google::APIClient::UploadIO] media
49
- # Media to upload
50
- # @param [String] location
51
- # URL to upload to
52
- def initialize(result, media, location)
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
- if @offset.nil?
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 @result.status == 404 || @result.status == 410
100
+ return @expired
121
101
  end
122
102
 
123
103
  ##
124
- # Get the last saved range from the server in case an error occurred
125
- # and the offset is not known.
104
+ # Check if upload is resumable. That is, neither complete nor expired
126
105
  #
127
- # @param [Google::APIClient] api_client
128
- # API Client instance to use for sending
129
- def resync_range(api_client)
130
- r = api_client.execute(
131
- :uri => self.location,
132
- :http_method => :put,
133
- :headers => {
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
- return process_result(r)
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
- # @param [Google::APIClient::Result] r
144
- # Result of a chunk upload or range query
145
- def process_result(result)
146
- case result.status
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 = result.headers['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 result.headers['location']
160
- self.location = result.headers['location']
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 nil
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