Skip to content

Commit a47c27a

Browse files
authored
Merge pull request #168 from Sammyjo20/fix/allow-constructed-pending-requests-to-be-authenticated
Fix | Authenticate a PendingRequest after being constructed
2 parents e40ae94 + 26ced25 commit a47c27a

File tree

4 files changed

+76
-9
lines changed

4 files changed

+76
-9
lines changed

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@
3333
"pestphp/pest": "^1.21",
3434
"phpstan/phpstan": "^1.9",
3535
"spatie/ray": "^1.33",
36-
"symfony/dom-crawler": "^6.0",
37-
"symfony/stopwatch": "^6.2"
36+
"symfony/dom-crawler": "^6.0"
3837
},
3938
"suggest": {
4039
"illuminate/collections": "Required for the response collect() method.",

src/Http/PendingRequest.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Saloon\Traits\Conditionable;
1717
use Saloon\Traits\HasMockClient;
1818
use Saloon\Contracts\Body\HasBody;
19+
use Saloon\Contracts\Authenticator;
1920
use Saloon\Helpers\ReflectionHelper;
2021
use GuzzleHttp\Promise\PromiseInterface;
2122
use Saloon\Contracts\Body\BodyRepository;
@@ -93,6 +94,13 @@ class PendingRequest implements PendingRequestContract
9394
*/
9495
protected bool $asynchronous = false;
9596

97+
/**
98+
* Determines if the PendingRequest is ready to be sent
99+
*
100+
* @var bool
101+
*/
102+
protected bool $ready = false;
103+
96104
/**
97105
* Build up the request payload.
98106
*
@@ -134,10 +142,14 @@ public function __construct(Connector $connector, Request $request, MockClient $
134142

135143
$this->registerDefaultMiddleware();
136144

137-
// Finally, we will execute the request middleware pipeline which will
145+
// Next, we will execute the request middleware pipeline which will
138146
// process any middleware added on the connector or the request.
139147

140148
$this->executeRequestPipeline();
149+
150+
// Finally, we'll mark our PendingRequest as ready.
151+
152+
$this->ready = true;
141153
}
142154

143155
/**
@@ -504,4 +516,26 @@ public function isAsynchronous(): bool
504516
{
505517
return $this->asynchronous;
506518
}
519+
520+
/**
521+
* Authenticate the PendingRequest
522+
*
523+
* @param \Saloon\Contracts\Authenticator $authenticator
524+
* @return $this
525+
*/
526+
public function authenticate(Authenticator $authenticator): static
527+
{
528+
$this->authenticator = $authenticator;
529+
530+
// If the PendingRequest has already been constructed, it would be nice
531+
// for someone to be able to run the "authenticate" method after. This
532+
// will allow us to do this. With future versions of Saloon we will
533+
// likely remove this method.
534+
535+
if ($this->ready === true) {
536+
$this->authenticator->set($this);
537+
}
538+
539+
return $this;
540+
}
507541
}

tests/Feature/RetryRequestTest.php

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use Saloon\Http\PendingRequest;
66
use Saloon\Http\Faking\MockClient;
77
use Saloon\Http\Faking\MockResponse;
8-
use Symfony\Component\Stopwatch\Stopwatch;
8+
use Saloon\Http\Auth\TokenAuthenticator;
99
use Saloon\Exceptions\Request\RequestException;
1010
use Saloon\Tests\Fixtures\Requests\UserRequest;
1111
use Saloon\Tests\Fixtures\Connectors\TestConnector;
@@ -98,17 +98,14 @@
9898
$connector = new TestConnector;
9999
$connector->withMockClient($mockClient);
100100

101-
$stopwatch = new Stopwatch();
102-
$stopwatch->start('sendAndRetry');
101+
$start = microtime(true);
103102

104103
$connector->sendAndRetry(new UserRequest, 3, 1000);
105104

106-
$duration = $stopwatch->stop('sendAndRetry')->getDuration();
107-
108105
// It should be a duration of 2000ms (2 seconds) because the there are two requests
109106
// after the first.
110107

111-
expect(floor($duration / 1000) * 1000)->toBeGreaterThanOrEqual(2000);
108+
expect(round(microtime(true) - $start))->toBeGreaterThanOrEqual(2);
112109
});
113110

114111
test('an exception other than a request exception will not be retried', function () {
@@ -263,3 +260,23 @@
263260

264261
$connector->sendAndRetry(new UserRequest, 0);
265262
});
263+
264+
test('you can authenticate the pending request inside the retry handler', function () {
265+
$mockClient = new MockClient([
266+
MockResponse::make(['name' => 'Sam'], 401),
267+
MockResponse::make(['name' => 'Gareth'], 200),
268+
]);
269+
270+
$connector = new TestConnector;
271+
$connector->withMockClient($mockClient);
272+
273+
$response = $connector->sendAndRetry(new UserRequest, 2, 0, function (Exception $exception, PendingRequest $pendingRequest) {
274+
$pendingRequest->authenticate(new TokenAuthenticator('newToken'));
275+
276+
return true;
277+
});
278+
279+
expect($response->status())->toBe(200);
280+
expect($response->json())->toEqual(['name' => 'Gareth']);
281+
expect($response->getPendingRequest()->headers()->get('Authorization'))->toEqual('Bearer newToken');
282+
});

tests/Unit/AuthenticatorTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Saloon\Http\Faking\MockResponse;
88
use Saloon\Http\Auth\TokenAuthenticator;
99
use Saloon\Tests\Fixtures\Requests\UserRequest;
10+
use Saloon\Tests\Fixtures\Connectors\TestConnector;
1011
use Saloon\Exceptions\MissingAuthenticatorException;
1112
use Saloon\Tests\Fixtures\Requests\RequiresAuthRequest;
1213
use Saloon\Tests\Fixtures\Authenticators\PizzaAuthenticator;
@@ -139,3 +140,19 @@
139140

140141
expect($pendingRequest->headers()->get('Authorization'))->toEqual('Bearer yee-haw-request');
141142
});
143+
144+
test('if you use the authenticate method on a fully constructed pending request it will authenticate right away', function () {
145+
$connector = new TestConnector();
146+
$pendingRequest = $connector->createPendingRequest(new UserRequest);
147+
148+
expect($pendingRequest->headers()->all())->toEqual([
149+
'Accept' => 'application/json',
150+
]);
151+
152+
$pendingRequest->authenticate(new TokenAuthenticator('yee-haw-request'));
153+
154+
expect($pendingRequest->headers()->all())->toEqual([
155+
'Accept' => 'application/json',
156+
'Authorization' => 'Bearer yee-haw-request',
157+
]);
158+
});

0 commit comments

Comments
 (0)