Skip to content

Commit a92f0a0

Browse files
authored
Merge pull request #8 from php-api-clients/improvement-ensure-headers-are-merged-as-expected
Correctly handle request options
2 parents 3437675 + bcb8d75 commit a92f0a0

File tree

2 files changed

+111
-37
lines changed

2 files changed

+111
-37
lines changed

src/Client.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,19 @@
33
namespace ApiClients\Foundation\Transport;
44

55
use ApiClients\Foundation\Middleware\Locator\Locator;
6-
use ApiClients\Foundation\Middleware\MiddlewareInterface;
76
use ApiClients\Foundation\Middleware\MiddlewareRunner;
87
use ApiClients\Foundation\Transport\CommandBus;
98
use Clue\React\Buzz\Browser;
109
use Interop\Container\ContainerInterface;
11-
use InvalidArgumentException;
1210
use Psr\Http\Message\RequestInterface;
1311
use Psr\Http\Message\ResponseInterface;
1412
use React\EventLoop\LoopInterface;
1513
use React\Promise\PromiseInterface;
1614
use RingCentral\Psr7\Uri;
1715
use Throwable;
16+
use function ApiClients\Foundation\options_merge;
1817
use function React\Promise\reject;
1918
use function React\Promise\resolve;
20-
use function WyriHaximus\React\futureFunctionPromise;
2119

2220
final class Client implements ClientInterface
2321
{
@@ -113,8 +111,8 @@ protected function combinedMiddlewares(array $extraMiddlewares): array
113111
*/
114112
public function request(RequestInterface $request, array $options = []): PromiseInterface
115113
{
116-
$request = $this->applyApiSettingsToRequest($request);
117114
$options = $this->applyRequestOptions($options);
115+
$request = $this->applyApiSettingsToRequest($request, $options);
118116
$executioner = $this->constructMiddlewares($options);
119117

120118
return $executioner->pre($request)->then(function ($request) use ($options) {
@@ -130,22 +128,23 @@ public function request(RequestInterface $request, array $options = []): Promise
130128
});
131129
}
132130

133-
protected function applyApiSettingsToRequest(RequestInterface $request): RequestInterface
131+
protected function applyApiSettingsToRequest(RequestInterface $request, array $options): RequestInterface
134132
{
133+
$options = array_replace_recursive($this->options, $options);
135134
$uri = $request->getUri();
136135
if (strpos((string)$uri, '://') === false) {
137136
$uri = Uri::resolve(
138137
new Uri(
139-
$this->options[Options::SCHEMA] .
138+
$options[Options::SCHEMA] .
140139
'://' .
141-
$this->options[Options::HOST] .
142-
$this->options[Options::PATH]
140+
$options[Options::HOST] .
141+
$options[Options::PATH]
143142
),
144143
$request->getUri()
145144
);
146145
}
147146

148-
foreach ($this->options[Options::HEADERS] as $key => $value) {
147+
foreach ($options[Options::HEADERS] as $key => $value) {
149148
$request = $request->withAddedHeader($key, $value);
150149
}
151150

tests/ClientTest.php

Lines changed: 103 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,18 @@
33
namespace ApiClients\Tests\Foundation\Transport;
44

55
use ApiClients\Foundation\Middleware\Locator\ContainerLocator;
6-
use ApiClients\Foundation\Middleware\Locator\Locator;
76
use ApiClients\Foundation\Transport\Client;
87
use ApiClients\Foundation\Transport\Options;
9-
use ApiClients\Foundation\Transport\UserAgentStrategies;
10-
use ApiClients\Foundation\Transport\UserAgentStrategy\PackageVersionStrategy;
11-
use ApiClients\Foundation\Transport\UserAgentStrategyInterface;
128
use ApiClients\Tools\TestUtilities\TestCase;
139
use Clue\React\Buzz\Browser as BuzzClient;
1410
use DI\ContainerBuilder;
1511
use Exception;
1612
use InvalidArgumentException;
17-
use PackageVersions\Versions;
1813
use Phake;
1914
use Psr\Http\Message\RequestInterface;
2015
use Psr\Http\Message\ResponseInterface;
2116
use Psr\Http\Message\StreamInterface;
2217
use React\EventLoop\Factory;
23-
use React\Promise\FulfilledPromise;
2418
use RingCentral\Psr7\Request;
2519
use function Clue\React\Block\await;
2620
use function React\Promise\reject;
@@ -30,28 +24,117 @@ class ClientTest extends TestCase
3024
{
3125
public function provideRequests()
3226
{
27+
$defaultClientOptions = [
28+
Options::SCHEMA => 'http',
29+
Options::HOST => 'api.example.com',
30+
Options::MIDDLEWARE => [
31+
DummyMiddleware::class,
32+
],
33+
];
34+
$defaultRequestOptions = [];
35+
3336
yield [
3437
new Request('GET', ''),
3538
new Request('GET', 'http://api.example.com/'),
39+
$defaultClientOptions,
40+
$defaultRequestOptions,
3641
];
3742

3843
yield [
3944
new Request('GET', 'status'),
4045
new Request('GET', 'http://api.example.com/status'),
46+
$defaultClientOptions,
47+
$defaultRequestOptions,
48+
];
49+
50+
yield [
51+
new Request('HEAD', 'https://api.example.com/status'),
52+
new Request('HEAD', 'https://api.example.com/status'),
53+
$defaultClientOptions,
54+
$defaultRequestOptions,
55+
];
56+
57+
yield [
58+
new Request('HEAD', 'https://api.example.com/status'),
59+
new Request('HEAD', 'https://api.example.com/status', ['Accept' => 'foo',]),
60+
$defaultClientOptions + [
61+
Options::HEADERS => [
62+
'Accept' => 'foo',
63+
],
64+
],
65+
$defaultRequestOptions,
66+
];
67+
68+
yield [
69+
new Request('HEAD', 'https://api.example.com/status'),
70+
new Request('HEAD', 'https://api.example.com/status', ['Accept' => 'bar',]),
71+
$defaultClientOptions + [
72+
Options::HEADERS => [
73+
'Accept' => 'foo',
74+
],
75+
],
76+
$defaultRequestOptions + [
77+
Options::HEADERS => [
78+
'Accept' => 'bar',
79+
],
80+
],
4181
];
4282

4383
yield [
4484
new Request('HEAD', 'https://api.example.com/status'),
85+
new Request('HEAD', 'https://api.example.com/status', ['Accept' => 'foo',' Decline' => 'bar',]),
86+
$defaultClientOptions + [
87+
Options::HEADERS => [
88+
'Accept' => 'foo',
89+
],
90+
],
91+
$defaultRequestOptions + [
92+
Options::HEADERS => [
93+
'Decline' => 'bar',
94+
],
95+
],
96+
];
97+
98+
yield [
99+
new Request('HEAD', 'https://api.example.com/status'),
100+
new Request('HEAD', 'https://api.example.com/status', ['Accept' => 'bar',' Decline' => 'bar',]),
101+
$defaultClientOptions + [
102+
Options::HEADERS => [
103+
'Accept' => 'foo',
104+
],
105+
],
106+
$defaultRequestOptions + [
107+
Options::HEADERS => [
108+
'Accept' => 'bar',
109+
'Decline' => 'bar',
110+
],
111+
],
112+
];
113+
114+
yield [
45115
new Request('HEAD', 'https://api.example.com/status'),
116+
new Request('HEAD', 'https://api.example.com/status', ['a' => 'b',' c' => 'd', 'e' => 'f', 'g' => 'h',]),
117+
$defaultClientOptions + [
118+
Options::HEADERS => [
119+
'a' => 'b',
120+
'c' => 'd',
121+
],
122+
],
123+
$defaultRequestOptions + [
124+
Options::HEADERS => [
125+
'e' => 'f',
126+
'g' => 'h',
127+
],
128+
],
46129
];
47130
}
48131

49132
/**
50133
* @dataProvider provideRequests
51134
*/
52-
public function testRequest(RequestInterface $inputRequest, RequestInterface $outputRequest)
135+
public function testRequest(RequestInterface $inputRequest, RequestInterface $outputRequest, array $clientOptions, array $requestOptions)
53136
{
54-
$locator = Phake::mock(Locator::class);
137+
$container = ContainerBuilder::buildDevContainer();
55138
$loop = Factory::create();
56139

57140
$stream = Phake::mock(StreamInterface::class);
@@ -65,31 +148,29 @@ public function testRequest(RequestInterface $inputRequest, RequestInterface $ou
65148
Phake::when($response)->getReasonPhrase()->thenReturn('OK');
66149

67150
$request = false;
68-
$handler = Phake::mock(BuzzClient::class);
69-
Phake::when($handler)->send($outputRequest)->thenReturnCallback(function (RequestInterface $guzzleRequest) use ($response, &$request) {
151+
$buzz = Phake::mock(BuzzClient::class);
152+
Phake::when($buzz)->send(Phake::anyParameters())->thenReturnCallback(function (RequestInterface $guzzleRequest) use ($response, &$request) {
70153
$request = $guzzleRequest;
71-
return new FulfilledPromise($response);
154+
return resolve($response);
72155
});
73156

74157
$client = new Client(
75158
$loop,
76-
$locator,
77-
$handler,
78-
[
79-
Options::SCHEMA => 'http',
80-
Options::HOST => 'api.example.com',
81-
]
159+
new ContainerLocator($container),
160+
$buzz,
161+
$clientOptions
82162
);
83163

84-
$client->request($inputRequest, [], true);
164+
$client->request($inputRequest, $requestOptions);
85165

86-
Phake::verify($handler)->send($outputRequest);
166+
Phake::verify($buzz)->send($outputRequest);
87167

88168
self::assertNotFalse($request);
89169
self::assertInstanceOf(RequestInterface::class, $request);
90170

91171
self::assertSame($outputRequest->getMethod(), $request->getMethod());
92172
self::assertSame((string) $outputRequest->getUri(), (string) $request->getUri());
173+
self::assertSame($outputRequest->getHeaders(), $request->getHeaders());
93174

94175
$headers = $outputRequest->getHeaders();
95176
ksort($headers);
@@ -101,7 +182,7 @@ public function testRequest(RequestInterface $inputRequest, RequestInterface $ou
101182
/**
102183
* @dataProvider provideRequests
103184
*/
104-
public function testError(RequestInterface $inputRequest, RequestInterface $outputRequest)
185+
public function testError(RequestInterface $inputRequest, RequestInterface $outputRequest, array $clientOptions, array $requestOptions)
105186
{
106187
$exceptionMessage = 'Exception turned InvalidArgumentException';
107188
$exception = new Exception($exceptionMessage);
@@ -129,15 +210,9 @@ public function testError(RequestInterface $inputRequest, RequestInterface $outp
129210
$loop,
130211
new ContainerLocator($container),
131212
$handler,
132-
[
133-
Options::SCHEMA => 'http',
134-
Options::HOST => 'api.example.com',
135-
Options::MIDDLEWARE => [
136-
DummyMiddleware::class,
137-
],
138-
]
213+
$clientOptions
139214
);
140215

141-
await($client->request($inputRequest, [], true), $loop);
216+
await($client->request($inputRequest, $requestOptions, true), $loop);
142217
}
143218
}

0 commit comments

Comments
 (0)