ruby-on-railsdevisedevise-confirmable

devise_scope in namespaces, rails api_only mode


I'm trying to remove the obsolete routes of devise, in my api_only rails setup. However i'm in a fuss about how to define them properly with devise_scope. I have the following routes.rb:

# config/routes.rb

Rails.application.routes.draw do    
  namespace :api do
    namespace :users do
      devise_scope :user do
        resource :confirmations, only: %i[create show], format: false
      end
    end
  end
end

Which refers to the confirmations_controller that contains custom json renders instead of the typical respond_with:

# app/controllers/api/users/confirmations_controller.rb

module Api
  module Users
    class ConfirmationsController < Devise::ConfirmationsController
      # POST /resource/confirmation
      def create
        self.resource = resource_class.send_confirmation_instructions(resource_params)
        yield resource if block_given?

        if successfully_sent?(resource)
          # respond_with({}, location: after_resending_confirmation_instructions_path_for(resource_name))
          render json: { status: 201 }, status: :created
        else
          # respond_with(resource)
          render json: { status: 422, errors: resource.errors.keys },
                 status: :unprocessable_entity
        end
      end

      # GET /resource/confirmation?confirmation_token=abcdef
      def show
        self.resource = resource_class.confirm_by_token(params[:confirmation_token])
        yield resource if block_given?

        if resource.errors.empty?
          set_flash_message!(:notice, :confirmed)
          # respond_with_navigational(resource) { redirect_to after_confirmation_path_for(resource_name, resource) }
          render json: { status: 200 }, status: :ok
        else
          # respond_with_navigational(resource.errors, status: :unprocessable_entity) { render :new }
          render json: { status: 422, errors: resource.errors.keys },
                 status: :unprocessable_entity
        end
      end
    end
  end
end

As can be seen in the routes.rb I only need the create and show endpoints of confirmations. However the current definition results in the following error when running rspec:

 Failure/Error: get api_users_confirmations_path, params: { confirmation_token: 'incorrect_token'  }

 AbstractController::ActionNotFound:
   Could not find devise mapping for path "/api/users/confirmations?confirmation_token=incorrect_token".
   This may happen for two reasons:

   1) You forgot to wrap your route inside the scope block. For example:

     devise_scope :user do
       get "/some/route" => "some_devise_controller"
     end

   2) You are testing a Devise controller bypassing the router.
      If so, you can explicitly tell Devise which mapping to use:

      @request.env["devise.mapping"] = Devise.mappings[:user]

Which tends mostly to the missing devise mapping, considering that the devise_scope is defined properly. However i'm not sure how to solve this properly without having to include the bindings in every devise controller. Is this doable from the routes?


Solution

  • I have never tried to use resources inside of devise_scope.

    This is how I have defined it.

    devise_scope :user do
      delete 'logout', to: 'devise/sessions#destroy'
    end
    

    This is how I have defined in one of my application

        devise_for :users, path: 'api/v1/accounts', controllers: {
            :registrations      => 'api/v1/accounts/registrations',
            :sessions           => 'api/v1/accounts/sessions',
            :passwords          => 'api/v1/accounts/passwords'
          }
    
    devise_scope :user do
        get  '/sessions/new' => "sessions#new", :as => :new_sessions
        get  '/sessions/forgot_password' => "sessions#forgot_password", :as => :forgot_password
        post '/validate_referral_code' => 'validates#validate_referral_code', as: :validate_referral_code
        post '/validate_employment_code' => 'validates#validate_employment_code', as: :validate_employment_code
        post '/get_weather' => 'temperature#weather', as: :weather
        get '/fetch' => 'zip_codes#fetch', as: :fetch_zip_code
        post '/request_demo' => 'demos#create', as: :create
      end
    
    namespace :api do
        namespace :v1 do
          scope :accounts do
            resources :third_party_logins, only: [] do
              collection do
                get :action_name
              end
            end
          end
        end
    end