Skip to content
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

Setting the action name for chaining #288

Open
mardy opened this issue Jan 5, 2021 · 6 comments
Open

Setting the action name for chaining #288

mardy opened this issue Jan 5, 2021 · 6 comments

Comments

@mardy
Copy link
Contributor

mardy commented Jan 5, 2021

I'm implementing a REST API (code is here, and I successfully got these endpoints exposed:

.-------------------+---------------------.
| Path              | Private             |
.-------------------+---------------------.
| /...              | /defaultPage        |
| /                 | /index              |
| /api/v1/login     | /api/v1/login/index |
| /api/v1/users/... | /api/v1/users/user  |
| /api/v1/users     | /api/v1/users/index |
.-------------------+---------------------.

Now I want to add a new method, say on /api/v1/users/{id}/changePassword, but I haven't been able to figure out how to achieve that. I've been trying to use the :Chained attribute, but I cannot make the action appear at the desired path. I don't fully understand what the value of the :Chained attribute is; I'd like to be able to set an action ID on the base action, and then chain to that. For example,

    C_ATTR(user, :Path :CaptureArgs(1) :ActionClass(REST) :ActionId("user"))
    // this is the new attribute I wish existed ----------^^^^^^^^^^^^^^^^^
    void user(Context *c, const QString &userId);

    C_ATTR(changePassword, :Chained("user") :PathPart("changePassword") :CaptureArgs(1) :ActionClass(REST))
    void ChangePassword(Context *c);

What is the way to achieve what I need? Does it involve playing with the Dispatcher classes, or is there an easier (and better) way?

@dantti
Copy link
Member

dantti commented Jan 6, 2021

You need the Chained dispatcher but it doesn't work the way you are implementing.
For /api/users/id/change_password it would be like:

C_ATTR(users_id, :Chained('/') :PathPart('api/v1/users') :AutoArgs :CaptureArgs(1))
void users_id(Context *c);

C_ATTR(users_change_password, :Chained('users_id') :AutoArgs)
void users_change_password(Context *c);

@mardy
Copy link
Contributor Author

mardy commented Jan 6, 2021

I see, but the issue is that I do not want to hardcode the namespace api/v1/users in the controller, because this same controller might be used in different versions of the API (by subclassing).

I'm now experimenting with a custom DispatcherType; I'll post a link as soon as I get something working, and then we can decide whether it's useful to Cutelyst (maybe its functionality can be incorporated into the Chained dispatcher), or not.

@dantti
Copy link
Member

dantti commented Jan 6, 2021

hmm not sure if Perl Catalyst supports this, but perhaps the empty Chained instead of / could chain to /_name_space

@mardy
Copy link
Contributor Author

mardy commented Jan 6, 2021

hmm not sure if Perl Catalyst supports this, but perhaps the empty Chained instead of / could chain to /_name_space

That would be perfect! Let me try and see if I manage to get it working that way, and if it all goes well I'll prepare a MR.

mardy added a commit to mardy/cutelyst that referenced this issue Jan 6, 2021
@mardy
Copy link
Contributor Author

mardy commented Jan 6, 2021

@dantti I would appreciate if you could have a look at the linked branch, because I'm kind of stuck :-)

My controller is now:

    C_ATTR(user, :Chained("") :AutoArgs :CaptureArgs(1) :ActionClass(REST))
    void user(Context *c, const QString &userId);

    C_ATTR(updatePin, :Chained("user") :AutoArgs :ActionClass(REST))
    void updatePin(Context *c);

where the name of the controller (sub)class is ApiV1Users. When I make a request to http://locav1/users/2/updatePin I see that the recurseMatch() is failing in this point, because the tryPart is user and the possiblePart is api. Since user is not even within the path parts (users is, but not user) I wonder if I might have an error in my controller metadata, actually. Can you spot any? If not, any idea on how to get past this check?

@mardy
Copy link
Contributor Author

mardy commented Jan 7, 2021

Meanwhile, I've implemented a custom dispatcher in my own project, which can be found here: https://gitlab.com/studentario/studentario-server/-/commit/33947b494b372e0a7b21288c41021b0f46acf1b4

It reacts on two attributes:

  1. :Object(<define the REST object name here>)
  2. :OnObject(<specify the object name that this method is a member of>)

For example:

    /* This serves the URL api/v1/users/<id>, which we declare as the "user" object */
    C_ATTR(user, :Path :CaptureArgs(1) :Object("user") :ActionClass(REST))
    void user(Context *c, const QString &userId);

    /* This serves the URL api/v1/users/<id>/updatePin, which is a method the "user" object */
    C_ATTR(updatePin, :OnObject("user") :ActionClass(REST))
    void updatePin(Context *c);

    C_ATTR(updatePassword, :OnObject("user") :ActionClass(REST))
    void updatePassword(Context *c);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants