google-apis-core 0.7.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/lib/google/apis/core/api_command.rb +6 -0
- data/lib/google/apis/core/base_service.rb +21 -0
- data/lib/google/apis/core/batch.rb +2 -1
- data/lib/google/apis/core/http_command.rb +11 -8
- data/lib/google/apis/core/storage_upload.rb +181 -0
- data/lib/google/apis/core/version.rb +1 -1
- data/lib/google/apis/options.rb +5 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39e8c0ee69956045fad35dd99b60187bee9a559e66c31141784f69c938b828da
|
4
|
+
data.tar.gz: 2bc2f65430050943360436b018b2149f876bdb190d8d637e42d1ac5c908aee41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26a81d876d54cd42e9e84a4c057297ccd77a1cfebe481de646504f8e30d08496b72141581532dfeae03d3dcb9ff955330b7e94c81bc9bb555130e501fc2879f2
|
7
|
+
data.tar.gz: 2c1242fd64e8e66e82b04aa2a4f4c0fb0979e7a0a53f8162836a90a58242f1d46d4b8660101a15c9e82b6b6e48819cdffe5053f90894a9cf1dd5040de2c21db0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 0.9.0 (2022-09-18)
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* add support to have invocation-id header ([#11655](https://github.com/googleapis/google-api-ruby-client/issues/11655))
|
8
|
+
|
9
|
+
### 0.8.0 (2022-09-16)
|
10
|
+
|
11
|
+
#### Features
|
12
|
+
|
13
|
+
* Add storage upload to move away from unified upload protocol ([#11508](https://github.com/googleapis/google-api-ruby-client/issues/11508))
|
14
|
+
|
15
|
+
### 0.7.2 (2022-09-15)
|
16
|
+
|
17
|
+
#### Bug Fixes
|
18
|
+
|
19
|
+
* do not reset query_values in case of form encoding ([#11654](https://github.com/googleapis/google-api-ruby-client/issues/11654))
|
20
|
+
|
3
21
|
### 0.7.1 (2022-09-14)
|
4
22
|
|
5
23
|
#### Bug Fixes
|
@@ -18,6 +18,7 @@ require 'google/apis/core/http_command'
|
|
18
18
|
require 'google/apis/errors'
|
19
19
|
require 'json'
|
20
20
|
require 'retriable'
|
21
|
+
require "securerandom"
|
21
22
|
|
22
23
|
module Google
|
23
24
|
module Apis
|
@@ -161,6 +162,7 @@ module Google
|
|
161
162
|
xgac = "gl-ruby/#{RUBY_VERSION} gdcl/#{munged_client_version}"
|
162
163
|
xgac = old_xgac.empty? ? xgac : "#{old_xgac} #{xgac}"
|
163
164
|
header.delete_if { |k, v| k.downcase == 'x-goog-api-client' }
|
165
|
+
xgac.concat(" ",invocation_id_header) if options.add_invocation_id_header
|
164
166
|
header['X-Goog-Api-Client'] = xgac
|
165
167
|
end
|
166
168
|
|
@@ -172,6 +174,10 @@ module Google
|
|
172
174
|
header['X-Goog-User-Project'] = quota_project_id if quota_project_id
|
173
175
|
end
|
174
176
|
|
177
|
+
def invocation_id_header
|
178
|
+
"gccl-invocation-id/#{SecureRandom.uuid}"
|
179
|
+
end
|
180
|
+
|
175
181
|
# Attempt to parse a JSON error message
|
176
182
|
# @param [String] body
|
177
183
|
# HTTP response body
|
@@ -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
|
-
|
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
|
@@ -168,7 +172,6 @@ module Google
|
|
168
172
|
@form_encoded = true
|
169
173
|
self.body = Addressable::URI.form_encode(url.query_values(Array))
|
170
174
|
self.header['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
|
171
|
-
self.url.query_values = {}
|
172
175
|
else
|
173
176
|
@form_encoded = false
|
174
177
|
end
|
@@ -217,7 +220,7 @@ module Google
|
|
217
220
|
def check_status(status, header = nil, body = nil, message = nil)
|
218
221
|
# TODO: 304 Not Modified depends on context...
|
219
222
|
case status
|
220
|
-
when 200...300
|
223
|
+
when 200...300, 308
|
221
224
|
nil
|
222
225
|
when 301, 302, 303, 307
|
223
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
|
data/lib/google/apis/options.rb
CHANGED
@@ -39,7 +39,8 @@ module Google
|
|
39
39
|
:api_format_version,
|
40
40
|
:use_opencensus,
|
41
41
|
:quota_project,
|
42
|
-
:query
|
42
|
+
:query,
|
43
|
+
:add_invocation_id_header)
|
43
44
|
|
44
45
|
# General client options
|
45
46
|
class ClientOptions
|
@@ -95,6 +96,8 @@ module Google
|
|
95
96
|
# @return [String] Project ID to charge quota, or `nil` to default to the credentials-specified project.
|
96
97
|
# @!attribute [rw] query
|
97
98
|
# @return [Hash<String,String>] Additional HTTP URL query parameters to include in requests.
|
99
|
+
# @!attribute [rw] add_invocation_id_header
|
100
|
+
# @return [Boolean] True if the header gccl-invocation-id need to be set
|
98
101
|
|
99
102
|
# Get the default options
|
100
103
|
# @return [Google::Apis::RequestOptions]
|
@@ -129,5 +132,6 @@ module Google
|
|
129
132
|
RequestOptions.default.api_format_version = nil
|
130
133
|
RequestOptions.default.use_opencensus = true
|
131
134
|
RequestOptions.default.quota_project = nil
|
135
|
+
RequestOptions.default.add_invocation_id_header = false
|
132
136
|
end
|
133
137
|
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.
|
4
|
+
version: 0.9.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-
|
11
|
+
date: 2022-09-19 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.
|
189
|
+
documentation_uri: https://googleapis.dev/ruby/google-apis-core/v0.9.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: []
|