Skip to content

Commit 597e046

Browse files
authored
Merge pull request #149 from DexterHarrison/3.x
Add Exception for Exceeding Forge API Rate Limit
2 parents 9a89f18 + 343a6a0 commit 597e046

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Laravel\Forge\Exceptions;
4+
5+
use Exception;
6+
7+
class RateLimitExceededException extends Exception
8+
{
9+
/**
10+
* The timestamp that the rate limit will be reset.
11+
*
12+
* @var int|null
13+
*/
14+
public $rateLimitResetsAt;
15+
16+
/**
17+
* Create a new exception instance.
18+
*
19+
* @param int|null $rateLimitReset
20+
* @return void
21+
*/
22+
public function __construct($rateLimitReset)
23+
{
24+
parent::__construct('Too Many Requests.');
25+
26+
$this->rateLimitResetsAt = $rateLimitReset;
27+
}
28+
}

src/MakesHttpRequests.php

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Exception;
66
use Laravel\Forge\Exceptions\FailedActionException;
77
use Laravel\Forge\Exceptions\NotFoundException;
8+
use Laravel\Forge\Exceptions\RateLimitExceededException;
89
use Laravel\Forge\Exceptions\TimeoutException;
910
use Laravel\Forge\Exceptions\ValidationException;
1011
use Psr\Http\Message\ResponseInterface;
@@ -97,6 +98,7 @@ protected function request($verb, $uri, array $payload = [])
9798
* @throws \Laravel\Forge\Exceptions\FailedActionException
9899
* @throws \Laravel\Forge\Exceptions\NotFoundException
99100
* @throws \Laravel\Forge\Exceptions\ValidationException
101+
* @throws \Laravel\Forge\Exceptions\RateLimitExceededException
100102
*/
101103
protected function handleRequestError(ResponseInterface $response)
102104
{
@@ -112,6 +114,14 @@ protected function handleRequestError(ResponseInterface $response)
112114
throw new FailedActionException((string) $response->getBody());
113115
}
114116

117+
if ($response->getStatusCode() === 429) {
118+
throw new RateLimitExceededException(
119+
$response->hasHeader('x-ratelimit-reset')
120+
? (int) $response->getHeader('x-ratelimit-reset')[0]
121+
: null
122+
);
123+
}
124+
115125
throw new Exception((string) $response->getBody());
116126
}
117127

tests/ForgeSDKTest.php

+35
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use GuzzleHttp\Psr7\Response;
77
use Laravel\Forge\Exceptions\FailedActionException;
88
use Laravel\Forge\Exceptions\NotFoundException;
9+
use Laravel\Forge\Exceptions\RateLimitExceededException;
910
use Laravel\Forge\Exceptions\TimeoutException;
1011
use Laravel\Forge\Exceptions\ValidationException;
1112
use Laravel\Forge\Forge;
@@ -174,4 +175,38 @@ public function testRetryHandlesFalseyArrayResultFromClosure()
174175
$this->assertSame([], $e->output());
175176
}
176177
}
178+
179+
public function testRateLimitExceededWithHeaderSet()
180+
{
181+
$forge = new Forge('123', $http = Mockery::mock(Client::class));
182+
183+
$timestamp = strtotime(date('Y-m-d H:i:s'));
184+
185+
$http->shouldReceive('request')->once()->with('GET', 'recipes', [])->andReturn(
186+
new Response(429, [
187+
'x-ratelimit-reset' => $timestamp,
188+
], 'Too Many Attempts.')
189+
);
190+
191+
try {
192+
$forge->recipes();
193+
} catch (RateLimitExceededException $e) {
194+
$this->assertSame($timestamp, $e->rateLimitResetsAt);
195+
}
196+
}
197+
198+
public function testRateLimitExceededWithHeaderNotAvailable()
199+
{
200+
$forge = new Forge('123', $http = Mockery::mock(Client::class));
201+
202+
$http->shouldReceive('request')->once()->with('GET', 'recipes', [])->andReturn(
203+
new Response(429, [], 'Too Many Attempts.')
204+
);
205+
206+
try {
207+
$forge->recipes();
208+
} catch (RateLimitExceededException $e) {
209+
$this->assertNull($e->rateLimitResetsAt);
210+
}
211+
}
177212
}

0 commit comments

Comments
 (0)