Fix issues with mapping errors from 5xx payloads. Ensure acceptableStatusCodes tracks the status codes configured in the response descriptors. fixes #1157

This commit is contained in:
Blake Watters
2013-01-16 12:38:44 -05:00
parent 868d83be2c
commit c26739ce6b
4 changed files with 42 additions and 17 deletions

View File

@@ -20,6 +20,7 @@
#import "RKObjectRequestOperation.h"
#import "RKResponseMapperOperation.h"
#import "RKResponseDescriptor.h"
#import "RKMIMETypeSerialization.h"
#import "RKHTTPUtilities.h"
#import "RKLog.h"
@@ -57,15 +58,14 @@ static inline NSString *RKDescriptionForRequest(NSURLRequest *request)
return [NSString stringWithFormat:@"%@ '%@'", request.HTTPMethod, [request.URL absoluteString]];
}
static NSIndexSet *RKObjectRequestOperationAcceptableStatusCodes()
static NSIndexSet *RKAcceptableStatusCodesFromResponseDescriptors(NSArray *responseDescriptors)
{
static NSMutableIndexSet *statusCodes = nil;
if (! statusCodes) {
statusCodes = [NSMutableIndexSet indexSet];
[statusCodes addIndexesInRange:RKStatusCodeRangeForClass(RKStatusCodeClassSuccessful)];
[statusCodes addIndexesInRange:RKStatusCodeRangeForClass(RKStatusCodeClassClientError)];
}
return statusCodes;
NSMutableIndexSet *acceptableStatusCodes = [NSMutableIndexSet indexSet];
[responseDescriptors enumerateObjectsUsingBlock:^(RKResponseDescriptor *responseDescriptor, NSUInteger idx, BOOL *stop) {
[acceptableStatusCodes addIndexes:responseDescriptor.statusCodes];
}];
// If there are no indexes specified in the response descriptors, then we want to aceept anything
return [acceptableStatusCodes count] ? acceptableStatusCodes : nil;
}
static NSString *RKStringForStateOfObjectRequestOperation(RKObjectRequestOperation *operation)
@@ -143,7 +143,7 @@ static NSString *RKStringDescribingURLResponseWithData(NSURLResponse *response,
self.responseDescriptors = responseDescriptors;
self.HTTPRequestOperation = requestOperation;
self.HTTPRequestOperation.acceptableContentTypes = [RKMIMETypeSerialization registeredMIMETypes];
self.HTTPRequestOperation.acceptableStatusCodes = RKObjectRequestOperationAcceptableStatusCodes();
self.HTTPRequestOperation.acceptableStatusCodes = RKAcceptableStatusCodesFromResponseDescriptors(responseDescriptors);
}
return self;

View File

@@ -91,6 +91,17 @@ static NSString *RKFailureReasonErrorStringForResponseDescriptorsMismatchWithRes
return failureReason;
}
static NSIndexSet *RKErrorStatusCodes()
{
static NSIndexSet *errorStatusCodes = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
errorStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(400, 200)];
});
return errorStatusCodes;
}
/**
A serial dispatch queue used for all deserialization of response bodies
*/
@@ -211,10 +222,10 @@ static dispatch_queue_t RKResponseMapperSerializationQueue() {
{
if (self.isCancelled) return;
BOOL isClientError = NSLocationInRange(self.response.statusCode, RKStatusCodeRangeForClass(RKStatusCodeClassClientError));
BOOL isErrorStatusCode = [RKErrorStatusCodes() containsIndex:self.response.statusCode];
// If we are an error response and empty, we emit an error that the content is unmappable
if (isClientError && [self hasEmptyResponse]) {
if (isErrorStatusCode && [self hasEmptyResponse]) {
self.error = RKUnprocessableClientErrorFromResponse(self.response);
return;
}
@@ -258,7 +269,7 @@ static dispatch_queue_t RKResponseMapperSerializationQueue() {
self.mappingResult = [self performMappingWithObject:parsedBody error:&error];
// If the response is a client error return either the mapping error or the mapped result to the caller as the error
if (isClientError) {
if (isErrorStatusCode) {
if ([self.mappingResult count] > 0) {
error = RKErrorFromMappingResult(self.mappingResult);
} else {

View File

@@ -54,9 +54,6 @@
- (RKResponseDescriptor *)responseDescriptorForComplexUser
{
// NSMutableDictionary *mappingDictionary = [NSMutableDictionary dictionary];
// [mappingsDictionary setObject:userMapping forKey:@"data.STUser"];
// return provider;
RKObjectMapping *userMapping = [RKObjectMapping mappingForClass:[RKTestComplexUser class]];
[userMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:@"firstname" toKeyPath:@"firstname"]];
@@ -606,7 +603,7 @@
[requestOperation waitUntilFinished];
expect(requestOperation.error).notTo.beNil();
expect([requestOperation.error localizedDescription]).to.equal(@"Expected status code in (200-299,400-499), got 503");
expect([requestOperation.error localizedDescription]).to.equal(@"Expected status code in (200-299), got 503");
}
- (void)testThatMapperOperationDelegateIsPassedThroughToUnderlyingMapperOperation

View File

@@ -1105,4 +1105,21 @@
expect(tagsCount).to.equal(2);
}
- (void)testMappingErrorsFromFiveHundredStatusCodeRange
{
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[RKTestFactory baseURL]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassServerError);
RKObjectMapping *errorResponseMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorResponseMapping addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@"errorMessage"]];
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:errorResponseMapping pathPattern:nil keyPath:@"errors" statusCodes:statusCodes]];
__block NSError *error = nil;
[objectManager getObjectsAtPath:@"/fail" parameters:nil success:nil failure:^(RKObjectRequestOperation *operation, NSError *blockError) {
error = blockError;
}];
expect(error).willNot.beNil();
expect([error localizedDescription]).to.equal(@"error1, error2");
}
@end