New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Path Pattern Mismatches are Very Hard to Debug #1060

Closed
jgervin opened this Issue Dec 6, 2012 · 9 comments

Comments

Projects
None yet
4 participants
@jgervin

jgervin commented Dec 6, 2012

I am trying to use Entitymapping and all goes well until I start trying to send POST's (have not tried GET's yet). When I try to send a POST, the server returns back the data correctly, but I get a 1001 error (Unable to find any mappings for the given content).

I set a debug point in the RKMapperOperation and on line 346 it shows the performKeyPathMappingUsingMappingDictionary: returns an empty dictionary. So it looks like the mappings below are not being created.

//=== SETUP method called after application finishes launch in a RestEngine class. ==============

NSURL *baseURL = [NSURL URLWithString:@"http://myapp.herokuapp.com/"];
objectManager = [RKObjectManager managerWithBaseURL:baseURL];



// Enable Activity Indicator Spinner
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;

// Initialize managed object store
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;


 RKEntityMapping *poolThingsMapping = [RKEntityMapping mappingForEntityForName:@"PoolThing"
                                                            inManagedObjectStore:objectManager.managedObjectStore];
[poolThingsMapping addAttributeMappingsFromArray:@[@"name", @"date_started", @"date_ended"] ];
[poolThingsMapping addAttributeMappingsFromDictionary:@{
 @"_id": @"poolThingID",
 }];

[poolThingsMapping setIdentificationAttributes:@[@"poolThingID"] ];



// Routes for PoolThings
[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[PoolThing class]
                                                    pathPattern:@"/v1/poolThings/:poolThingID" method:RKRequestMethodGET]];

[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[PoolThing class]
                                                    pathPattern:@"/v1/poolThings/:poolThingID" method:RKRequestMethodPUT]];

[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[PoolThing class]
                                                    pathPattern:@"/v1/poolThings/:poolThingID" method:RKRequestMethodDELETE]];

[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[PoolThing class]
                                                    pathPattern:@"/v1/poolThings" method:RKRequestMethodPOST]];


// Register our mappings with the provider
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:poolThingsMapping
                                                                                   pathPattern:@"/v1/poolThings/:poolThingID"
                                                                                       keyPath:nil
                                                                                   statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];




// --- USER MAPPING -----------------------------------------------------------------------------------------------------------

RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:@"User"
                                                   inManagedObjectStore:objectManager.managedObjectStore];

[userMapping addAttributeMappingsFromArray:@[@"username", @"gender", @"email",
                                             @"phone_number", @"photo_url",
                                             @"emergency_contact_name",
                                             @"emergency_contact_phone_number"] ];

[userMapping addAttributeMappingsFromDictionary:@{
 @"_id": @"userID"
 }];


[userMapping setIdentificationAttributes:@[@"userID"] ];



// Routes for Users ==================================
[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[User class]
                                                    pathPattern:@"/v122/users/:userID\\.json" method:RKRequestMethodGET]];

[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[User class]
                                                    pathPattern:@"/v122/users/:userID\\.json" method:RKRequestMethodPUT]];

[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[User class]
                                                    pathPattern:@"/v122/users/:userID\\.json" method:RKRequestMethodDELETE]];

[objectManager.router.routeSet addRoute:[RKRoute routeWithClass:[User class]
                                                    pathPattern:@"/v122/users.json" method:RKRequestMethodPOST]];

// Register our mappings with the provider
RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping
                                                                                       pathPattern:@"/v122/users.json"
                                                                                           keyPath:nil
                                                                                       statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];



[objectManager addResponseDescriptor:responseDescriptor];
[objectManager addResponseDescriptor:userResponseDescriptor];



[managedObjectStore createPersistentStoreCoordinator];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"STMyTHing.sqlite"];
NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"STSeedDatabase" ofType:@"sqlite"];
NSError *error;
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);

// Create the managed object contexts
[managedObjectStore createManagedObjectContexts];

// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];

// This call to the POST makes it to the server and creates the object on the server, but when the server responds with the appropriate data and a 201 success code, I get the 1001 error (Unable to find any mappings for the given content)?

[[RKObjectManager sharedManager]  postObject:joeBlow path:@"/v122/users.json"
                                  parameters:@{
 @"username" : joeBlow.username,
 @"gender" : joeBlow.gender,
 @"email" : joeBlow.email,
 @"phone_number" : joeBlow.phone_number,
 @"emergency_contact_name" : joeBlow.emergency_contact_name,
 @"emergency_contact_phone_number" : joeBlow.emergency_contact_phone_number
}
                                     success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                        RKLogInfo(@"Load complete refresh view...");

                                     }

                                     failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                        RKLogError(@"Load failed with error: %@", error);  <---- 1001 error is here


                                     }];

// === HERE IS THE DATA RETURNED FROM THE SERVER AFTER POST IS SUCCESSFUL ===
{"_id":"50c12819adc4683815000005","email":"jason_nert@hotmail.com","username":"JoeBlow","gender":"male","phone_number":"512-555-1212","photo_url":null,"emergency_contact_name":"Grama mama","emergency_contact_phone_number":"512-333-3333"}

Not sure why the mappings are not being created? Not sure where the bug is?

@blakewatters

This comment has been minimized.

Show comment
Hide comment
@blakewatters

blakewatters Dec 6, 2012

Member

What's most likely happening is that the responseDescriptors are failing to match the URL path pattern. This seems to be the most common hang-up people are running into with 0.20 and I'm working on improving the situation.

To debug, open up 'RKResponseMapperOperation.m' and navigate to the buildResponseMappingsDictionary method. The source looks like this:

- (NSDictionary *)buildResponseMappingsDictionary
{
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    for (RKResponseDescriptor *responseDescriptor in self.responseDescriptors) {
        if ([responseDescriptor matchesResponse:self.response]) {
            id key = responseDescriptor.keyPath ? responseDescriptor.keyPath : [NSNull null];
            [dictionary setObject:responseDescriptor.mapping forKey:key];
        }
    }

    return dictionary;
}

Log or print out self.responseDescriptors. You should see all of your response descriptors that were registered with the object manager. What's most likely happening is that NO is being returned for all invocations of matchesResponse:. Inside matchesResponse:, the URL is compared against the path pattern using a path matcher.

If this dictionary is indeed being returned empty, but the response descriptors are setup, then you'll want to set a breakpoint inside of the matchesURL: or matchesPath: methods on RKResponseDescriptor and take a look at the path and the patterns being matched. There will be a disconnected in there that once resolved will cause everything to snap into place.

I will look into adding some logging or other strategies to improve the debugging situation tonight/tomorrow.

Member

blakewatters commented Dec 6, 2012

What's most likely happening is that the responseDescriptors are failing to match the URL path pattern. This seems to be the most common hang-up people are running into with 0.20 and I'm working on improving the situation.

To debug, open up 'RKResponseMapperOperation.m' and navigate to the buildResponseMappingsDictionary method. The source looks like this:

- (NSDictionary *)buildResponseMappingsDictionary
{
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    for (RKResponseDescriptor *responseDescriptor in self.responseDescriptors) {
        if ([responseDescriptor matchesResponse:self.response]) {
            id key = responseDescriptor.keyPath ? responseDescriptor.keyPath : [NSNull null];
            [dictionary setObject:responseDescriptor.mapping forKey:key];
        }
    }

    return dictionary;
}

Log or print out self.responseDescriptors. You should see all of your response descriptors that were registered with the object manager. What's most likely happening is that NO is being returned for all invocations of matchesResponse:. Inside matchesResponse:, the URL is compared against the path pattern using a path matcher.

If this dictionary is indeed being returned empty, but the response descriptors are setup, then you'll want to set a breakpoint inside of the matchesURL: or matchesPath: methods on RKResponseDescriptor and take a look at the path and the patterns being matched. There will be a disconnected in there that once resolved will cause everything to snap into place.

I will look into adding some logging or other strategies to improve the debugging situation tonight/tomorrow.

@jgervin

This comment has been minimized.

Show comment
Hide comment
@jgervin

jgervin Dec 6, 2012

I found the issue.

My base URL is:
NSURL *baseURL = [NSURL URLWithString:@"http://myapp.herokuapp.com/"];

My paths (see above) are all "/v122/..." or "/v1/..."

The issue is first "/" so if changed to "v122/..." or "v1/..." it works.

I know its my issue and I own, but is there a way for the framework to try as the path as the user, me in this case, has entered it then try again by removing the first "/" if it fails the first time? I know we can't always code around users, but after searching the web this seems like one of the pesky time consuming bugs. Or not sure if there is a way to compile time check the base to force not using and ending "/" or the reverse force no "/" allowed.

Or I can just not be an IDIOT and check them before submitting bugs.

jgervin commented Dec 6, 2012

I found the issue.

My base URL is:
NSURL *baseURL = [NSURL URLWithString:@"http://myapp.herokuapp.com/"];

My paths (see above) are all "/v122/..." or "/v1/..."

The issue is first "/" so if changed to "v122/..." or "v1/..." it works.

I know its my issue and I own, but is there a way for the framework to try as the path as the user, me in this case, has entered it then try again by removing the first "/" if it fails the first time? I know we can't always code around users, but after searching the web this seems like one of the pesky time consuming bugs. Or not sure if there is a way to compile time check the base to force not using and ending "/" or the reverse force no "/" allowed.

Or I can just not be an IDIOT and check them before submitting bugs.

@jgervin jgervin closed this Dec 6, 2012

@blakewatters

This comment has been minimized.

Show comment
Hide comment
@blakewatters

blakewatters Dec 6, 2012

Member

I have a couple of immediate thoughts on how to improve the situation:

  1. Detect that the mappingsDictionary is empty and raise a specific error from the response mapper. This will move the error closer to the source. In this error, include some substantive language about the likelihood that this is because of a path pattern mismatch.
  2. Add some logging infrastructure that lets you cause it to spit out the details of the path matching, letting you see it without having to dig in the debugger.
  3. Expand the header documentation to specifically cover the importance of aligning your baseURL and pathPattern arguments.

Once I've done these things we can see if any further action is warranted. I suspect that will greatly simplify the debugging process/

Member

blakewatters commented Dec 6, 2012

I have a couple of immediate thoughts on how to improve the situation:

  1. Detect that the mappingsDictionary is empty and raise a specific error from the response mapper. This will move the error closer to the source. In this error, include some substantive language about the likelihood that this is because of a path pattern mismatch.
  2. Add some logging infrastructure that lets you cause it to spit out the details of the path matching, letting you see it without having to dig in the debugger.
  3. Expand the header documentation to specifically cover the importance of aligning your baseURL and pathPattern arguments.

Once I've done these things we can see if any further action is warranted. I suspect that will greatly simplify the debugging process/

@blakewatters blakewatters reopened this Dec 6, 2012

@blakewatters

This comment has been minimized.

Show comment
Hide comment
@blakewatters

blakewatters Dec 6, 2012

Member

I am reopening this issue and have renamed it to track those improvements.

Member

blakewatters commented Dec 6, 2012

I am reopening this issue and have renamed it to track those improvements.

@jgervin

This comment has been minimized.

Show comment
Hide comment
@jgervin

jgervin Dec 7, 2012

Thank you!

jgervin commented Dec 7, 2012

Thank you!

@blakewatters

This comment has been minimized.

Show comment
Hide comment
@blakewatters

blakewatters Dec 7, 2012

Member

Okay, I think I have greatly improved the situation here. Here is the new error output when you fail to map a response due to path pattern mismatches:

(NSError *) $1 = 0x0230da90 Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No response descriptors match the response loaded." UserInfo=0x235c140 {NSErrorFailingURLStringKey=http://restkit.org/api/v1/users, NSLocalizedFailureReason=A 200 response was loaded from the URL 'http://restkit.org/api/v1/users', which failed to match 2 response descriptors:
  <RKResponseDescriptor 0x116dd7d0: baseURL='http://restkit.org/api/v1/', pathPattern='/users'> failed to match: response path 'users' did not match the path pattern '/users'.
  <RKResponseDescriptor 0x116e13e0: baseURL='http://google.com', pathPattern='/users'> failed to match: response URL 'http://restkit.org/api/v1/users' is not relative to the baseURL 'http://google.com'.
, NSLocalizedDescription=No response descriptors match the response loaded., keyPath=null, NSErrorFailingURLKey=http://restkit.org/api/v1/users}
Member

blakewatters commented Dec 7, 2012

Okay, I think I have greatly improved the situation here. Here is the new error output when you fail to map a response due to path pattern mismatches:

(NSError *) $1 = 0x0230da90 Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No response descriptors match the response loaded." UserInfo=0x235c140 {NSErrorFailingURLStringKey=http://restkit.org/api/v1/users, NSLocalizedFailureReason=A 200 response was loaded from the URL 'http://restkit.org/api/v1/users', which failed to match 2 response descriptors:
  <RKResponseDescriptor 0x116dd7d0: baseURL='http://restkit.org/api/v1/', pathPattern='/users'> failed to match: response path 'users' did not match the path pattern '/users'.
  <RKResponseDescriptor 0x116e13e0: baseURL='http://google.com', pathPattern='/users'> failed to match: response URL 'http://restkit.org/api/v1/users' is not relative to the baseURL 'http://google.com'.
, NSLocalizedDescription=No response descriptors match the response loaded., keyPath=null, NSErrorFailingURLKey=http://restkit.org/api/v1/users}
@jgervin

This comment has been minimized.

Show comment
Hide comment
@jgervin

jgervin Dec 7, 2012

Perfect! I bet this will greatly reduce the posts to stackoverflow. Nice!

jgervin commented Dec 7, 2012

Perfect! I bet this will greatly reduce the posts to stackoverflow. Nice!

stojanovicigi added a commit to CenterDevice/RestKit that referenced this issue Dec 12, 2013

@amccloud

This comment has been minimized.

Show comment
Hide comment
@amccloud

amccloud Dec 27, 2013

I'm glad you added the verbose logging but I must ask what does this mean? "response path 'thing_photos' did not match the path pattern 'thing_photos'."

2013-12-26 21:24:00.101 Om[91933:3d07] E restkit.network:RKObjectRequestOperation.m:213 POST 'http://localhost:3000/api/v1/thing_photos' (201 Created / 0 objects) [request=16.2300s mapping=0.0000s total=16.2343s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No response descriptors match the response loaded." UserInfo=0x94a19a0 {NSErrorFailingURLStringKey=http://localhost:3000/api/v1/thing_photos, NSLocalizedFailureReason=A 201 response was loaded from the URL 'http://localhost:3000/api/v1/thing_photos', which failed to match all (3) response descriptors:
...
<RKResponseDescriptor: 0x90c34f0 baseURL=http://localhost:3000/api/v1/ pathPattern=thing_photos statusCodes=200-299> failed to match: response path 'thing_photos' did not match the path pattern 'thing_photos'.
...

amccloud commented Dec 27, 2013

I'm glad you added the verbose logging but I must ask what does this mean? "response path 'thing_photos' did not match the path pattern 'thing_photos'."

2013-12-26 21:24:00.101 Om[91933:3d07] E restkit.network:RKObjectRequestOperation.m:213 POST 'http://localhost:3000/api/v1/thing_photos' (201 Created / 0 objects) [request=16.2300s mapping=0.0000s total=16.2343s]: Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "No response descriptors match the response loaded." UserInfo=0x94a19a0 {NSErrorFailingURLStringKey=http://localhost:3000/api/v1/thing_photos, NSLocalizedFailureReason=A 201 response was loaded from the URL 'http://localhost:3000/api/v1/thing_photos', which failed to match all (3) response descriptors:
...
<RKResponseDescriptor: 0x90c34f0 baseURL=http://localhost:3000/api/v1/ pathPattern=thing_photos statusCodes=200-299> failed to match: response path 'thing_photos' did not match the path pattern 'thing_photos'.
...
@ritec

This comment has been minimized.

Show comment
Hide comment
@ritec

ritec Apr 14, 2014

I know this issue is closed but I must say, @jgervin thank you for posting:
"The issue is first "/" so if changed to "v122/..." or "v1/..." it works."
I had the same issue and spent about 3 hours trying to solve it... good grief.

ritec commented Apr 14, 2014

I know this issue is closed but I must say, @jgervin thank you for posting:
"The issue is first "/" so if changed to "v122/..." or "v1/..." it works."
I had the same issue and spent about 3 hours trying to solve it... good grief.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment