Recursive requests from `redirect_handle_rest_rel_request()` are broken for self-referential relationships
Closed this issue · 1 comments
Given a self-referential relationship like this for MyApp::Schema::Result::Customer
:
__PACKAGE__->has_many(
"child_customers",
"MyApp::Schema::Result::Customer",
{ 'foreign.parent_customer_id' => 'self.id' },
{ cascade_copy => 0, cascade_delete => 0 },
);
With this self-referential relationship, a URI path like /main/db/db_customer/item/1234/rel/child_customers
is broken. Instead of returning the matching customer records, this will return all customers.
After extensive tracing and debugging of the core RapidApp code, and discussing this issue with @vanstyn, I believe I have a fix for the root cause of the bug. The chain of causation (in reverse) looks like this:
- The relationship URL displays all customers instead of the correct list of specific customers.
- This is because the Ajax request to
/main/db/db_customer/store/read
sends POST parameters which are missing thers_method
andrs_path
parameters, which are required to narrow the result set to the correct results. - This is because the Ajax request to the relationship URI returns Javascript code without base parameters containing the
rs_method
andrs_path
parameters. - This is because
redirect_handle_rest_rel_request()
uses the ExtJS configuration data in theRapidApp::Module::DatStor
store object to construct the base parameters, and thers_method
andrs_path
are also missing from the store object's ExtJS configuration. - This is because
store_init_onrequest()
sets the ExtJS configuration, but it doesn't have thers_method
andrs_path
values available when it runs on the store object. - This is because
store_init_onrequest()
runs on the store object beforeredirect_handle_rest_rel_request()
runs, determining the correctrs_method
andrs_path
values. - This is because the
store_init_onrequest()
method runs as an ONREQUEST handler, which only run once per request. In this case, the ONREQUEST handlers for the store object have already been triggered earlier in the relationship URI request cycle by the timeredirect_handle_rest_rel_request()
runs. - This only seems to be an issue for self-referential relationships. If the relationship connects to different table,
redirect_handle_rest_rel_request()
ends up running before the ONREQUEST handlers for the other table's store object, which is why the relationship URLs normally work as expected.
The recursive call from redirect_handle_rest_rel_request()
to $c->root_module_controller->approot($c,$url)
is meant to run as an independent request with fresh object state, so the recursive call should not be skipping ONREQUEST handlers which previously ran for the original request. While it took many hours to trace/debug the code and understand the chain of causation above, the actual code fix is very simple. The value of $c->request_id
must be changed to a unique value during the recursive approot()
call, which can be accomplished with a single-line code change for each of the two recursive approot()
calls. This will cause RapidApp to actually treat the recursive request as an independent request and run all ONREQUEST handlers as it should, regardless of what ONREQUEST handlers may have already run during the original request.
With this fix, the self-referential relationship URL works correctly, returning the intended subset of customers in the results.
This is the same technique used by RapidApp::RapidApp::cleanupAfterRequest()
for any post-processing tasks that it runs. My patch adds 0.01 to the $c->request_id
value to prevent overlap with the values generated for post-processing tasks.