Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/Http/Batch.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,13 @@ public function parseResponse(ResponseInterface $response, $classes = [])
$responses = [];
$requests = array_values($this->requests);

// Key the requests the same way the API keys the response parts, so
// a part can be matched to the request which produced it.
$requestsByContentId = [];
foreach ($this->requests as $requestKey => $batchedRequest) {
$requestsByContentId['response-' . $requestKey] = $batchedRequest;
}

foreach ($parts as $i => $part) {
$part = trim($part);
if (!empty($part)) {
Expand All @@ -184,8 +191,18 @@ public function parseResponse(ResponseInterface $response, $classes = [])
// Need content id.
$key = $headers['content-id'];

// The parts are not guaranteed to be returned in the order
// they were sent, so resolve the request and the expected
// class from the content id, and only fall back to the
// position of the part when the content id is unknown.
$request = $requestsByContentId[$key] ?? ($requests[$i-1] ?? null);

try {
$response = REST::decodeHttpResponse($response, $requests[$i-1]);
$response = REST::decodeHttpResponse(
$response,
$request,
$classes[$key] ?? null
);
} catch (GoogleServiceException $e) {
// Store the exception as the response, so successful responses
// can be processed.
Expand Down
53 changes: 53 additions & 0 deletions tests/Google/Http/BatchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
use Google\Service\Storage;
use Google\Service\Exception as ServiceException;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;

class BatchTest extends BaseTest
{
Expand Down Expand Up @@ -65,6 +67,57 @@ public function testInvalidBatchRequest()
);
}

public function testParseResponseMatchesResponsesToRequestsByContentId()
{
// The API is not required to return the parts in the order they were
// sent, so each part must be matched to its request via its Content-ID.
$client = $this->getClient();
$batch = new Batch($client);
$batch->add(
(new Request('GET', 'http://foo.bar/volume'))
->withHeader('X-Php-Expected-Class', Books\Volume::class),
'key1'
);
$batch->add(
(new Request('GET', 'http://foo.bar/bookshelf'))
->withHeader('X-Php-Expected-Class', Books\Bookshelf::class),
'key2'
);

$boundary = 'batch_boundary';
$classes = [
'response-key1' => Books\Volume::class,
'response-key2' => Books\Bookshelf::class,
];

// The parts come back in the reverse order of the requests.
$body = '';
foreach (['key2', 'key1'] as $key) {
$body .= "--$boundary\r\n"
. "Content-Type: application/http\r\n"
. "Content-ID: response-$key\r\n"
. "\r\n"
. "HTTP/1.1 200 OK\r\n"
. "Content-Type: application/json\r\n"
. "\r\n"
. '{"id": "' . $key . '"}' . "\r\n";
}
$body .= "--$boundary--";

$response = new Response(
200,
['Content-Type' => "multipart/mixed; boundary=$boundary"],
$body
);

$result = $batch->parseResponse($response, $classes);

$this->assertInstanceOf(Books\Volume::class, $result['response-key1']);
$this->assertEquals('key1', $result['response-key1']->id);
$this->assertInstanceOf(Books\Bookshelf::class, $result['response-key2']);
$this->assertEquals('key2', $result['response-key2']->id);
}

public function testMediaFileBatch()
{
$client = $this->getClient();
Expand Down
Loading