Skip to content
Merged
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
18 changes: 16 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ name: Tests
on: [push, pull_request]
jobs:
php:
name: PHP ${{ matrix.php-versions }}
name: PHP ${{ matrix.php-versions }}, Guzzle ${{ matrix.guzzle-versions }}
# Same-repo changes run on push; fork PRs run on pull_request.
if: >-
(github.event_name == 'push' && !github.event.pull_request.head.repo.fork) ||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork)
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand All @@ -13,6 +17,14 @@ jobs:
- '8.2'
- '8.3'
- '8.4'
guzzle-versions:
- '7'
# Guzzle 8 on lowest and highest PHP only.
include:
- php-versions: '8.0'
guzzle-versions: '8'
- php-versions: '8.4'
guzzle-versions: '8'
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -23,7 +35,9 @@ jobs:
extensions: json
coverage: xdebug
- name: Install Composer dependencies
run: composer update -n
run: |
composer require -n --no-update guzzlehttp/guzzle:^${{ matrix.guzzle-versions }}
composer update -n
- name: Run Tests
run: |
export CLOUDINARY_URL=$(bash tools/get_test_cloud.sh);
Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
"require": {
"php": ">=8.0.0",
"cloudinary/transformation-builder-sdk": "^2",
"guzzlehttp/guzzle": "^7.4.5",
"guzzlehttp/promises": "^1.5.3|^2.0",
"guzzlehttp/psr7": "^2.7",
"guzzlehttp/guzzle": "^7.4.5|^8.0",
"guzzlehttp/promises": "^1.5.3|^2.0|^3.0",
"guzzlehttp/psr7": "^2.7|^3.0",
"ext-json": "*",
"monolog/monolog": "^2|^3",
"psr/log" : "^2|^3"
Expand Down
4 changes: 3 additions & 1 deletion src/Api/ApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,12 @@ protected function buildHttpClientConfig(): array
$authConfig = [
'headers' => ['Authorization' => 'Bearer ' . $this->cloud->oauthToken],
];
} else {
} elseif (! empty($this->cloud->apiKey) && ! empty($this->cloud->apiSecret)) {
$authConfig = [
'auth' => [$this->cloud->apiKey, $this->cloud->apiSecret],
];
} else {
$authConfig = [];
}

if (isset($authConfig['headers'])) {
Expand Down
33 changes: 29 additions & 4 deletions src/Api/BaseApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use InvalidArgumentException;
use JsonSerializable;
use Psr\Http\Message\ResponseInterface;
use Throwable;

/**
* Class BaseApiClient
Expand Down Expand Up @@ -297,9 +298,11 @@ protected static function finalizeEndPoint(array|string $endPoint): array|string
protected function callAsync(string $method, array|string $endPoint, array $options): PromiseInterface
{
$endPoint = self::finalizeEndPoint($endPoint);
$options['headers'] = ArrayUtils::mergeNonEmpty(
ArrayUtils::get($options, 'headers', []),
ArrayUtils::get($options, 'extra_headers', [])
$options['headers'] = self::normalizeHeaders(
ArrayUtils::mergeNonEmpty(
ArrayUtils::get($options, 'headers', []),
ArrayUtils::get($options, 'extra_headers', [])
)
);
$this->getLogger()->debug("Making async $method request", ['method' => $method, 'endPoint' => $endPoint]);

Expand All @@ -326,7 +329,7 @@ function (ResponseInterface $response) {
return Create::rejectionFor($e);
}
},
function (Exception $error) {
function (Throwable $error) {
$this->getLogger()->critical(
'Async request failed',
[
Expand All @@ -343,6 +346,28 @@ function (Exception $error) {
);
}

/**
* Keeps named headers only and casts their values to strings.
*
* @param array $headers The headers to normalize.
*
* @internal
*/
private static function normalizeHeaders(array $headers): array
{
$normalized = [];

foreach ($headers as $name => $value) {
if (! is_string($name)) {
continue;
}

$normalized[$name] = is_array($value) ? array_map('strval', $value) : (string)$value;
}

return $normalized;
}

/**
* Provides the {@see ApiClient::$userAgent} string that is passed to the Cloudinary servers.
*
Expand Down
3 changes: 2 additions & 1 deletion src/Configuration/ApiConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class ApiConfig extends BaseConfigSection

/**
* Describing the number of seconds to wait while trying to connect to a server.
* Use 0 to wait indefinitely (the default behavior).
* When not set, the HTTP client default applies (Guzzle 8: 60 seconds, Guzzle 7 with cURL: 300 seconds).
* Use 0 to disable the limit on Guzzle 8; on Guzzle 7 with cURL, 0 selects the 300 seconds default.
*
* @var int|float|null
*/
Expand Down
13 changes: 10 additions & 3 deletions tests/Unit/Admin/AssetsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,16 @@ public function testVisualSearchImageFile()
$lastRequest = $mockAdminApi->getMockHandler()->getLastRequest();

self::assertRequestUrl($lastRequest, '/resources/visual_search');
$body = $lastRequest->getBody()->getContents();

self::assertStringContainsString('image_file', $body);
self::assertStringContainsString('Content-Length: 5110', $body);
$file = array_values(
array_filter(
$mockAdminApi->getApiClient()->getRequestOptions()['multipart'],
static fn($part) => $part['name'] === 'image_file'
)
)[0];
$file['contents']->rewind();

self::assertSame(file_get_contents(IntegrationTestCase::TEST_IMAGE_PATH), $file['contents']->getContents());
self::assertSame('sample.png', $file['filename']);
}
}
1 change: 1 addition & 0 deletions tests/Unit/Upload/OAuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,6 @@ public function testMissingCredentialsUnsignedUploadApi()
self::API_TEST_PRESET,
$uploadApi->getApiClient()->getRequestMultipartOptions()['upload_preset']
);
self::assertArrayNotHasKey('Authorization', $uploadApi->getApiClient()->getLastRequestHeaders());
}
}
23 changes: 23 additions & 0 deletions tests/Unit/Upload/UploadApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,29 @@ public function headersDataProvider()
],
[],
],
[
[
'headers' => ['X-Request-ID' => 12345],
'extra_headers' => ['X-Debug' => [true, 1]],
],
['X-Request-ID' => ['12345'], 'X-Debug' => ['1', '1']],
],
];
}

/**
* Should send list-form `headers` as an upload parameter only, not as HTTP request headers.
*/
public function testListFormHeadersAreNotSentAsHttpHeaders()
{
$mockUploadApi = new MockUploadApi();
$mockUploadApi->upload(self::TEST_BASE64_IMAGE, ['headers' => ['X-Robots-Tag: noindex']]);

self::assertArrayNotHasKey('0', $mockUploadApi->getApiClient()->getLastRequestHeaders());
self::assertArrayNotHasKey('X-Robots-Tag', $mockUploadApi->getApiClient()->getLastRequestHeaders());
self::assertSame(
'X-Robots-Tag: noindex',
$mockUploadApi->getApiClient()->getRequestMultipartOptions()['headers']
);
}
}
Loading