Corvusoft / restbed

Corvusoft's Restbed framework brings asynchronous RESTful functionality to C++14 applications.
http://www.corvusoft.co.uk
Other
1.92k stars 379 forks source link

Unique Paths/Unique Method Handling #216

Closed stkrwork closed 7 years ago

stkrwork commented 7 years ago

Is it possible to change the service to not check for unique paths.

The reason behind this is that I am trying to create a Code Generator for Restbed using Swagger Codegen. My idea would be to instead check if resources with the same path do not listen for the same http methods.

Is that possible, or would there be a clash somewhere else?

stkrwork commented 7 years ago

An example:

Resource 1 has path /test1/ and handler for get. Resource 2 has path /test1/ too, but a handler for post.

Right now, it would block this, because of the same path. With the change it would allow this.

With the change it would block if Resource 2 would also have a get handler.

stkrwork commented 7 years ago

Nevermind, found a workaround

T4lus commented 7 years ago

could you explain your workaround ?

stkrwork commented 7 years ago

It was a workaround in swagger codegen to generate it in the way it currently is.

ben-crowhurst commented 7 years ago

Could someone please flesh out this use case? What is the end user feature that this ticket represents?

T4lus commented 7 years ago

To manage my User I want to use only one endpoint, in RESTFull way

[GET] /users -> List all user [GET] /users/{ID} -> Get user {ID} [POST] /users -> Create new user [PUT] /users/{ID} -> Update user {ID} [DELETE] /users/{ID} -> Delete user {ID}

this design is more easy to understand for my end client

ben-crowhurst commented 7 years ago

Is this what you mean?

auto user = make_shared< Resource >( );
user->set_path( "/users/{ID}" );
user->set_method_handler( ... );

auto users = make_shared< Resource >( );
users->set_path( "/users" );
users->set_method_handler( ... )
T4lus commented 7 years ago

yes but I have the same path for POST and GET :

auto users = make_shared< Resource >( );
users->set_path( "/users" );
users->set_method_handler("GET");

auto users = make_shared< Resource >( );
users->set_path( "/users" );
users->set_method_handler("POST");

and for the other GET, and PUT, DELETE :

auto users = make_shared< Resource >( );
users->set_path( "/users/{ID}" );
users->set_method_handler("GET");

auto users = make_shared< Resource >( );
users->set_path( "/users/{ID}" );
users->set_method_handler("PUT");

auto users = make_shared< Resource >( );
users->set_path( "/users/{ID}" );
users->set_method_handler("DELETE");
ben-crowhurst commented 7 years ago

See how we have implemented it with the RestQ project.

ben-crowhurst commented 7 years ago

The two get paths you have supplied should work correctly.

ben-crowhurst commented 7 years ago
auto users = make_shared< Resource >( );
users->set_path( "/users/{ID: .*}" );
users->set_method_handler("GET");
ben-crowhurst commented 7 years ago

Is your code available for me to review?

T4lus commented 7 years ago

My path are creating a the start, susing a config file :

    std::cout << "[Ressources] Loading : " << std::endl;
    std::vector< std::shared_ptr<Resource> > ressourcesList;
    for (auto route : config["routes"]) {
        std::cout << "[Ressources] " << route["name"] << std::endl;
        auto ressource = std::make_shared<Resource>();
        ressource->set_paths(route["rules"]);
        ressource->set_method_handler(route["method"], handlerList[route["handler"]]);
        ressourcesList.push_back(ressource);
    }
    std::cout << "[Ressources][OK]" << std::endl;

    std::cout << "[Service] Publishing Ressources ..." << std::endl;
    Service service;
    for (auto ressource : ressourcesList) {
        service.publish(ressource);
    }
    std::cout << "[Service][OK]" << std::endl;

here is my config file :

    "routes" : [
        {
            "name" : "User GET",
            "rules" : [
                "/api/users",
                "/api/users/{id: .*}"
            ],
            "method" : "GET",
            "handler" : "handler_get_user"
        },
        {
            "name" : "User POST",
            "rules" : [
                "/api/users"
            ],
            "method" : "POST",
            "handler" : "handler_post_user"
        },
        {
            "name" : "User PUT",
            "rules" : [
                "/api/users/{id: .*}"
            ],
            "method" : "PUT",
            "handler" : "handler_put_user"
        },
        {
            "name" : "User DELETE",
            "rules" : [
                "/api/users/{id: .*}"
            ],
            "method" : "DELETE",
            "handler" : "handler_delete_user"
        }
    ]

And I get this :

terminate called after throwing an instance of 'std::invalid_argument'
  what():  Resource would pollute namespace. Please ensure all published resources have unique paths.
Aborted (core dumped)
T4lus commented 7 years ago

I made some change and It working :

    std::cout << "[Ressources] Loading : " << std::endl;
    std::vector< std::shared_ptr<Resource> > ressourcesList;
    for (auto route : config["routes"]) {
        std::cout << "[Ressources] " << route["name"].get<std::string>().c_str() << " ";
        auto ressource = std::make_shared<Resource>();
        ressource->set_paths(route["paths"]);
        for (auto handler : route["handlers"]) {
            std::cout << "[" << handler["method"].get<std::string>().c_str() << "]";
            ressource->set_method_handler(handler["method"], handlerList[handler["handler"]]);
        }
        std::cout << std::endl;
        ressourcesList.push_back(ressource);
    }
    std::cout << "[Ressources][OK]" << std::endl;

and for the config :

        {
            "name" : "Users",
            "paths" : [
                "/api/users",
                "/api/users/{id: .*}"
            ],
            "handlers" : [
                { "method" : "GET", "handler" : "handler_get_user"},
                { "method" : "POST", "handler" : "handler_post_user"},
                { "method" : "PUT", "handler" : "handler_put_user"},
                { "method" : "DELETE", "handler" : "handler_delete_user"}
            ]
        }