preaction / Yancy

The Best Web Framework Deserves the Best Content Management System
http://preaction.me/yancy/
Other
54 stars 21 forks source link

dynamic backend for multi-tenant application #89

Closed rmallah closed 4 years ago

rmallah commented 4 years ago

Hi ,

We have a use case where DBIx::Class backend DSN needs to be set at request handling time in a mojo app depending on the tenant selection (based on Host header currently).

We are registering the Yancy plugin in Mojo app during the application startup but the backend is not expected to be valid at that point as its determined during HTTP request handling phase.

Kindly guide how can we use Yancy based on its current features.

related topic #33

regds Rajesh Mallah.

rmallah commented 4 years ago

I think there is solution here: https://metacpan.org/pod/Mojolicious::Plugin::Yancy#yancy.backend

Get the Yancy backend object. By default, gets the backend configured while loading the Yancy plugin. Requests can override the backend by setting the backend stash value. See Yancy::Backend for the methods you can call on a backend object and their purpose.

preaction commented 4 years ago

Yes, what you want should be possible through that feature. That's a more-recent change that came out of a discussion at 2019 TPC, so let me know if there is anything missing. I think controllers and the editor both can have custom backend, but I don't remember if I added that to auth and form plugins yet.

In any case, the documentation is accurate (what I want Yancy to do), it just may not be entirely bug-free.

But an under route handler should be able to do the right thing:

under `/user/:username` => sub( $c ) {
    state %user_backend;
    my $backend = $user_backend{ $c->stash( 'username' ) } ||= $c->connect_to_user_database();
    $c->stash( backend => $backend );
    return 1;
};
rmallah commented 4 years ago

Hi I could accomplish what i had wanted i had to set the stash variable in a around_action hook , not sure if it was the right place.

use Yancy::Util qw( load_backend );

$self->hook(around_action => sub {
    my ($next, $c, $action, $last) = @_;
       .......
       .......
       my $be = load_backend( $yancy_be_config ,  $collections  );
    $c->stash ('backend' => $be );
    return $next->();
}); 
rmallah commented 4 years ago

Sorry @preaction i read your comment after posting.

I use '/yancy/api/*' endpoints from my front-end ajaxy UIs . I love this plugin as it saves a lot of time.

I was at loss where to set the stash i use 'route' config option in application setup routine.

regds Rajesh.

preaction commented 4 years ago

For that, you'd want to pass in the route object returned from the under method. Something like:

my $user_route = $app->routes->under( '/user/:username', sub( $c ) {
    state %user_backend;
    my $backend = $user_backend{ $c->stash( 'username' ) } ||= $c->connect_to_user_database();
    $c->stash( backend => $backend );
    return 1;
} );
$app->plugin( Yancy => {
    # <other config here>
    editor => {
        route => $user_route,
    },
} );

Another way to customize how the editor (and therefore its API) handles requests is to set the default_controller config. This is what I'm trying to use to make an additional user dashboard in this blog site for users to edit their own content (distinct from the site admin dashboard which admins can use to edit any content): https://github.com/preaction/Yancy-App-Blog/blob/9334d2dd976052126d4abd7c912edf3844f28d22/myapp.pl#L52-L64

rmallah commented 4 years ago

I did very similar to this except that i use named routes and use find to retrieve them.

I can see that the actions are executing in the route that is invoked by yancy . In such an action i set 'backend' stash variable , however i do not find the stash variable inside Mojolicious/Plugin/Yancy.pm . Hence i had to revert to hook method which i did not want.

not sure if the behavior will be replicable elsewhere.

Regds Rajesh Mallah.

preaction commented 4 years ago

Okay, I think I understand: You need to set the backend for every request to the website, not just requests to the Yancy admin app. Is that right?

In that case, you'd need to either set up that under() handler for all the routes in the site, or do as you did and use the around_action hook (in this case, they end up doing the same thing: setting the backend for every request).

preaction commented 4 years ago

If there are any more issues / questions, feel free to re-open this.

rmallah commented 1 year ago

With below commit it looks like setting backend in stash does not have any affect , hence old code that uses $c->stash ('backend' , ....) may break.

https://github.com/preaction/Yancy/commit/7c00a40e0270e1ee44a8c5bd1ffe161aa2626164