google-apis-core 0.7.2 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38cee6b3beff5b42204b1b518e742b3fe8925bc58d7a64565dd6744300193bb1
4
- data.tar.gz: f2b1ebe5ff08156d56867e1eb51520a6328c6654847ded469df9b90efc438208
3
+ metadata.gz: 0e3d4a94e6fc85bf5b6a94cc7d579c3e32be968dfa8df1c56e71232ee3030bbe
4
+ data.tar.gz: d8a593392db24783f8e31785996f90108f67d13d950d7c7f82e669caae65c0a8
5
5
  SHA512:
6
- metadata.gz: a888eda52341a233166b95641dd0bea997fff51f7242e5be0a1df449ffd19c9afdca57fb2d6fbad33f44aa7710a53d6939dd88538b60415d94b1f61087d8a78d
7
- data.tar.gz: bed80fd1c5b86c02ffcec60c5ca1fbe8c1cd434c62e96935b32f447dee61cbb5f3359d6580f82f0269b79028a1817c39c3470ebc8658cb98c7e56c45eab272cd
6
+ metadata.gz: 286116c08612e28eedc00886eee6bdf96b85fd6b7e8b2fa7287b66504c84458bc98936cb9893451db7843cbce6c4bdc797e726678c16241832f1ce467ab01eb0
7
+ data.tar.gz: 81c3a46b26d25a587c558d089f8f2f2d36ddceffd381df969d4296d111b75f54dff592c59680171ebbf8c6641723f5bfeb2613bd939a54c28d1b556acd42e550
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Release History
2
2
 
3
+ ### 0.8.0 (2022-09-16)
4
+
5
+ #### Features
6
+
7
+ * Add storage upload to move away from unified upload protocol ([#11508](https://github.com/googleapis/google-api-ruby-client/issues/11508))
8
+
3
9
  ### 0.7.2 (2022-09-15)
4
10
 
5
11
  #### Bug Fixes
@@ -19,6 +19,7 @@ require 'google/apis/core/version'
19
19
  require 'google/apis/core/api_command'
20
20
  require 'google/apis/core/batch'
21
21
  require 'google/apis/core/upload'
22
+ require 'google/apis/core/storage_upload'
22
23
  require 'google/apis/core/download'
23
24
  require 'google/apis/core/storage_download'
24
25
  require 'google/apis/options'
@@ -318,6 +319,26 @@ module Google
318
319
  command
319
320
  end
320
321
 
322
+ # Create a new storage upload command.
323
+ # This is specifically for storage because we are moving to a new upload protocol.
324
+ # Ref: https://cloud.google.com/storage/docs/performing-resumable-uploads
325
+ #
326
+ # @param [Symbol] method
327
+ # HTTP method for uploading. The initial request to initiate a resumable session
328
+ # is :post and the subsequent chunks uploaded to the session are :put
329
+ # @param [String] path
330
+ # Additional path to upload endpoint, appended to API base path
331
+ # @param [Hash, Google::Apis::RequestOptions] options
332
+ # Request-specific options
333
+ # @return [Google::Apis::Core::StorageUploadCommand]
334
+ def make_storage_upload_command(method, path, options)
335
+ template = Addressable::Template.new(root_url + upload_path + path)
336
+ command = StorageUploadCommand.new(method, template, client_version: client_version)
337
+ command.options = request_options.merge(options)
338
+ apply_command_defaults(command)
339
+ command
340
+ end
341
+
321
342
  # Create a new download command.
322
343
  #
323
344
  # @param [symbol] method
@@ -28,6 +28,7 @@
28
28
  require 'google/apis/core/multipart'
29
29
  require 'google/apis/core/http_command'
30
30
  require 'google/apis/core/upload'
31
+ require 'google/apis/core/storage_upload'
31
32
  require 'google/apis/core/download'
32
33
  require 'google/apis/core/composite_io'
33
34
  require 'addressable/uri'
@@ -120,7 +121,7 @@ module Google
120
121
  end
121
122
 
122
123
  def ensure_valid_command(command)
123
- if command.is_a?(Google::Apis::Core::BaseUploadCommand) || command.is_a?(Google::Apis::Core::DownloadCommand) || command.is_a?(Google::Apis::Core::StorageDownloadCommand)
124
+ if command.is_a?(Google::Apis::Core::BaseUploadCommand) || command.is_a?(Google::Apis::Core::DownloadCommand) || command.is_a?(Google::Apis::Core::StorageDownloadCommand) || command.is_a?(Google::Apis::Core::StorageUploadCommand)
124
125
  fail Google::Apis::ClientError, 'Can not include media requests in batch'
125
126
  end
126
127
  fail Google::Apis::ClientError, 'Invalid command object' unless command.is_a?(HttpCommand)
@@ -98,9 +98,17 @@ module Google
98
98
  # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
99
99
  # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
100
100
  # @raise [Google::Apis::AuthorizationError] Authorization is required
101
- def execute(client)
101
+ def execute(client, &block)
102
102
  prepare!
103
103
  opencensus_begin_span
104
+ do_retry :execute_once, client, &block
105
+ ensure
106
+ opencensus_end_span
107
+ @http_res = nil
108
+ release!
109
+ end
110
+
111
+ def do_retry func, client
104
112
  begin
105
113
  Retriable.retriable tries: options.retries + 1,
106
114
  max_elapsed_time: options.max_elapsed_time,
@@ -115,7 +123,7 @@ module Google
115
123
  Retriable.retriable tries: auth_tries,
116
124
  on: [Google::Apis::AuthorizationError, Signet::AuthorizationError, Signet::RemoteServerError, Signet::UnexpectedStatusError],
117
125
  on_retry: proc { |*| refresh_authorization } do
118
- execute_once(client).tap do |result|
126
+ send(func, client).tap do |result|
119
127
  if block_given?
120
128
  yield result, nil
121
129
  end
@@ -129,10 +137,6 @@ module Google
129
137
  raise e
130
138
  end
131
139
  end
132
- ensure
133
- opencensus_end_span
134
- @http_res = nil
135
- release!
136
140
  end
137
141
 
138
142
  # Refresh the authorization authorization after a 401 error
@@ -216,7 +220,7 @@ module Google
216
220
  def check_status(status, header = nil, body = nil, message = nil)
217
221
  # TODO: 304 Not Modified depends on context...
218
222
  case status
219
- when 200...300
223
+ when 200...300, 308
220
224
  nil
221
225
  when 301, 302, 303, 307
222
226
  message ||= sprintf('Redirect to %s', header['Location'])
@@ -0,0 +1,181 @@
1
+ # Copyright 2022 Google LLC
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 'google/apis/core/http_command'
16
+ require 'google/apis/core/api_command'
17
+ require 'google/apis/errors'
18
+ require 'tempfile'
19
+ require 'mini_mime'
20
+
21
+ module Google
22
+ module Apis
23
+ module Core
24
+ # Base upload command. Not intended to be used directly
25
+ # @private
26
+ class StorageUploadCommand < ApiCommand
27
+ CONTENT_LENGTH_HEADER = "Content-Length"
28
+ CONTENT_TYPE_HEADER = "Content-Type"
29
+ UPLOAD_CONTENT_TYPE_HEADER = "X-Upload-Content-Type"
30
+ LOCATION_HEADER = "Location"
31
+ CONTENT_RANGE_HEADER = "Content-Range"
32
+ RESUMABLE = "resumable"
33
+ OK_STATUS = 200
34
+ CHUNK_SIZE = 8 * 1024 * 1024 # 8 MB
35
+
36
+ # File name or IO containing the content to upload
37
+ # @return [String, File, #read]
38
+ attr_accessor :upload_source
39
+
40
+ # Content type of the upload material
41
+ # @return [String]
42
+ attr_accessor :upload_content_type
43
+
44
+ # Content, as UploadIO
45
+ # @return [Google::Apis::Core::UploadIO]
46
+ attr_accessor :upload_io
47
+
48
+ # Ensure the content is readable and wrapped in an IO instance.
49
+ #
50
+ # @return [void]
51
+ # @raise [Google::Apis::ClientError] if upload source is invalid
52
+ def prepare!
53
+ @upload_url = nil
54
+ @offset = 0
55
+ @upload_incomplete = true
56
+ # Prevent the command from populating the body with form encoding, by
57
+ # asserting that it already has a body. Form encoding is never used
58
+ # by upload requests.
59
+ self.body = '' unless self.body
60
+
61
+ super
62
+ if streamable?(upload_source)
63
+ self.upload_io = upload_source
64
+ @close_io_on_finish = false
65
+ elsif self.upload_source.is_a?(String)
66
+ self.upload_io = File.new(upload_source, 'r')
67
+ if self.upload_content_type.nil?
68
+ type = MiniMime.lookup_by_filename(upload_source)
69
+ self.upload_content_type = type&.content_type
70
+ end
71
+ @close_io_on_finish = true
72
+ else
73
+ fail Google::Apis::ClientError, 'Invalid upload source'
74
+ end
75
+ end
76
+
77
+ # Close IO stream when command done. Only closes the stream if it was opened by the command.
78
+ def release!
79
+ upload_io.close if @close_io_on_finish
80
+ end
81
+
82
+ # Execute the command, retrying as necessary
83
+ #
84
+ # @param [HTTPClient] client
85
+ # HTTP client
86
+ # @yield [result, err] Result or error if block supplied
87
+ # @return [Object]
88
+ # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
89
+ # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
90
+ # @raise [Google::Apis::AuthorizationError] Authorization is required
91
+ def execute(client)
92
+ prepare!
93
+ opencensus_begin_span
94
+
95
+ do_retry :initiate_resumable_upload, client
96
+ while @upload_incomplete
97
+ res = do_retry :send_upload_command, client
98
+ end
99
+ res
100
+ ensure
101
+ opencensus_end_span
102
+ @http_res = nil
103
+ release!
104
+ end
105
+
106
+ def initiate_resumable_upload(client)
107
+ logger.debug { sprintf('Intiating resumable upload command to %s', url) }
108
+
109
+ request_header = header.dup
110
+ apply_request_options(request_header)
111
+
112
+ request_query = query.dup
113
+ request_query['uploadType'] = RESUMABLE
114
+
115
+ request_header[CONTENT_LENGTH_HEADER] = upload_io.size.to_s
116
+ request_header[CONTENT_TYPE_HEADER] = JSON_CONTENT_TYPE
117
+ request_header[UPLOAD_CONTENT_TYPE_HEADER] = upload_content_type unless upload_content_type.nil?
118
+
119
+ response = client.post(url.to_s, query: request_query,
120
+ body: body,
121
+ header: request_header,
122
+ follow_redirect: true)
123
+ result = process_response(response.status_code, response.header, response.body)
124
+ success(result)
125
+ rescue => e
126
+ error(e, rethrow: true)
127
+ end
128
+
129
+ # Send the actual content
130
+ #
131
+ # @param [HTTPClient] client
132
+ # HTTP client
133
+ # @return [HTTP::Message]
134
+ # @raise [Google::Apis::ServerError] Unable to send the request
135
+ def send_upload_command(client)
136
+ logger.debug { sprintf('Sending upload command to %s', @upload_url) }
137
+
138
+ remaining_content_size = upload_io.size - @offset
139
+ current_chunk_size = remaining_content_size < CHUNK_SIZE ? remaining_content_size : CHUNK_SIZE
140
+
141
+ request_header = header.dup
142
+ request_header[CONTENT_RANGE_HEADER] = sprintf('bytes %d-%d/%d', @offset, @offset+current_chunk_size-1, upload_io.size)
143
+ request_header[CONTENT_LENGTH_HEADER] = current_chunk_size
144
+ chunk_body = upload_io.read(current_chunk_size)
145
+
146
+ response = client.put(@upload_url, body: chunk_body, header: request_header, follow_redirect: true)
147
+
148
+ result = process_response(response.status_code, response.header, response.body)
149
+ @upload_incomplete = false if response.status_code.eql? OK_STATUS
150
+ @offset += current_chunk_size if @upload_incomplete
151
+ success(result)
152
+ rescue => e
153
+ upload_io.pos = @offset
154
+ error(e, rethrow: true)
155
+ end
156
+
157
+ # Check the to see if the upload is complete or needs to be resumed.
158
+ #
159
+ # @param [Integer] status
160
+ # HTTP status code of response
161
+ # @param [HTTP::Message::Headers] header
162
+ # Response headers
163
+ # @param [String, #read] body
164
+ # Response body
165
+ # @return [Object]
166
+ # Response object
167
+ # @raise [Google::Apis::ServerError] An error occurred on the server and the request can be retried
168
+ # @raise [Google::Apis::ClientError] The request is invalid and should not be retried without modification
169
+ # @raise [Google::Apis::AuthorizationError] Authorization is required
170
+ def process_response(status, header, body)
171
+ @upload_url = header[LOCATION_HEADER].first unless header[LOCATION_HEADER].empty?
172
+ super(status, header, body)
173
+ end
174
+
175
+ def streamable?(upload_source)
176
+ upload_source.is_a?(IO) || upload_source.is_a?(StringIO) || upload_source.is_a?(Tempfile)
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -16,7 +16,7 @@ module Google
16
16
  module Apis
17
17
  module Core
18
18
  # Core version
19
- VERSION = "0.7.2".freeze
19
+ VERSION = "0.8.0".freeze
20
20
  end
21
21
  end
22
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google-apis-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Google LLC
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-15 00:00:00.000000000 Z
11
+ date: 2022-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: representable
@@ -175,6 +175,7 @@ files:
175
175
  - lib/google/apis/core/logging.rb
176
176
  - lib/google/apis/core/multipart.rb
177
177
  - lib/google/apis/core/storage_download.rb
178
+ - lib/google/apis/core/storage_upload.rb
178
179
  - lib/google/apis/core/upload.rb
179
180
  - lib/google/apis/core/version.rb
180
181
  - lib/google/apis/errors.rb
@@ -185,7 +186,7 @@ licenses:
185
186
  metadata:
186
187
  bug_tracker_uri: https://github.com/googleapis/google-api-ruby-client/issues
187
188
  changelog_uri: https://github.com/googleapis/google-api-ruby-client/tree/main/google-apis-core/CHANGELOG.md
188
- documentation_uri: https://googleapis.dev/ruby/google-apis-core/v0.7.2
189
+ documentation_uri: https://googleapis.dev/ruby/google-apis-core/v0.8.0
189
190
  source_code_uri: https://github.com/googleapis/google-api-ruby-client/tree/main/google-apis-core
190
191
  post_install_message:
191
192
  rdoc_options: []