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
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-zlib": "*",
"psr/http-message": "^1.0",
"psr/log": "^1.0",
"psr/simple-cache": "^1.0"
Expand Down
21 changes: 21 additions & 0 deletions src/Compressors/Compressor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Algolia\AlgoliaSearch\Compressors;

use Algolia\AlgoliaSearch\RequestOptions\RequestOptions;

/**
* @internal
*/
interface Compressor
{
/**
* Mutates the given `$requestOptions` object and returns the encoded body.
*
* @param \Algolia\AlgoliaSearch\RequestOptions\RequestOptions $requestOptions
* @param string $body
*
* @return string
*/
public function compress(RequestOptions $requestOptions, $body);
}
29 changes: 29 additions & 0 deletions src/Compressors/CompressorFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Algolia\AlgoliaSearch\Compressors;

use Algolia\AlgoliaSearch\Config\AbstractConfig;
use Doctrine\Instantiator\Exception\InvalidArgumentException;

/**
* @internal
*/
final class CompressorFactory
{
/**
* @param string $type
*
* @return \Algolia\AlgoliaSearch\Compressors\Compressor
*/
public static function create($type)
{
switch ($type) {
case AbstractConfig::COMPRESSION_TYPE_NONE:
return new NullCompressor();
case AbstractConfig::COMPRESSION_TYPE_GZIP:
return new GzipCompressor();
default:
throw new InvalidArgumentException('Compression type not supported');
}
}
}
21 changes: 21 additions & 0 deletions src/Compressors/GzipCompressor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Algolia\AlgoliaSearch\Compressors;

use Algolia\AlgoliaSearch\RequestOptions\RequestOptions;

/**
* @internal
*/
final class GzipCompressor implements Compressor
{
public function compress(RequestOptions $requestOptions, $body)
{
$compressedBody = gzencode($body, 9);

Check warning on line 14 in src/Compressors/GzipCompressor.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Compressors/GzipCompressor.php#L14

The use of function gzencode() is discouraged

$requestOptions->addHeader('Content-Encoding', 'gzip');
$requestOptions->addHeader('Content-Length', strlen($compressedBody));

return $compressedBody;
}
}
16 changes: 16 additions & 0 deletions src/Compressors/NullCompressor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Algolia\AlgoliaSearch\Compressors;

use Algolia\AlgoliaSearch\RequestOptions\RequestOptions;

/**
* @internal
*/
final class NullCompressor implements Compressor
{
public function compress(RequestOptions $requestOptions, $body)
{
return $body;
}
}
32 changes: 32 additions & 0 deletions src/Config/AbstractConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

abstract class AbstractConfig
{
const COMPRESSION_TYPE_NONE = 'none';
const COMPRESSION_TYPE_GZIP = 'gzip';

protected $config;

protected $defaultReadTimeout = 5;
Expand All @@ -29,6 +32,7 @@ public function getDefaultConfig()
'writeTimeout' => $this->defaultWriteTimeout,
'connectTimeout' => $this->defaultConnectTimeout,
'defaultHeaders' => array(),
'compressionType' => self::COMPRESSION_TYPE_NONE,
);
}

Expand Down Expand Up @@ -115,4 +119,32 @@ public function setDefaultHeaders(array $defaultHeaders)

return $this;
}

/**
* @return string
*/
public function getCompressionType()
{
return $this->config['compressionType'];
}

/**
* @param string $compressionType
*
* @return $this
*/
public function setCompressionType($compressionType)
{
if (!in_array(
$compressionType,
array(self::COMPRESSION_TYPE_GZIP, self::COMPRESSION_TYPE_NONE),
true
)) {
throw new \InvalidArgumentException('Compression type not supported');
}

$this->config['compressionType'] = $compressionType;

return $this;
}
}
1 change: 1 addition & 0 deletions src/Config/SearchConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function getDefaultConfig()
'defaultHeaders' => array(),
'defaultForwardToReplicas' => null,
'batchSize' => 1000,
'compressionType' => self::COMPRESSION_TYPE_GZIP,
);
}

Expand Down
62 changes: 35 additions & 27 deletions src/RetryStrategy/ApiWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Algolia\AlgoliaSearch\RetryStrategy;

use Algolia\AlgoliaSearch\Algolia;
use Algolia\AlgoliaSearch\Compressors\CompressorFactory;
use Algolia\AlgoliaSearch\Config\AbstractConfig;
use Algolia\AlgoliaSearch\Exceptions\AlgoliaException;
use Algolia\AlgoliaSearch\Exceptions\BadRequestException;
Expand Down Expand Up @@ -42,6 +43,11 @@ final class ApiWrapper implements ApiWrapperInterface
*/
private $requestOptionsFactory;

/**
* @var \Algolia\AlgoliaSearch\Compressors\Compressor
*/
private $compressor;

public function __construct(
HttpClientInterface $http,
AbstractConfig $config,
Expand All @@ -52,6 +58,8 @@ public function __construct(
$this->config = $config;
$this->clusterHosts = $clusterHosts;
$this->requestOptionsFactory = $RqstOptsFactory ?: new RequestOptionsFactory($config);

$this->compressor = CompressorFactory::create($this->config->getCompressionType());
}

public function read($method, $path, $requestOptions = array(), $defaultRequestOptions = array())
Expand Down Expand Up @@ -115,7 +123,13 @@ private function request($method, $path, RequestOptions $requestOptions, $hosts,
->withQuery($requestOptions->getBuiltQueryParameters())
->withScheme('https');

$body = array_merge($data, $requestOptions->getBody());
$body = $this->sanitizeBody(array_merge($data, $requestOptions->getBody()));

if ('POST' === strtoupper($method) || 'PUT' === strtoupper($method)) {
$compressedBody = $this->compressor->compress($requestOptions, $body);
} else {
$compressedBody = $body;
}

$logParams = array(
'body' => $body,
Expand All @@ -125,19 +139,15 @@ private function request($method, $path, RequestOptions $requestOptions, $hosts,
);

$retry = 1;

foreach ($hosts as $host) {
$uri = $uri->withHost($host);
$request = null;
$logParams['retryNumber'] = $retry;
$logParams['host'] = (string) $uri;

try {
$request = $this->createRequest(
$method,
$uri,
$requestOptions->getHeaders(),
$body
);
$request = new Request($method, $uri, $requestOptions->getHeaders(), $compressedBody, '1.1');

$this->log(LogLevel::DEBUG, 'Sending request.', $logParams);

Expand Down Expand Up @@ -215,16 +225,24 @@ private function createUri($uri)
throw new \InvalidArgumentException('URI must be a string or UriInterface');
}

private function createRequest(
$method,
$uri,
array $headers = array(),
$body = null,
$protocolVersion = '1.1'
) {
/**
* @param string $level
* @param string $message
* @param array $context
*/
private function log($level, $message, array $context = array())
{
Algolia::getLogger()->log($level, 'Algolia API client: '.$message, $context);
}

/**
* @param string $body
*
* @return string
*/
private function sanitizeBody($body)
{
if (is_array($body)) {
// Send an empty body instead of "[]" in case there are
// no content/params to send
if (empty($body)) {
$body = '';
} else {
Expand All @@ -236,16 +254,6 @@ private function createRequest(
}
}

return new Request($method, $uri, $headers, $body, $protocolVersion);
}

/**
* @param string $level
* @param string $message
* @param array $context
*/
private function log($level, $message, array $context = array())
{
Algolia::getLogger()->log($level, 'Algolia API client: '.$message, $context);
return $body;
}
}
1 change: 0 additions & 1 deletion tests/Integration/IndexingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ public function testIndexing()
$multiResponse->wait();

/* Check 6 first records with getObject */

$objectID1 = $responses[0][0]['objectIDs'][0];
$objectID2 = $responses[1][0]['objectIDs'][0];
$objectID3 = $responses[2][0]['objectIDs'][0];
Expand Down
15 changes: 12 additions & 3 deletions tests/Unit/CopyResourcesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ public function testCopySettings()
static::$client->copySettings('src', 'dest');
} catch (RequestException $e) {
$this->assertEndpointEquals($e->getRequest(), '/1/indexes/src/operation');
$this->assertBodySubset(array(
$this->assertHeaderIsSet('Content-Encoding', $e->getRequest());
$this->assertHeaderIsSet('Content-Length', $e->getRequest());
$this->assertBodyEncoded($e->getRequest());
$this->assertEncodedBodySubset(array(
'operation' => 'copy',
'destination' => 'dest',
'scope' => array('settings'),
Expand All @@ -38,7 +41,10 @@ public function testCopySynonyms()
static::$client->copySynonyms('src', 'dest');
} catch (RequestException $e) {
$this->assertEndpointEquals($e->getRequest(), '/1/indexes/src/operation');
$this->assertBodySubset(array(
$this->assertHeaderIsSet('Content-Encoding', $e->getRequest());
$this->assertHeaderIsSet('Content-Length', $e->getRequest());
$this->assertBodyEncoded($e->getRequest());
$this->assertEncodedBodySubset(array(
'operation' => 'copy',
'destination' => 'dest',
'scope' => array('synonyms'),
Expand All @@ -54,7 +60,10 @@ public function testCopyRules()
static::$client->copyRules('src', 'dest');
} catch (RequestException $e) {
$this->assertEndpointEquals($e->getRequest(), '/1/indexes/src/operation');
$this->assertBodySubset(array(
$this->assertHeaderIsSet('Content-Encoding', $e->getRequest());
$this->assertHeaderIsSet('Content-Length', $e->getRequest());
$this->assertBodyEncoded($e->getRequest());
$this->assertEncodedBodySubset(array(
'operation' => 'copy',
'destination' => 'dest',
'scope' => array('rules'),
Expand Down
25 changes: 25 additions & 0 deletions tests/Unit/RequestTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
$this->assertArraySubset($subset, $body, true);
}

protected function assertEncodedBodySubset(
$subset,
RequestInterface $request
) {
$body = json_decode(gzdecode($request->getBody()), true);

Check warning on line 39 in tests/Unit/RequestTestCase.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/Unit/RequestTestCase.php#L39

The use of function gzdecode() is discouraged
$this->assertArraySubset($subset, $body, true);
}

protected function assertQueryParametersSubset(array $subset, RequestInterface $request)
{
$params = $this->requestQueryParametersToArray($request);
Expand All @@ -44,6 +52,23 @@
$this->assertArrayNotHasKey($key, $params);
}

protected function assertBodyEncoded(RequestInterface $request)
{
return gzdecode($request->getBody());

Check warning on line 57 in tests/Unit/RequestTestCase.php

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/Unit/RequestTestCase.php#L57

The use of function gzdecode() is discouraged
}

protected function assertHeaderIsSet($headerName, RequestInterface $request)
{
$this->assertArrayHasKey($headerName, $request->getHeaders());
}

protected function assertHeaderIsNotSet(
$headerName,
RequestInterface $request
) {
$this->assertArrayNotHasKey($headerName, $request->getHeaders());
}

private function requestQueryParametersToArray(RequestInterface $request)
{
$array = array();
Expand Down
Loading