Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,16 @@ First, you need to mount the gem in the routes file like this
Rails.application.routes.draw do
mount_graphql_devise_for 'User', at: 'api/v1', authenticable_type: Types::CustomUserType, operations: {
login: Mutations::Login
}
}, skip: [:sign_up]
end
```
If you used DTA's installer you will have to remove the `mount_devise_token_auth_for`
line.

Here are the option for the mount method:

1. `at`: Route where the GraphQL schema will be mounted on the Rails server. In the
example your API will have this two routes `POST /api/v1//graphql_auth` `GET /api/v1//graphql_auth`.
example your API will have this two routes `POST /api/v1/graphql_auth` and `GET /api/v1/graphql_auth`.
If no this option is not specified, the schema will be mounted at `/graphql_auth`.
1. `operations`: Specifying this one is optional. Here you can override default
behavior by specifying your own mutations and queries for every GraphQL operation.
Expand All @@ -62,6 +63,23 @@ our default classes and yielding your customized code after calling `super`, exa
and an `authenticable` type to every query. Gem will try to use `Types::<model>Type` by
default, so in our example you could define `Types::UserType` and every query and mutation
will use it. But, you can override this type with this option like in the example.
1. `skip`: An array of the operations that should not be available in the authentication schema. All these operations are
symbols and should belong to the list of available operations in the gem.
1. `only`: An array of the operations that should be available in the authentication schema. The `skip` and `only` options are
mutually exclusive, an error will be raised if you pass both to the mount method.

#### Available Operations
The following is a list of the symbols you can provide to the `operations`, `skip` and `only` options of the mount method:
```ruby
:login
:logout
:sign_up
:update_password
:send_reset_password
:confirm_account
:check_password_token
```


### Configuring Model
Just like with Devise and DTA, you need to include a module in your authenticable model,
Expand Down Expand Up @@ -145,8 +163,7 @@ templates.
We will continue to improve the gem and add better docs.

1. Add install generator.
1. Support more options on the mount method.
1. Better support for multiple mounted models (it already works by mounting in different routes).
1. Add mount option that will create a separate schema for the mounted resource.
1. Make sure this gem can correctly work alongside DTA and the original Devise gem.
1. Improve DOCS.
1. Add support for unlockable and other Devise modules.
Expand Down
55 changes: 39 additions & 16 deletions lib/graphql_devise/rails/routes.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
module ActionDispatch::Routing
class Mapper
def mount_graphql_devise_for(resource, opts = {})
custom_operations = opts[:operations] || {}
custom_operations = opts[:operations] || {}
skipped_operations = opts.fetch(:skip, [])
only_operations = opts.fetch(:only, [])

if [skipped_operations, only_operations].all?(&:any?)
raise GraphqlDevise::Error, "Can't specify both `skip` and `only` options when mounting the route."
end

default_mutations = {
login: GraphqlDevise::Mutations::Login,
logout: GraphqlDevise::Mutations::Logout,
sign_up: GraphqlDevise::Mutations::SignUp,
update_password: GraphqlDevise::Mutations::UpdatePassword,
send_reset_password: GraphqlDevise::Mutations::SendPasswordReset
}.freeze
default_queries = {
confirm_account: GraphqlDevise::Resolvers::ConfirmAccount,
check_password_token: GraphqlDevise::Resolvers::CheckPasswordToken
}
supported_operations = default_mutations.keys + default_queries.keys

unless skipped_operations.all? { |skipped| supported_operations.include?(skipped) }
raise GraphqlDevise::Error, 'Trying to skip a non supported operation. Check for typos.'
end
unless only_operations.all? { |only| supported_operations.include?(only) }
raise GraphqlDevise::Error, 'One of the `only` operations is not supported. Check for typos.'
end

path = opts.fetch(:at, '/graphql_auth')
mapping_name = resource.underscore.tr('/', '_').to_sym
Expand All @@ -16,15 +42,12 @@ def mount_graphql_devise_for(resource, opts = {})
"Types::#{resource}Type".safe_constantize ||
GraphqlDevise::Types::AuthenticableType

default_mutations = {
login: GraphqlDevise::Mutations::Login,
logout: GraphqlDevise::Mutations::Logout,
sign_up: GraphqlDevise::Mutations::SignUp,
update_password: GraphqlDevise::Mutations::UpdatePassword,
send_reset_password: GraphqlDevise::Mutations::SendPasswordReset
}.freeze

default_mutations.each do |action, mutation|
used_mutations = if only_operations.present?
default_mutations.slice(*only_operations)
else
default_mutations.except(*skipped_operations)
end
used_mutations.each do |action, mutation|
used_mutation = if custom_operations[action].present?
custom_operations[action]
else
Expand All @@ -39,12 +62,12 @@ def mount_graphql_devise_for(resource, opts = {})
GraphqlDevise::Types::MutationType.field("#{mapping_name}_#{action}", mutation: used_mutation)
end

default_queries = {
confirm_account: GraphqlDevise::Resolvers::ConfirmAccount,
check_password_token: GraphqlDevise::Resolvers::CheckPasswordToken
}

default_queries.each do |action, query|
used_queries = if only_operations.present?
default_queries.slice(*only_operations)
else
default_queries.except(*skipped_operations)
end
used_queries.each do |action, query|
used_query = if custom_operations[action].present?
custom_operations[action]
else
Expand Down
9 changes: 9 additions & 0 deletions spec/dummy/app/models/guest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Guest < ApplicationRecord
devise :database_authenticatable,
:registerable,
:recoverable,
:validatable,
:confirmable

include GraphqlDevise::Concerns::Model
end
7 changes: 7 additions & 0 deletions spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@
mount_graphql_devise_for(
'Admin',
authenticable_type: Types::CustomAdminType,
skip: [:sign_up, :check_password_token],
at: '/api/v1/admin/graphql_auth'
)

mount_graphql_devise_for(
'Guest',
only: [:login, :logout],
at: '/api/v1/guest/graphql_auth'
)

post '/api/v1/graphql', to: 'api/v1/graphql#graphql'
end
36 changes: 36 additions & 0 deletions spec/dummy/db/migrate/20191013213045_create_guests.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class CreateGuests < ActiveRecord::Migration[6.0]
def change
create_table :guests do |t|
## Required
t.string :provider, null: false, default: 'email'
t.string :uid, null: false, default: ''

## Database authenticatable
t.string :encrypted_password, null: false, default: ''

## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.boolean :allow_password_change, default: false

## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable

## User Info
t.string :email

## Tokens
t.text :tokens

t.timestamps
end

add_index :guests, :email, unique: true
add_index :guests, [:uid, :provider], unique: true
add_index :guests, :reset_password_token, unique: true
add_index :guests, :confirmation_token, unique: true
end
end
23 changes: 22 additions & 1 deletion spec/dummy/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2019_09_16_012505) do
ActiveRecord::Schema.define(version: 2019_10_13_213045) do

create_table "admins", force: :cascade do |t|
t.string "provider", default: "email", null: false
Expand All @@ -33,6 +33,27 @@
t.index ["uid", "provider"], name: "index_admins_on_uid_and_provider", unique: true
end

create_table "guests", force: :cascade do |t|
t.string "provider", default: "email", null: false
t.string "uid", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.boolean "allow_password_change", default: false
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.string "email"
t.text "tokens"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["confirmation_token"], name: "index_guests_on_confirmation_token", unique: true
t.index ["email"], name: "index_guests_on_email", unique: true
t.index ["reset_password_token"], name: "index_guests_on_reset_password_token", unique: true
t.index ["uid", "provider"], name: "index_guests_on_uid_and_provider", unique: true
end

create_table "users", force: :cascade do |t|
t.string "provider", default: "email", null: false
t.string "uid", default: "", null: false
Expand Down
10 changes: 10 additions & 0 deletions spec/factories/guests.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FactoryBot.define do
factory :guest do
email { Faker::Internet.unique.email }
password { Faker::Internet.password }

trait :confirmed do
confirmed_at { Time.now }
end
end
end
24 changes: 24 additions & 0 deletions spec/requests/mutations/login_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,28 @@
)
end
end

context 'when using the guest model' do
let(:guest) { create(:guest, :confirmed, password: password) }
let(:query) do
<<-GRAPHQL
mutation {
guestLogin(
email: "#{guest.email}",
password: "#{password}"
) {
authenticable { email }
}
}
GRAPHQL
end

before { post_request }

it 'works alongside the user mount point' do
expect(json_response[:data][:guestLogin]).to include(
authenticable: { email: guest.email }
)
end
end
end
Loading