Skip to content

Commit 48e7522

Browse files
Merge pull request #30 from stevenmaguire/add-rate-limiting-support
Add rate limit reporting support
2 parents d18c91f + e047c8f commit 48e7522

File tree

7 files changed

+172
-10
lines changed

7 files changed

+172
-10
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,27 @@ Version | Constant | Documentation
6666
v2 | `Stevenmaguire\Yelp\Version::TWO` | [API-GUIDE-v2.md](API-GUIDE-v2.md)
6767
v3 | `Stevenmaguire\Yelp\Version::THREE` | [API-GUIDE-v3.md](API-GUIDE-v3.md)
6868

69+
##### Get Rate Limit data from most recent request
70+
71+
For the v3 client, [rate limiting data](https://www.yelp.com/developers/documentation/v3/rate_limiting) is avaiable after a recent request.
72+
73+
```php
74+
// $latestRateLimit will be null if an http request hasn't been successfully completed.
75+
$latestRateLimit = $client->getRateLimit();
76+
77+
// The maximum number of calls you can make per day
78+
$latestDailyLimit = $latestRateLimit->dailyLimit;
79+
80+
// The number of calls remaining within the current day
81+
$latestRemaining = $latestRateLimit->remaining;
82+
83+
// The time at which the current rate limiting window will expire as an ISO 8601 timestamp
84+
$latestResetTime = $latestRateLimit->resetTime;
85+
86+
// The time at which the current rate limiting data was observed as an ISO 8601 timestamp
87+
$latestCreatedAt = $latestRateLimit->createdAt;
88+
```
89+
6990
### Exceptions
7091

7192
If the API request results in an Http error, the client will throw a `Stevenmaguire\Yelp\Exception\HttpException` that includes the response body, as a string, from the Yelp API.

src/Tool/HttpTrait.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use GuzzleHttp\Psr7\Request;
99
use GuzzleHttp\Psr7\Uri;
1010
use Psr\Http\Message\RequestInterface;
11+
use Psr\Http\Message\ResponseInterface;
1112
use Stevenmaguire\Yelp\Exception\ClientConfigurationException;
1213
use Stevenmaguire\Yelp\Exception\HttpException;
1314

@@ -148,6 +149,15 @@ public function getResponse(RequestInterface $request)
148149
}
149150
}
150151

152+
/**
153+
* Provides a hook that handles the response before returning to the consumer.
154+
*
155+
* @param ResponseInterface $response
156+
*
157+
* @return ResponseInterface
158+
*/
159+
abstract protected function handleResponse(ResponseInterface $response);
160+
151161
/**
152162
* Updates query params array to apply yelp specific formatting rules.
153163
*
@@ -182,7 +192,7 @@ protected function prepareQueryParams($params = [], $csvParams = [])
182192
*/
183193
protected function processRequest(RequestInterface $request)
184194
{
185-
$response = $this->getResponse($request);
195+
$response = $this->handleResponse($this->getResponse($request));
186196

187197
return json_decode($response->getBody());
188198
}

src/v2/Client.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use GuzzleHttp\HandlerStack;
88
use GuzzleHttp\Subscriber\Oauth\Oauth1;
99
use Psr\Http\Message\RequestInterface;
10+
use Psr\Http\Message\ResponseInterface;
1011
use Stevenmaguire\Yelp\Contract\Http as HttpContract;
1112
use Stevenmaguire\Yelp\Exception\HttpException;
1213
use Stevenmaguire\Yelp\Tool\ConfigurationTrait;
@@ -150,6 +151,18 @@ public function getResponse(RequestInterface $request)
150151
}
151152
}
152153

154+
/**
155+
* Provides a hook that handles the response before returning to the consumer.
156+
*
157+
* @param ResponseInterface $response
158+
*
159+
* @return ResponseInterface
160+
*/
161+
protected function handleResponse(ResponseInterface $response)
162+
{
163+
return $response;
164+
}
165+
153166
/**
154167
* Fetches results from the Business Search API.
155168
*

src/v3/Client.php

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Stevenmaguire\Yelp\v3;
44

55
use GuzzleHttp\Client as HttpClient;
6+
use Psr\Http\Message\ResponseInterface;
67
use Stevenmaguire\Yelp\Contract\Http as HttpContract;
78
use Stevenmaguire\Yelp\Tool\ConfigurationTrait;
89
use Stevenmaguire\Yelp\Tool\HttpTrait;
@@ -26,6 +27,13 @@ class Client implements HttpContract
2627
*/
2728
protected $apiKey;
2829

30+
/**
31+
* Rate limit
32+
*
33+
* @var RateLimit|null
34+
*/
35+
protected $rateLimit;
36+
2937
/**
3038
* Creates new client
3139
*
@@ -54,9 +62,7 @@ public function __construct(array $options = array())
5462
public function createDefaultHttpClient()
5563
{
5664
return new HttpClient([
57-
'headers' => [
58-
'Authorization' => 'Bearer ' . $this->getBearerToken(),
59-
]
65+
'headers' => $this->getDefaultHeaders()
6066
]);
6167
}
6268

@@ -72,7 +78,7 @@ public function createDefaultHttpClient()
7278
public function getAutocompleteResults($parameters = [])
7379
{
7480
$path = $this->appendParametersToUrl('/v3/autocomplete', $parameters);
75-
$request = $this->getRequest('GET', $path);
81+
$request = $this->getRequest('GET', $path, $this->getDefaultHeaders());
7682

7783
return $this->processRequest($request);
7884
}
@@ -105,7 +111,7 @@ private function getBearerToken()
105111
public function getBusiness($businessId, $parameters = [])
106112
{
107113
$path = $this->appendParametersToUrl('/v3/businesses/'.$businessId, $parameters);
108-
$request = $this->getRequest('GET', $path);
114+
$request = $this->getRequest('GET', $path, $this->getDefaultHeaders());
109115

110116
return $this->processRequest($request);
111117
}
@@ -123,7 +129,7 @@ public function getBusiness($businessId, $parameters = [])
123129
public function getBusinessReviews($businessId, $parameters = [])
124130
{
125131
$path = $this->appendParametersToUrl('/v3/businesses/'.$businessId.'/reviews', $parameters);
126-
$request = $this->getRequest('GET', $path);
132+
$request = $this->getRequest('GET', $path, $this->getDefaultHeaders());
127133

128134
return $this->processRequest($request);
129135
}
@@ -142,7 +148,7 @@ public function getBusinessesSearchResults($parameters = [])
142148
$csvParams = ['attributes', 'categories', 'price'];
143149

144150
$path = $this->appendParametersToUrl('/v3/businesses/search', $parameters, $csvParams);
145-
$request = $this->getRequest('GET', $path);
151+
$request = $this->getRequest('GET', $path, $this->getDefaultHeaders());
146152

147153
return $this->processRequest($request);
148154
}
@@ -163,11 +169,37 @@ public function getBusinessesSearchResultsByPhone($phoneNumber)
163169
];
164170

165171
$path = $this->appendParametersToUrl('/v3/businesses/search/phone', $parameters);
166-
$request = $this->getRequest('GET', $path);
172+
$request = $this->getRequest('GET', $path, $this->getDefaultHeaders());
167173

168174
return $this->processRequest($request);
169175
}
170176

177+
/**
178+
* Builds and returns default headers, specifically including the Authorization
179+
* header used for authenticating HTTP requests to Yelp.
180+
*
181+
* @return array
182+
*/
183+
protected function getDefaultHeaders()
184+
{
185+
return [
186+
'Authorization' => 'Bearer ' . $this->getBearerToken(),
187+
];
188+
}
189+
190+
/**
191+
* Returns the latest rate limit metrics, absorbed from the HTTP headers of
192+
* the most recent HTTP request to the Yelp v3 service.
193+
*
194+
* @return RateLimit|null
195+
*
196+
* @see https://www.yelp.com/developers/documentation/v3/rate_limiting
197+
*/
198+
public function getRateLimit()
199+
{
200+
return $this->rateLimit;
201+
}
202+
171203
/**
172204
* Fetches results from the Business Search API by Type.
173205
*
@@ -181,8 +213,25 @@ public function getBusinessesSearchResultsByPhone($phoneNumber)
181213
public function getTransactionsSearchResultsByType($type, $parameters = [])
182214
{
183215
$path = $this->appendParametersToUrl('/v3/transactions/'.$type.'/search', $parameters);
184-
$request = $this->getRequest('GET', $path);
216+
$request = $this->getRequest('GET', $path, $this->getDefaultHeaders());
185217

186218
return $this->processRequest($request);
187219
}
220+
221+
/**
222+
* Provides a hook that handles the response before returning to the consumer.
223+
*
224+
* @param ResponseInterface $response
225+
*
226+
* @return ResponseInterface
227+
*/
228+
protected function handleResponse(ResponseInterface $response)
229+
{
230+
$this->rateLimit = new RateLimit;
231+
$this->rateLimit->dailyLimit = (integer) $response->getHeaderLine('RateLimit-DailyLimit');
232+
$this->rateLimit->remaining = (integer) $response->getHeaderLine('RateLimit-Remaining');
233+
$this->rateLimit->resetTime = $response->getHeaderLine('RateLimit-ResetTime');
234+
235+
return $response;
236+
}
188237
}

src/v3/RateLimit.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Stevenmaguire\Yelp\v3;
4+
5+
class RateLimit
6+
{
7+
/**
8+
* Created at, ISO 8601 format
9+
*
10+
* @var string
11+
*/
12+
public $createdAt;
13+
14+
/**
15+
* Daily limit
16+
*
17+
* @var integer
18+
*/
19+
public $dailyLimit;
20+
21+
/**
22+
* Remaining
23+
*
24+
* @var integer
25+
*/
26+
public $remaining;
27+
28+
/**
29+
* Reset time
30+
*
31+
* @var string
32+
*/
33+
public $resetTime;
34+
35+
/**
36+
* Creates a new rate limit instance, setting the createdAt attribute.
37+
*/
38+
public function __construct()
39+
{
40+
$this->createdAt = (string) date('c');
41+
}
42+
}

tests/Tool/HttpTraitTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,25 @@
22

33
namespace Stevenmaguire\Yelp\Test\Tool;
44

5+
use Psr\Http\Message\ResponseInterface;
56
use Stevenmaguire\Yelp\Tool\HttpTrait;
67

78
class HttpTraitTest extends \PHPUnit_Framework_TestCase
89
{
910
use HttpTrait;
1011

12+
/**
13+
* Provides a hook that handles the response before returning to the consumer.
14+
*
15+
* @param ResponseInterface $response
16+
*
17+
* @return ResponseInterface
18+
*/
19+
protected function handleResponse(ResponseInterface $response)
20+
{
21+
return $response;
22+
}
23+
1124
public function testGetRequestAddsHostWhenNotProvided()
1225
{
1326
$this->apiHost = 'foo';

tests/v3/ClientTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ public function testGetBusiness()
143143
$businessId = 'foo';
144144
$path = '/v3/businesses/'.$businessId;
145145
$payload = $this->getResponseJson('business');
146+
$dailyLimit = rand();
147+
$remaining = rand();
148+
$resetTime = uniqid();
146149

147150
$parameters = [
148151
'locale' => 'bar'
@@ -152,7 +155,11 @@ public function testGetBusiness()
152155
$stream->__toString->returns($payload);
153156

154157
$response = Phony::mock(ResponseInterface::class);
158+
155159
$response->getBody->returns($stream->get());
160+
$response->getHeaderLine->with('RateLimit-DailyLimit')->returns($dailyLimit);
161+
$response->getHeaderLine->with('RateLimit-Remaining')->returns($remaining);
162+
$response->getHeaderLine->with('RateLimit-ResetTime')->returns($resetTime);
156163

157164
$httpClient = Phony::mock(HttpClient::class);
158165
$httpClient->send->returns($response->get());
@@ -171,9 +178,16 @@ public function testGetBusiness()
171178
&& ($queryString && strpos((string) $request->getUri(), $queryString) !== false);
172179
})
173180
),
181+
$response->getHeaderLine->calledWith('RateLimit-DailyLimit'),
182+
$response->getHeaderLine->calledWith('RateLimit-Remaining'),
183+
$response->getHeaderLine->calledWith('RateLimit-ResetTime'),
174184
$response->getBody->called(),
175185
$stream->__toString->called()
176186
);
187+
188+
$this->assertEquals($dailyLimit, $this->client->getRateLimit()->dailyLimit);
189+
$this->assertEquals($remaining, $this->client->getRateLimit()->remaining);
190+
$this->assertEquals($resetTime, $this->client->getRateLimit()->resetTime);
177191
}
178192

179193
public function testGetBusinessReviews()

0 commit comments

Comments
 (0)