Skip to content

Commit 219fbd6

Browse files
simonseyer1ec5
authored andcommitted
Fixed parsing of the rate limit error response
Cherry-picked from f7a543a.
1 parent af6119f commit 219fbd6

File tree

2 files changed

+58
-6
lines changed

2 files changed

+58
-6
lines changed

MapboxGeocoder/MBGeocoder.swift

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,23 +282,22 @@ public class Geocoder: NSObject {
282282
/**
283283
Returns an error that supplements the given underlying error with additional information from the an HTTP response’s body or headers.
284284
*/
285-
private static func descriptiveError(json: JSONDictionary, response: NSURLResponse?, underlyingError error: NSError?) -> NSError {
285+
static func descriptiveError(json: JSONDictionary, response: NSURLResponse?, underlyingError error: NSError?) -> NSError {
286286
var userInfo = error?.userInfo ?? [:]
287287
if let response = response as? NSHTTPURLResponse {
288288
var failureReason: String? = nil
289289
var recoverySuggestion: String? = nil
290290
switch response.statusCode {
291291
case 429:
292-
if let timeInterval = response.allHeaderFields["x-rate-limit-interval"] as? NSTimeInterval, maximumCountOfRequests = response.allHeaderFields["x-rate-limit-limit"] as? UInt {
292+
if let timeInterval = response.rateLimitInterval, let maximumCountOfRequests = response.rateLimit {
293293
let intervalFormatter = NSDateComponentsFormatter()
294294
intervalFormatter.unitsStyle = .Full
295-
let formattedInterval = intervalFormatter.stringFromTimeInterval(timeInterval)
295+
let formattedInterval = intervalFormatter.stringFromTimeInterval(timeInterval) ?? "? minutes"
296296
let formattedCount = NSNumberFormatter.localizedStringFromNumber(maximumCountOfRequests, numberStyle: .DecimalStyle)
297297
failureReason = "More than \(formattedCount) requests have been made with this access token within a period of \(formattedInterval)."
298298
}
299-
if let rolloverTimestamp = response.allHeaderFields["x-rate-limit-reset"] as? Double {
300-
let date = NSDate(timeIntervalSince1970: rolloverTimestamp)
301-
let formattedDate = NSDateFormatter.localizedStringFromDate(date, dateStyle: .LongStyle, timeStyle: .FullStyle)
299+
if let rolloverTime = response.rateLimitResetTime {
300+
let formattedDate = NSDateFormatter.localizedStringFromDate(rolloverTime, dateStyle: .LongStyle, timeStyle: .FullStyle)
302301
recoverySuggestion = "Wait until \(formattedDate) before retrying."
303302
}
304303
default:
@@ -311,3 +310,35 @@ public class Geocoder: NSObject {
311310
return NSError(domain: error?.domain ?? MBGeocoderErrorDomain, code: error?.code ?? -1, userInfo: userInfo)
312311
}
313312
}
313+
314+
extension NSHTTPURLResponse {
315+
316+
@nonobjc static let rateLimitIntervalHeaderKey = "X-Rate-Limit-Interval"
317+
@nonobjc static let rateLimitLimitHeaderKey = "X-Rate-Limit-Limit"
318+
@nonobjc static let rateLimitResetHeaderKey = "X-Rate-Limit-Reset"
319+
320+
var rateLimit: UInt? {
321+
guard let limit = allHeaderFields[NSHTTPURLResponse.rateLimitLimitHeaderKey] as? String else {
322+
return nil
323+
}
324+
return UInt(limit)
325+
}
326+
327+
var rateLimitInterval: NSTimeInterval? {
328+
guard let interval = allHeaderFields[NSHTTPURLResponse.rateLimitIntervalHeaderKey] as? String else {
329+
return nil
330+
}
331+
return NSTimeInterval(interval)
332+
}
333+
334+
var rateLimitResetTime: NSDate? {
335+
guard let resetTime = allHeaderFields[NSHTTPURLResponse.rateLimitResetHeaderKey] as? String else {
336+
return nil
337+
}
338+
guard let resetTimeNumber = Double(resetTime) else {
339+
return nil;
340+
}
341+
return NSDate(timeIntervalSince1970: resetTimeNumber)
342+
}
343+
344+
}

MapboxGeocoderTests/GeocoderTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import OHHTTPStubs
55
let BogusToken = "pk.feedCafeDadeDeadBeef-BadeBede.FadeCafeDadeDeed-BadeBede"
66

77
class GeocoderTests: XCTestCase {
8+
9+
override func setUp() {
10+
// Make sure tests run in all time zones
11+
NSTimeZone.setDefaultTimeZone(NSTimeZone(forSecondsFromGMT: 0))
12+
}
13+
814
override func tearDown() {
915
OHHTTPStubs.removeAllStubs()
1016
super.tearDown()
@@ -15,4 +21,19 @@ class GeocoderTests: XCTestCase {
1521
XCTAssertEqual(geocoder.accessToken, BogusToken)
1622
XCTAssertEqual(geocoder.apiEndpoint.absoluteString, "https://api.mapbox.com")
1723
}
24+
25+
func testRateLimitErrorParsing() {
26+
let json = ["message" : "Hit rate limit"]
27+
28+
let url = NSURL(string: "https://api.mapbox.com")!
29+
let headerFields = ["X-Rate-Limit-Interval" : "60", "X-Rate-Limit-Limit" : "600", "X-Rate-Limit-Reset" : "1479460584"]
30+
let response = NSHTTPURLResponse(URL: url, statusCode: 429, HTTPVersion: nil, headerFields: headerFields)
31+
32+
let error: NSError? = nil
33+
34+
let resultError = Geocoder.descriptiveError(json, response: response, underlyingError: error)
35+
36+
XCTAssertEqual(resultError.localizedFailureReason, "More than 600 requests have been made with this access token within a period of 1 minute.")
37+
XCTAssertEqual(resultError.localizedRecoverySuggestion, "Wait until November 18, 2016 at 9:16:24 AM GMT before retrying.")
38+
}
1839
}

0 commit comments

Comments
 (0)