jomz-google-api-client 0.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.
Files changed (63) hide show
  1. data/CHANGELOG.md +144 -0
  2. data/CONTRIBUTING.md +32 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE +202 -0
  5. data/README.md +192 -0
  6. data/Rakefile +46 -0
  7. data/lib/cacerts.pem +2183 -0
  8. data/lib/compat/multi_json.rb +16 -0
  9. data/lib/google/api_client.rb +672 -0
  10. data/lib/google/api_client/auth/compute_service_account.rb +28 -0
  11. data/lib/google/api_client/auth/file_storage.rb +87 -0
  12. data/lib/google/api_client/auth/installed_app.rb +122 -0
  13. data/lib/google/api_client/auth/jwt_asserter.rb +126 -0
  14. data/lib/google/api_client/auth/key_utils.rb +93 -0
  15. data/lib/google/api_client/auth/pkcs12.rb +41 -0
  16. data/lib/google/api_client/batch.rb +323 -0
  17. data/lib/google/api_client/client_secrets.rb +176 -0
  18. data/lib/google/api_client/discovery.rb +19 -0
  19. data/lib/google/api_client/discovery/api.rb +300 -0
  20. data/lib/google/api_client/discovery/media.rb +77 -0
  21. data/lib/google/api_client/discovery/method.rb +363 -0
  22. data/lib/google/api_client/discovery/resource.rb +156 -0
  23. data/lib/google/api_client/discovery/schema.rb +121 -0
  24. data/lib/google/api_client/environment.rb +42 -0
  25. data/lib/google/api_client/errors.rb +60 -0
  26. data/lib/google/api_client/gzip.rb +28 -0
  27. data/lib/google/api_client/logging.rb +32 -0
  28. data/lib/google/api_client/media.rb +259 -0
  29. data/lib/google/api_client/railtie.rb +16 -0
  30. data/lib/google/api_client/reference.rb +27 -0
  31. data/lib/google/api_client/request.rb +351 -0
  32. data/lib/google/api_client/result.rb +253 -0
  33. data/lib/google/api_client/service.rb +233 -0
  34. data/lib/google/api_client/service/batch.rb +103 -0
  35. data/lib/google/api_client/service/request.rb +144 -0
  36. data/lib/google/api_client/service/resource.rb +40 -0
  37. data/lib/google/api_client/service/result.rb +162 -0
  38. data/lib/google/api_client/service/simple_file_store.rb +151 -0
  39. data/lib/google/api_client/service/stub_generator.rb +59 -0
  40. data/lib/google/api_client/service_account.rb +18 -0
  41. data/lib/google/api_client/version.rb +31 -0
  42. data/lib/google/inflection.rb +28 -0
  43. data/spec/fixtures/files/privatekey.p12 +0 -0
  44. data/spec/fixtures/files/sample.txt +33 -0
  45. data/spec/fixtures/files/secret.pem +19 -0
  46. data/spec/google/api_client/batch_spec.rb +249 -0
  47. data/spec/google/api_client/discovery_spec.rb +652 -0
  48. data/spec/google/api_client/gzip_spec.rb +86 -0
  49. data/spec/google/api_client/media_spec.rb +179 -0
  50. data/spec/google/api_client/request_spec.rb +30 -0
  51. data/spec/google/api_client/result_spec.rb +203 -0
  52. data/spec/google/api_client/service_account_spec.rb +164 -0
  53. data/spec/google/api_client/service_spec.rb +586 -0
  54. data/spec/google/api_client/simple_file_store_spec.rb +137 -0
  55. data/spec/google/api_client_spec.rb +253 -0
  56. data/spec/spec_helper.rb +56 -0
  57. data/tasks/gem.rake +97 -0
  58. data/tasks/git.rake +45 -0
  59. data/tasks/metrics.rake +22 -0
  60. data/tasks/spec.rake +57 -0
  61. data/tasks/wiki.rake +82 -0
  62. data/tasks/yard.rake +29 -0
  63. metadata +309 -0
@@ -0,0 +1,156 @@
1
+ # Copyright 2010 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
+
16
+ require 'addressable/uri'
17
+
18
+ require 'google/inflection'
19
+ require 'google/api_client/discovery/method'
20
+
21
+
22
+ module Google
23
+ class APIClient
24
+ ##
25
+ # A resource that has been described by a discovery document.
26
+ class Resource
27
+
28
+ ##
29
+ # Creates a description of a particular version of a resource.
30
+ #
31
+ # @param [Google::APIClient::API] api
32
+ # The API this resource belongs to.
33
+ # @param [Addressable::URI] method_base
34
+ # The base URI for the service.
35
+ # @param [String] resource_name
36
+ # The identifier for the resource.
37
+ # @param [Hash] discovery_document
38
+ # The section of the discovery document that applies to this resource.
39
+ #
40
+ # @return [Google::APIClient::Resource] The constructed resource object.
41
+ def initialize(api, method_base, resource_name, discovery_document)
42
+ @api = api
43
+ @method_base = method_base
44
+ @name = resource_name
45
+ @discovery_document = discovery_document
46
+ metaclass = (class <<self; self; end)
47
+ self.discovered_resources.each do |resource|
48
+ method_name = Google::INFLECTOR.underscore(resource.name).to_sym
49
+ if !self.respond_to?(method_name)
50
+ metaclass.send(:define_method, method_name) { resource }
51
+ end
52
+ end
53
+ self.discovered_methods.each do |method|
54
+ method_name = Google::INFLECTOR.underscore(method.name).to_sym
55
+ if !self.respond_to?(method_name)
56
+ metaclass.send(:define_method, method_name) { method }
57
+ end
58
+ end
59
+ end
60
+
61
+ # @return [String] unparsed discovery document for the resource
62
+ attr_reader :discovery_document
63
+
64
+ ##
65
+ # Returns the identifier for the resource.
66
+ #
67
+ # @return [String] The resource identifier.
68
+ attr_reader :name
69
+
70
+ ##
71
+ # Returns the base URI for this resource.
72
+ #
73
+ # @return [Addressable::URI] The base URI that methods are joined to.
74
+ attr_reader :method_base
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
+
84
+ ##
85
+ # Updates the hierarchy of resources and methods with the new base.
86
+ #
87
+ # @param [Addressable::URI, #to_str, String] new_method_base
88
+ # The new base URI to use for the resource.
89
+ def method_base=(new_method_base)
90
+ @method_base = Addressable::URI.parse(new_method_base)
91
+ self.discovered_resources.each do |resource|
92
+ resource.method_base = @method_base
93
+ end
94
+ self.discovered_methods.each do |method|
95
+ method.method_base = @method_base
96
+ end
97
+ end
98
+
99
+ ##
100
+ # A list of sub-resources available on this resource.
101
+ #
102
+ # @return [Array] A list of {Google::APIClient::Resource} objects.
103
+ def discovered_resources
104
+ return @discovered_resources ||= (
105
+ (@discovery_document['resources'] || []).inject([]) do |accu, (k, v)|
106
+ accu << Google::APIClient::Resource.new(
107
+ @api, self.method_base, k, v
108
+ )
109
+ accu
110
+ end
111
+ )
112
+ end
113
+
114
+ ##
115
+ # A list of methods available on this resource.
116
+ #
117
+ # @return [Array] A list of {Google::APIClient::Method} objects.
118
+ def discovered_methods
119
+ return @discovered_methods ||= (
120
+ (@discovery_document['methods'] || []).inject([]) do |accu, (k, v)|
121
+ accu << Google::APIClient::Method.new(@api, self.method_base, k, v)
122
+ accu
123
+ end
124
+ )
125
+ end
126
+
127
+ ##
128
+ # Converts the resource to a flat mapping of RPC names and method
129
+ # objects.
130
+ #
131
+ # @return [Hash] All methods available on the resource.
132
+ def to_h
133
+ return @hash ||= (begin
134
+ methods_hash = {}
135
+ self.discovered_methods.each do |method|
136
+ methods_hash[method.id] = method
137
+ end
138
+ self.discovered_resources.each do |resource|
139
+ methods_hash.merge!(resource.to_h)
140
+ end
141
+ methods_hash
142
+ end)
143
+ end
144
+
145
+ ##
146
+ # Returns a <code>String</code> representation of the resource's state.
147
+ #
148
+ # @return [String] The resource's state, as a <code>String</code>.
149
+ def inspect
150
+ sprintf(
151
+ "#<%s:%#0x NAME:%s>", self.class.to_s, self.object_id, self.name
152
+ )
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,121 @@
1
+ # Copyright 2010 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
+
16
+ require 'time'
17
+ require 'multi_json'
18
+ require 'compat/multi_json'
19
+ require 'base64'
20
+ require 'autoparse'
21
+ require 'addressable/uri'
22
+ require 'addressable/template'
23
+
24
+ require 'google/inflection'
25
+ require 'google/api_client/errors'
26
+
27
+
28
+ module Google
29
+ class APIClient
30
+ ##
31
+ # @api private
32
+ module Schema
33
+ def self.parse(api, schema_data)
34
+ # This method is super-long, but hard to break up due to the
35
+ # unavoidable dependence on closures and execution context.
36
+ schema_name = schema_data['id']
37
+
38
+ # Due to an oversight, schema IDs may not be URI references.
39
+ # TODO(bobaman): Remove this code once this has been resolved.
40
+ schema_uri = (
41
+ api.document_base +
42
+ (schema_name[0..0] != '#' ? '#' + schema_name : schema_name)
43
+ )
44
+ # puts schema_uri
45
+
46
+ # Due to an oversight, schema IDs may not be URI references.
47
+ # TODO(bobaman): Remove this whole lambda once this has been resolved.
48
+ reformat_references = lambda do |data|
49
+ # This code is not particularly efficient due to recursive traversal
50
+ # and excess object creation, but this hopefully shouldn't be an
51
+ # issue since it should only be called only once per schema per
52
+ # process.
53
+ if data.kind_of?(Hash) &&
54
+ data['$ref'] && !data['$ref'].kind_of?(Hash)
55
+ if data['$ref'].respond_to?(:to_str)
56
+ reference = data['$ref'].to_str
57
+ else
58
+ raise TypeError, "Expected String, got #{data['$ref'].class}"
59
+ end
60
+ reference = '#' + reference if reference[0..0] != '#'
61
+ data.merge({
62
+ '$ref' => reference
63
+ })
64
+ elsif data.kind_of?(Hash)
65
+ data.inject({}) do |accu, (key, value)|
66
+ if value.kind_of?(Hash)
67
+ accu[key] = reformat_references.call(value)
68
+ else
69
+ accu[key] = value
70
+ end
71
+ accu
72
+ end
73
+ else
74
+ data
75
+ end
76
+ end
77
+ schema_data = reformat_references.call(schema_data)
78
+ # puts schema_data.inspect
79
+
80
+ if schema_name
81
+ api_name_string =
82
+ Google::INFLECTOR.camelize(api.name)
83
+ api_version_string =
84
+ Google::INFLECTOR.camelize(api.version).gsub('.', '_')
85
+ # This is for compatibility with Ruby 1.8.7.
86
+ # TODO(bobaman) Remove this when we eventually stop supporting 1.8.7.
87
+ args = []
88
+ args << false if Class.method(:const_defined?).arity != 1
89
+ if Google::APIClient::Schema.const_defined?(api_name_string, *args)
90
+ api_name = Google::APIClient::Schema.const_get(
91
+ api_name_string, *args
92
+ )
93
+ else
94
+ api_name = Google::APIClient::Schema.const_set(
95
+ api_name_string, Module.new
96
+ )
97
+ end
98
+ if api_name.const_defined?(api_version_string, *args)
99
+ api_version = api_name.const_get(api_version_string, *args)
100
+ else
101
+ api_version = api_name.const_set(api_version_string, Module.new)
102
+ end
103
+ if api_version.const_defined?(schema_name, *args)
104
+ schema_class = api_version.const_get(schema_name, *args)
105
+ end
106
+ end
107
+
108
+ # It's possible the schema has already been defined. If so, don't
109
+ # redefine it. This means that reloading a schema which has already
110
+ # been loaded into memory is not possible.
111
+ unless schema_class
112
+ schema_class = AutoParse.generate(schema_data, :uri => schema_uri)
113
+ if schema_name
114
+ api_version.const_set(schema_name, schema_class)
115
+ end
116
+ end
117
+ return schema_class
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,42 @@
1
+ # Copyright 2010 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
+
16
+ module Google
17
+ class APIClient
18
+ module ENV
19
+ OS_VERSION = begin
20
+ if RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/
21
+ # TODO(bobaman)
22
+ # Confirm that all of these Windows environments actually have access
23
+ # to the `ver` command.
24
+ `ver`.sub(/\s*\[Version\s*/, '/').sub(']', '').strip
25
+ elsif RUBY_PLATFORM =~ /darwin/i
26
+ "Mac OS X/#{`sw_vers -productVersion`}"
27
+ elsif RUBY_PLATFORM == 'java'
28
+ # Get the information from java system properties to avoid spawning a
29
+ # sub-process, which is not friendly in some contexts (web servers).
30
+ require 'java'
31
+ name = java.lang.System.getProperty('os.name')
32
+ version = java.lang.System.getProperty('os.version')
33
+ "#{name}/#{version}"
34
+ else
35
+ `uname -sr`.sub(' ', '/')
36
+ end
37
+ rescue Exception
38
+ RUBY_PLATFORM
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,60 @@
1
+ # Copyright 2010 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
+
16
+ module Google
17
+ class APIClient
18
+ ##
19
+ # An error which is raised when there is an unexpected response or other
20
+ # transport error that prevents an operation from succeeding.
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
33
+ end
34
+
35
+ ##
36
+ # An exception that is raised if a method is called with missing or
37
+ # invalid parameter values.
38
+ class ValidationError < StandardError
39
+ end
40
+
41
+ ##
42
+ # A 4xx class HTTP error occurred.
43
+ class ClientError < TransmissionError
44
+ end
45
+
46
+ ##
47
+ # A 5xx class HTTP error occurred.
48
+ class ServerError < TransmissionError
49
+ end
50
+
51
+ ##
52
+ # An exception that is raised if an ID token could not be validated.
53
+ class InvalidIDTokenError < StandardError
54
+ end
55
+
56
+ # Error class for problems in batch requests.
57
+ class BatchError < StandardError
58
+ end
59
+ end
60
+ end
@@ -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
@@ -0,0 +1,32 @@
1
+ require 'logger'
2
+
3
+ module Google
4
+ class APIClient
5
+
6
+ class << self
7
+ ##
8
+ # Logger for the API client
9
+ #
10
+ # @return [Logger] logger instance.
11
+ attr_accessor :logger
12
+ end
13
+
14
+ self.logger = Logger.new(STDOUT)
15
+ self.logger.level = Logger::WARN
16
+
17
+ ##
18
+ # Module to make accessing the logger simpler
19
+ module Logging
20
+ ##
21
+ # Logger for the API client
22
+ #
23
+ # @return [Logger] logger instance.
24
+ def logger
25
+ Google::APIClient.logger
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+
32
+ end
@@ -0,0 +1,259 @@
1
+ # Copyright 2010 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
+ require 'google/api_client/reference'
15
+
16
+ module Google
17
+ class APIClient
18
+ ##
19
+ # Uploadable media support. Holds an IO stream & content type.
20
+ #
21
+ # @see Faraday::UploadIO
22
+ # @example
23
+ # media = Google::APIClient::UploadIO.new('mymovie.m4v', 'video/mp4')
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
+
29
+ ##
30
+ # Get the length of the stream
31
+ #
32
+ # @return [Fixnum]
33
+ # Length of stream, in bytes
34
+ def length
35
+ io.respond_to?(:length) ? io.length : File.size(local_path)
36
+ end
37
+ end
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
+
110
+ ##
111
+ # Resumable uploader.
112
+ #
113
+ class ResumableUpload < Request
114
+ # @return [Fixnum] Max bytes to send in a single request
115
+ attr_accessor :chunk_size
116
+
117
+ ##
118
+ # Creates a new uploader.
119
+ #
120
+ # @param [Hash] options
121
+ # Request options
122
+ def initialize(options={})
123
+ super options
124
+ self.uri = options[:uri]
125
+ self.http_method = :put
126
+ @offset = options[:offset] || 0
127
+ @complete = false
128
+ @expired = false
129
+ end
130
+
131
+ ##
132
+ # Sends all remaining chunks to the server
133
+ #
134
+ # @deprecated Pass the instance to {Google::APIClient#execute} instead
135
+ #
136
+ # @param [Google::APIClient] api_client
137
+ # API Client instance to use for sending
138
+ def send_all(api_client)
139
+ result = nil
140
+ until complete?
141
+ result = send_chunk(api_client)
142
+ break unless result.status == 308
143
+ end
144
+ return result
145
+ end
146
+
147
+
148
+ ##
149
+ # Sends the next chunk to the server
150
+ #
151
+ # @deprecated Pass the instance to {Google::APIClient#execute} instead
152
+ #
153
+ # @param [Google::APIClient] api_client
154
+ # API Client instance to use for sending
155
+ def send_chunk(api_client)
156
+ return api_client.execute(self)
157
+ end
158
+
159
+ ##
160
+ # Check if upload is complete
161
+ #
162
+ # @return [TrueClass, FalseClass]
163
+ # Whether or not the upload complete successfully
164
+ def complete?
165
+ return @complete
166
+ end
167
+
168
+ ##
169
+ # Check if the upload URL expired (upload not completed in alotted time.)
170
+ # Expired uploads must be restarted from the beginning
171
+ #
172
+ # @return [TrueClass, FalseClass]
173
+ # Whether or not the upload has expired and can not be resumed
174
+ def expired?
175
+ return @expired
176
+ end
177
+
178
+ ##
179
+ # Check if upload is resumable. That is, neither complete nor expired
180
+ #
181
+ # @return [TrueClass, FalseClass] True if upload can be resumed
182
+ def resumable?
183
+ return !(self.complete? or self.expired?)
184
+ end
185
+
186
+ ##
187
+ # Convert to an HTTP request. Returns components in order of method, URI,
188
+ # request headers, and body
189
+ #
190
+ # @api private
191
+ #
192
+ # @return [Array<(Symbol, Addressable::URI, Hash, [#read,#to_str])>]
193
+ def to_http_request
194
+ if @complete
195
+ raise Google::APIClient::ClientError, "Upload already complete"
196
+ elsif @offset.nil?
197
+ self.headers.update({
198
+ 'Content-Length' => "0",
199
+ 'Content-Range' => "bytes */#{media.length}" })
200
+ else
201
+ start_offset = @offset
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)
206
+ end_offset = start_offset + content_length - 1
207
+ self.headers.update({
208
+ 'Content-Length' => "#{content_length}",
209
+ 'Content-Type' => self.media.content_type,
210
+ 'Content-Range' => "bytes #{start_offset}-#{end_offset}/#{media.length}" })
211
+ self.body = chunk
212
+ end
213
+ super
214
+ end
215
+
216
+ ##
217
+ # Check the result from the server, updating the offset and/or location
218
+ # if available.
219
+ #
220
+ # @api private
221
+ #
222
+ # @param [Faraday::Response] response
223
+ # HTTP response
224
+ #
225
+ # @return [Google::APIClient::Result]
226
+ # Processed API response
227
+ def process_http_response(response)
228
+ case response.status
229
+ when 200...299
230
+ @complete = true
231
+ when 308
232
+ range = response.headers['range']
233
+ if range
234
+ @offset = range.scan(/\d+/).collect{|x| Integer(x)}.last + 1
235
+ end
236
+ if response.headers['location']
237
+ self.uri = response.headers['location']
238
+ end
239
+ when 400...499
240
+ @expired = true
241
+ when 500...599
242
+ # Invalidate the offset to mark it needs to be queried on the
243
+ # next request
244
+ @offset = nil
245
+ end
246
+ return Google::APIClient::Result.new(self, response)
247
+ end
248
+
249
+ ##
250
+ # Hashified verison of the API request
251
+ #
252
+ # @return [Hash]
253
+ def to_hash
254
+ super.merge(:offset => @offset)
255
+ end
256
+
257
+ end
258
+ end
259
+ end