grape-swagger icon indicating copy to clipboard operation
grape-swagger copied to clipboard

Namespaces don't map to Grape namespaces 1:1

Open dblock opened this issue 10 years ago • 5 comments

Grape-swagger extracts the top-level path as a "namespace", however it supports namespace options that are "native" Grape namespaces. If those two match, you're in luck. If they don't, you're not.

I think what should happen is that "real" Grape namespaces become actual namespaces per Swagger, and that they nest as expected.

In the following example the namespace "b" exists, but not namespace "a".

namespaced_api_a = Class.new(Grape::API) do
  namespace :aspace, desc: 'Description for aspace' do
    get '/'
  end
 end

namespaced_api_b = Class.new(Grape::API) do
  namespace :bspace, desc: 'Description for bspace' do
    mount namespaced_api_a
  end
end

Class.new(Grape::API) do
  mount namespaced_api_b
  add_swagger_documentation format: :json
end
{"apiVersion"=>"0.1", "swaggerVersion"=>"1.2", "produces"=>["application/xml", "application/json", "application/vnd.api+json", "text/plain"], "apis"=>[{"path"=>"/bspace.{format}", "description"=>"Description for bspace"}, {"path"=>"/swagger_doc.{format}", "description"=>"Operations about swagger_docs"}], "info"=>{}}

In the following example foos is a namespace that was improvised:

Class.new(Grape::API) do
  get 'foos/:id'
  get 'foos'
  add_swagger_documentation format: :json
end
{"apiVersion"=>"0.1", "swaggerVersion"=>"1.2", "produces"=>["application/xml", "application/json", "application/vnd.api+json", "text/plain"], "apis"=>[{"path"=>"/foos.{format}", "description"=>"Operations about foos"}, {"path"=>"/swagger_doc.{format}", "description"=>"Operations about swagger_docs"}], "info"=>{}}

dblock avatar Jul 28 '14 16:07 dblock

Lets take the example of the Petstore api. Where the store endpoint has an nested endpoint orders. Ill illustrate the layout below for when the petstore api example changes.

store: Operations about store
get      /store/order/{orderId}
delete /store/order/{orderId}
post    /store/order/

Given the example getting orders are operations on a store. Some people might want this behaviour some people do not. I can understand some people wants their orders nested under the store endpoint. Since an order is an operation on a store. But that means if an order has nested products that they have to be nested there as well. Resulting in:

get      /store/order/{orderId}/products/{id}

What we also could do is to specify if the endpoint has to be nested under the parent endpoint or not. So your code would look like this.

order = Class.new(Grape::API) do
  namespace :order, desc: 'Description for order', nested: false do
    get ':id'    
    delete ':id'
    post ''
  end
end

store = Class.new(Grape::API) do
  namespace :store, desc: 'Description for store' do
    route_param :store_id do    
      mount order
    end
    get ''
  end
end

And it should generate:

store: Operations about store
get      /store/    "lists all stores"

order: Operations about order
get      /store/{storeId}/order/{orderId}
delete /store/{storeId}/order/{orderId}
post    /store/{storeId}/order/

instead of :

store: Operations about store
get      /store/    "lists all stores"
get      /store/{storeId}/order/{orderId}
delete /store/{storeId}/order/{orderId}
post    /store/{storeId}/order/

antek-drzewiecki avatar Aug 05 '14 08:08 antek-drzewiecki

Hi all, i liked the idea of @antek-drzewiecki and was looking for a similar solution. Could you please have a look at the pull request #220 to discuss whether the approach seems valid?

croeck avatar Feb 25 '15 19:02 croeck

+1

brodock avatar May 26 '15 18:05 brodock

@croeck I have been looking at this functionality as I want it for my API. And I noticed some odd behavior.

order = Class.new(Grape::API) do
  namespace :store, desc: 'Description for store', swagger: {nested: false} do
          resource :order do
            get ':id'
            delete ':id'
            post ''
          end

          resource :cart do
            get ':id'
            delete ':id'
            post ''
          end
        end
end

When I use the above snippet it seems to ignore the nested false parameter. Is that expected behavior? It works when I comment out the resource part and handle everything through namespaces.

renelux avatar Apr 11 '16 08:04 renelux

@renelux Could you please tell us which version of grape-swagger you are using? I haven't been looking at the project for a couple of months now, so I am not quite familiar with all the recent changes.

At first glance I could not find any tests covering the standalone exposure in the latest release. It seems to me they got lost during the upgrade to the lastest swagger specification. However, as far as i can recall, the original use case was to expose nested routes and therefore one also had to add the option to the nested namespace. In your case this option should be applied to your resources (which is only an alias to namespaces) order and cart rather than the parental namespace. Without this option the store namespace would also appear as standalone group as it already is the root node.

IMHO it should look like this:

order = Class.new(Grape::API) do
  namespace :store, desc: 'Description for store' do
          resource :order, swagger: {nested: false} do
            get ':id'
            delete ':id'
            post ''
          end

          resource :cart, swagger: {nested: false} do
            get ':id'
            delete ':id'
            post ''
          end
        end
end

croeck avatar Apr 13 '16 17:04 croeck