graphiti icon indicating copy to clipboard operation
graphiti copied to clipboard

Graphiti schema does not include endpoints with subdomain constraint

Open masterT opened this issue 3 years ago • 2 comments

I'm trying to configure the Vandal UI for a Rails application, Vandal does not display any available endpoints.

Screen Shot 2022-04-19 at 15 05 37

The schema.json that Vandal fetched does not contain any endpoints.

Screen Shot 2022-04-19 at 15 11 48

I'm using a subdomain constraint on the route of my endpoints.

# config/routes.rb

constraints subdomain: 'api' do
  namespace :v3, defaults: { format: :jsonapi } do
    mount VandalUi::Engine, at: '/vandal'

    resources :accounts, only: %i[index show]
  end
end

I dig a little in the source code, and realise that the Graphiti::Schema.generate uses the Graphiti.config.context_for_endpoint.

https://github.com/graphiti-api/graphiti/blob/aecf5647801e5e45047dc20f7ca7c220c60faad2/lib/graphiti/schema.rb#L59

https://github.com/graphiti-api/graphiti/blob/aecf5647801e5e45047dc20f7ca7c220c60faad2/lib/graphiti/schema.rb#L84-L86

Which is configured by graphiti-rails in lib/graphiti/rails/railtie.rb#L120-L139.

Graphiti.config.context_for_endpoint = ->(path, action) {
  method = :GET
  case action
    when :show then path = "#{path}/1"
    when :create then method = :POST
    when :update
      path = "#{path}/1"
      method = :PUT
    when :destroy
      path = "#{path}/1"
      method = :DELETE
  end


  route = begin
            ::Rails.application.routes.recognize_path(path, method: method)
          rescue
            nil
          end
  "#{route[:controller]}_controller".classify.safe_constantize if route
}

The problem is that in order for recognize_path to return the route for a subdomain constraint, the path should include the host (ex: http://api.example.com/v1) instead of the path (ex: /v1).

This can be easily fixed by changing the Graphiti.config.context_for_endpoint in a Rails initializer (ex: config/initializers/graphiti.rb).

Graphiti.config.context_for_endpoint = ->(path, action) {
  method = :GET
  case action
    when :show then path = "#{path}/1"
    when :create then method = :POST
    when :update
      path = "#{path}/1"
      method = :PUT
    when :destroy
      path = "#{path}/1"
      method = :DELETE
  end

+  # Add host to the path to resolve route with subdomain constraint.
+  path = 'http://api.example.com' + path.to_s

  route = begin
            ::Rails.application.routes.recognize_path(path, method: method)
          rescue
            nil
          end
  "#{route[:controller]}_controller".classify.safe_constantize if route
}

After this change, the schema contains the expected endpoints.

{
  # ...
  :endpoints => {
    :"/v3/accounts" => {
      :actions => {
        :index => {
          :resource => "V3::AccountResource"
        },
         :show => {
          :resource => "V3::AccountResource"
        }
      }
    }
  }
}

I'm wondering if some changes should be made (gem graphiti, gem graphiti-rails) so the this is handled by default. I'm opening an issue to document the issue, but also to discuss potential changes to improve Graphiti. 🙂

I know that a Resource has a base_url (through the module Graphiti::Links).

https://github.com/graphiti-api/graphiti/blob/19c75b58017eaf860a5160ff28c0f7dbd3f47674/lib/graphiti/resource/links.rb#L19

Maybe the resource base_url should prefix the endpoint full_path when calling Graphiti.config.context_for_endpoint from Graphiti::Schema.generate_endpoints. 🤔

masterT avatar Apr 19 '22 19:04 masterT

Hi,

I just faced the same problem ! Thanks for your solution, it avoid me a lot of headaches ! I have one question, do you know why the links have still localhost and doesn't use my subdomain (api) ? image

Also, i'm agree that we maybe should make an PR to handle this case properly, without overriding the context_for_endpoint method.

bilouw avatar Mar 07 '23 17:03 bilouw