google-apis-core 0.7.2 → 0.9.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 +4 -4
- data/CHANGELOG.md +12 -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 -7
- 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,17 @@
|
|
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
|
+
|
3
15
|
### 0.7.2 (2022-09-15)
|
4
16
|
|
5
17
|
#### 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
|
@@ -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
|
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: []
|