From 7b551188943f2840e8e6fda44d6e6a0af211da1f Mon Sep 17 00:00:00 2001 From: Lily Ballard Date: Tue, 23 Apr 2019 19:11:22 -0700 Subject: [PATCH] Provide PMHTTPStatusCodeErrorKey user info key for more error types Fixes #59. --- README.md | 2 ++ Sources/ObjectiveC.swift | 4 +++- Sources/PMHTTPError.m | 3 +++ Tests/PMHTTPErrorTests.m | 21 +++++++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a7592c..251333b 100644 --- a/README.md +++ b/README.md @@ -416,7 +416,9 @@ work by you shall be dual licensed as above, without any additional terms or con * Add computed properties on `HTTPManagerError` for convenient access to the associated values (e.g. `.response`, `.body`, etc). * Add computed property `HTTPManagerError.statusCode` that returns the failing status code for the error, or `nil` for `.unexpectedContentType` ([#60][]). * Add Obj-C function `PMHTTPErrorGetStatusCode()` that returns the failing status code for the error, or `nil` for `PMHTTPErrorUnexpectedContentType` or for non-PMHTTP errors ([#60][]). +* Provide `PMHTTPStatusCodeErrorKey` user info key for more error types ([#59][]). +[#59]: https://github.com/postmates/PMHTTP/issues/59 "PMHTTPStatusCodeErrorKey should be used for unauthorized and unexpectedNoContent · Issue #59 · postmates/PMHTTP" [#60]: https://github.com/postmates/PMHTTP/issues/60 "HTTPManagerError should have .statusCode property · Issue #60 · postmates/PMHTTP" [#62]: https://github.com/postmates/PMHTTP/issues/62 "Unknown Hint Identifier for Image MIME Types · Issue #62 · postmates/PMHTTP" diff --git a/Sources/ObjectiveC.swift b/Sources/ObjectiveC.swift index 9e9bec1..6242d8d 100644 --- a/Sources/ObjectiveC.swift +++ b/Sources/ObjectiveC.swift @@ -259,6 +259,7 @@ extension HTTPManagerError: CustomNSError { var userInfo: [String: Any] = [ NSLocalizedDescriptionKey: auth?.localizedDescription?(for: self) ?? "401 Unauthorized HTTP response", PMHTTPURLResponseErrorKey: response, + PMHTTPStatusCodeErrorKey: 401, PMHTTPBodyDataErrorKey: body ] userInfo[PMHTTPAuthErrorKey] = auth @@ -273,7 +274,8 @@ extension HTTPManagerError: CustomNSError { case let .unexpectedNoContent(response): return [ NSLocalizedDescriptionKey: "HTTP response returned 204 No Content when an entity was expected", - PMHTTPURLResponseErrorKey: response] + PMHTTPURLResponseErrorKey: response, + PMHTTPStatusCodeErrorKey: 204] case let .unexpectedRedirect(statusCode, location, response, body): let statusString = HTTPURLResponse.localizedString(forStatusCode: statusCode) var userInfo: [String: Any] = [ diff --git a/Sources/PMHTTPError.m b/Sources/PMHTTPError.m index e912879..45720ad 100644 --- a/Sources/PMHTTPError.m +++ b/Sources/PMHTTPError.m @@ -38,10 +38,13 @@ BOOL PMHTTPErrorIsFailedResponse(NSError * _Nullable error, NSInteger statusCode return [errorStatusCode isKindOfClass:[NSNumber class]] ? errorStatusCode : nil; } case PMHTTPErrorUnauthorized: + // NB: Ignore PMHTTPStatusCodeErrorKey and just hardcode the status here, because there's only one valid code. + // This protects us from hand-crafted `NSError`s that are missing the key. return @401; case PMHTTPErrorUnexpectedContentType: return nil; case PMHTTPErrorUnexpectedNoContent: + // NB: See above regarding hardcoding the status code. return @204; } return nil; diff --git a/Tests/PMHTTPErrorTests.m b/Tests/PMHTTPErrorTests.m index 533a406..f62941e 100644 --- a/Tests/PMHTTPErrorTests.m +++ b/Tests/PMHTTPErrorTests.m @@ -82,6 +82,27 @@ - (void)testPMHTTPErrorGetStatusCode { XCTAssertEqualObjects(PMHTTPErrorGetStatusCode([ObjCTestSupport createUnexpectedNoContentErrorWith:response]), @204, @"unexpectedNoContent"); XCTAssertEqualObjects(PMHTTPErrorGetStatusCode([ObjCTestSupport createUnexpectedRedirectErrorWithStatusCode:301 location:nil response:response body:[NSData data]]), @301, @"unexpectedRedirect code 301"); XCTAssertEqualObjects(PMHTTPErrorGetStatusCode([ObjCTestSupport createUnexpectedRedirectErrorWithStatusCode:304 location:nil response:response body:[NSData data]]), @304, @"unexpectedRedirect code 304"); + + // Ensure we're not relying on PMHTTPStatusCodeErrorKey for the error types that don't need it + XCTAssertEqualObjects(PMHTTPErrorGetStatusCode(errorWithStatusCode([ObjCTestSupport createUnauthorizedErrorWith:nil response:response body:[NSData data] bodyJson:nil], 500)), @401, @"unauthorized with modified status code userInfo key"); + XCTAssertEqualObjects(PMHTTPErrorGetStatusCode(errorWithStatusCode([ObjCTestSupport createUnexpectedNoContentErrorWith:response], 500)), @204, @"unexpectedNoContent"); +} + +- (void)testPMHTTPStatusCodeErrorKey { + // Ensure we provide PMHTTPStatusCodeErrorKey for all errors that PMHTTPErrorGetStatusCode returns a value for + NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"http://example.com"] statusCode:419 HTTPVersion:nil headerFields:nil]; + + XCTAssertEqualObjects([ObjCTestSupport createFailedResponseErrorWithStatusCode:404 response:response body:[NSData data] bodyJson:nil].userInfo[PMHTTPStatusCodeErrorKey], @404, @"failedResponse code 404"); + XCTAssertEqualObjects([ObjCTestSupport createUnauthorizedErrorWith:nil response:response body:[NSData data] bodyJson:nil].userInfo[PMHTTPStatusCodeErrorKey], @401, @"unauthorized"); + XCTAssertNil([ObjCTestSupport createUnexpectedContentTypeErrorWithContentType:@"text/plain" response:response body:[NSData data]].userInfo[PMHTTPStatusCodeErrorKey], @"unexpectedContentType"); + XCTAssertEqualObjects([ObjCTestSupport createUnexpectedNoContentErrorWith:response].userInfo[PMHTTPStatusCodeErrorKey], @204, @"unexpectedNoContent"); + XCTAssertEqualObjects([ObjCTestSupport createUnexpectedRedirectErrorWithStatusCode:304 location:nil response:response body:[NSData data]].userInfo[PMHTTPStatusCodeErrorKey], @304, @"unexpectedRedirect"); +} + +static NSError * _Nonnull errorWithStatusCode(NSError * _Nonnull error, NSInteger statusCode) { + NSMutableDictionary *userInfo = [error.userInfo mutableCopy]; + userInfo[PMHTTPStatusCodeErrorKey] = @(statusCode); + return [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; } @end