#286 - Send content-id in batch requests
This commit is contained in:
parent
ed40d7b750
commit
8b296b148e
|
@ -31,7 +31,7 @@ require 'google/apis/core/http_command'
|
|||
require 'google/apis/core/upload'
|
||||
require 'google/apis/core/download'
|
||||
require 'addressable/uri'
|
||||
|
||||
require 'securerandom'
|
||||
module Google
|
||||
module Apis
|
||||
module Core
|
||||
|
@ -47,6 +47,7 @@ module Google
|
|||
def initialize(method, url)
|
||||
super(method, url)
|
||||
@calls = []
|
||||
@base_id = SecureRandom.uuid
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -78,9 +79,12 @@ module Google
|
|||
parts = split_parts(body, m[1])
|
||||
deserializer = CallDeserializer.new
|
||||
parts.each_index do |index|
|
||||
call, callback = @calls[index]
|
||||
response = deserializer.to_http_response(parts[index])
|
||||
outer_header = response.shift
|
||||
call_id = header_to_id(outer_header[:content_id]) || index
|
||||
call, callback = @calls[call_id]
|
||||
begin
|
||||
result = call.process_response(*deserializer.to_http_response(parts[index])) unless call.nil?
|
||||
result = call.process_response(*response) unless call.nil?
|
||||
success(result, &callback)
|
||||
rescue => e
|
||||
error(e, &callback)
|
||||
|
@ -103,9 +107,11 @@ module Google
|
|||
|
||||
serializer = CallSerializer.new
|
||||
multipart = Multipart.new(boundary: BATCH_BOUNDARY, content_type: MULTIPART_MIXED)
|
||||
@calls.each do |(call, _)|
|
||||
@calls.each_index do |index|
|
||||
call, _ = @calls[index]
|
||||
content_id = id_to_header(index)
|
||||
io = serializer.to_upload_io(call)
|
||||
multipart.add_upload(io)
|
||||
multipart.add_upload(io, content_id: content_id)
|
||||
end
|
||||
self.body = multipart.assemble
|
||||
|
||||
|
@ -120,6 +126,17 @@ module Google
|
|||
end
|
||||
fail Google::Apis::ClientError, 'Invalid command object' unless command.is_a?(HttpCommand)
|
||||
end
|
||||
|
||||
def id_to_header(call_id)
|
||||
return sprintf('<%s+%i>', @base_id, call_id)
|
||||
end
|
||||
|
||||
def header_to_id(content_id)
|
||||
match = /<response-.*\+(\d+)>/.match(content_id)
|
||||
return match[1].to_i if match
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Wrapper request for batching multiple uploads in a single server request
|
||||
|
@ -180,19 +197,19 @@ module Google
|
|||
# Deconstructs a raw HTTP response part
|
||||
# @private
|
||||
class CallDeserializer
|
||||
# Convert a single batched response into a BatchedCallResponse object.
|
||||
# Parse a batched response.
|
||||
#
|
||||
# @param [String] call_response
|
||||
# the response to parse.
|
||||
# @return [Array<(Fixnum, Hurley::Header, String)>]
|
||||
# Status, header, and response body.
|
||||
def to_http_response(call_response)
|
||||
_, outer_body = split_header_and_body(call_response)
|
||||
outer_header, outer_body = split_header_and_body(call_response)
|
||||
status_line, payload = outer_body.split(/\n/, 2)
|
||||
_, status = status_line.split(' ', 3)
|
||||
|
||||
header, body = split_header_and_body(payload)
|
||||
[status.to_i, header, body]
|
||||
[outer_header, status.to_i, header, body]
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -31,7 +31,7 @@ module Google
|
|||
# Multipart boundary
|
||||
# @param [String] value
|
||||
# JSON content
|
||||
def initialize(boundary, value)
|
||||
def initialize(boundary, value, header = {})
|
||||
@part = build_part(boundary, value)
|
||||
@length = @part.bytesize
|
||||
@io = StringIO.new(@part)
|
||||
|
@ -95,19 +95,25 @@ module Google
|
|||
# @param [Hash] header
|
||||
# Headers for the part
|
||||
def build_head(boundary, type, content_len, header)
|
||||
content_id = ''
|
||||
if header[:content_id]
|
||||
content_id = sprintf(CID_FORMAT, header[:content_id])
|
||||
end
|
||||
sprintf(HEAD_FORMAT,
|
||||
boundary,
|
||||
content_len.to_i,
|
||||
content_id,
|
||||
header[:content_type] || type,
|
||||
header[:content_transfer_encoding] || DEFAULT_TR_ENCODING)
|
||||
end
|
||||
|
||||
DEFAULT_TR_ENCODING = 'binary'.freeze
|
||||
FOOT = "\r\n".freeze
|
||||
CID_FORMAT = "Content-ID: %s\r\n"
|
||||
HEAD_FORMAT = <<-END
|
||||
--%s\r
|
||||
Content-Length: %d\r
|
||||
Content-Type: %s\r
|
||||
%sContent-Type: %s\r
|
||||
Content-Transfer-Encoding: %s\r
|
||||
\r
|
||||
END
|
||||
|
@ -137,9 +143,12 @@ Content-Transfer-Encoding: %s\r
|
|||
#
|
||||
# @param [String] body
|
||||
# JSON text
|
||||
# @param [String] content_id
|
||||
# Optional unique ID of this part
|
||||
# @return [self]
|
||||
def add_json(body)
|
||||
@parts << Google::Apis::Core::JsonPart.new(@boundary, body)
|
||||
def add_json(body, content_id: nil)
|
||||
header = { :content_id => content_id }
|
||||
@parts << Google::Apis::Core::JsonPart.new(@boundary, body, header)
|
||||
self
|
||||
end
|
||||
|
||||
|
@ -147,9 +156,14 @@ Content-Transfer-Encoding: %s\r
|
|||
#
|
||||
# @param [Google::Apis::Core::UploadIO] upload_io
|
||||
# IO stream
|
||||
# @param [String] content_id
|
||||
# Optional unique ID of this part
|
||||
# @return [self]
|
||||
def add_upload(upload_io)
|
||||
@parts << Google::Apis::Core::FilePart.new(@boundary, upload_io)
|
||||
def add_upload(upload_io, content_id: nil)
|
||||
header = { :content_id => content_id }
|
||||
@parts << Google::Apis::Core::FilePart.new(@boundary,
|
||||
upload_io,
|
||||
header)
|
||||
self
|
||||
end
|
||||
|
||||
|
|
|
@ -42,9 +42,12 @@ RSpec.describe Google::Apis::Core::BatchCommand do
|
|||
end
|
||||
|
||||
before(:example) do
|
||||
allow(SecureRandom).to receive(:uuid).and_return('ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f')
|
||||
|
||||
response = <<EOF
|
||||
--batch123
|
||||
Content-Type: application/http
|
||||
Content-ID: <response-ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+0>
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
|
@ -52,18 +55,20 @@ Content-Type: text/plain; charset=UTF-8
|
|||
Hello
|
||||
--batch123
|
||||
Content-Type: application/http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
|
||||
world
|
||||
--batch123
|
||||
Content-Type: application/http
|
||||
Content-ID: <response-ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+2>
|
||||
|
||||
HTTP/1.1 500 Server Error
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
|
||||
Error!
|
||||
--batch123
|
||||
Content-Type: application/http
|
||||
Content-ID: <response-ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+1>
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
|
||||
world
|
||||
--batch123--
|
||||
EOF
|
||||
stub_request(:post, 'https://www.googleapis.com/batch')
|
||||
|
@ -80,6 +85,7 @@ EOF
|
|||
expected_body = <<EOF.gsub(/\n/, "\r\n")
|
||||
--RubyApiBatchRequest
|
||||
Content-Length: 58
|
||||
Content-ID: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+0>
|
||||
Content-Type: application/http
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
|
@ -89,6 +95,7 @@ Host: www.googleapis.com
|
|||
|
||||
--RubyApiBatchRequest
|
||||
Content-Length: 96
|
||||
Content-ID: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+1>
|
||||
Content-Type: application/http
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
|
@ -99,6 +106,7 @@ Host: www.googleapis.com
|
|||
Hello world
|
||||
--RubyApiBatchRequest
|
||||
Content-Length: 93
|
||||
Content-ID: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+2>
|
||||
Content-Type: application/http
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
|
@ -115,11 +123,17 @@ EOF
|
|||
|
||||
it 'should send decode responses' do
|
||||
expect do |b|
|
||||
command.add(get_command, &b)
|
||||
command.add(post_with_string_command, &b)
|
||||
command.add(post_with_io_command, &b)
|
||||
command.add(get_command) do |res, err|
|
||||
b.to_proc.call(1, res, err)
|
||||
end
|
||||
command.add(post_with_string_command) do |res, err|
|
||||
b.to_proc.call(2, res, err)
|
||||
end
|
||||
command.add(post_with_io_command) do |res, err|
|
||||
b.to_proc.call(3, res, err)
|
||||
end
|
||||
command.execute(client)
|
||||
end.to yield_successive_args(['Hello', nil], ['world', nil], [nil, an_instance_of(Google::Apis::ServerError)])
|
||||
end.to yield_successive_args([1, 'Hello', nil], [3, nil, an_instance_of(Google::Apis::ServerError)], [2, 'world', nil],)
|
||||
end
|
||||
|
||||
it 'should raise error if batch is empty' do
|
||||
|
|
Loading…
Reference in New Issue