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
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
--format documentation
--color
--order random
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

12 changes: 9 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,19 @@ jobs:
rvm: 2.7.1
- gemfile: gemfiles/rails6.0_graphql_edge.gemfile
rvm: 2.6.6
env: SKIP_COVERALLS=true
env:
- SKIP_COVERALLS=true
- EAGER_LOAD=true
- gemfile: gemfiles/rails6.0_graphql_edge.gemfile
rvm: 2.7.1
env: SKIP_COVERALLS=true
env:
- SKIP_COVERALLS=true
- EAGER_LOAD=true
- gemfile: gemfiles/rails_edge_graphql_edge.gemfile
rvm: 2.7.1
env: SKIP_COVERALLS=true
env:
- SKIP_COVERALLS=true
- EAGER_LOAD=true
exclude:
- gemfile: gemfiles/rails4.2_graphql1.8.gemfile
rvm: 2.7.1
Expand Down
16 changes: 4 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,12 @@ class User < ApplicationRecord
:confirmable

# including after calling the `devise` method is important.
# include DeviseTokenAuth::Concerns::User # is also valid (generator includes this one).
include GraphqlDevise::Concerns::Model
end
```

The install generator can do this for you if you specify the `user_class` option.
See [Installation](#installation) for details.
The generator will include a different module in your model, `DeviseTokenAuth::Concerns::User` which is also correct,
we just made an alias on our namespace for consistency and possible extension.
Generators have to be updated to generate our module.

### Customizing Email Templates
The approach of this gem is a bit different from DeviseTokenAuth. We have placed our templates in `app/views/graphql_devise/mailer`,
Expand All @@ -226,7 +222,6 @@ In our example our model is `User`, so it would look like this:
# app/controllers/my_controller.rb

class MyController < ApplicationController
# include DeviseTokenAuth::Concerns::SetUserByToken # is also valid (generator includes this one).
include GraphqlDevise::Concerns::SetUserByToken

before_action :authenticate_user!
Expand All @@ -239,9 +234,6 @@ end

The install generator can do this for you because it executes DTA installer.
See [Installation](#Installation) for details.
The generator will include a different module in your model, `DeviseTokenAuth::Concerns::SetUserByToken` which is also correct,
we just made an alias on our namespace for consistency and possible extension.
Generators have to be updated to generate our module.

### Making Requests
Here is a list of the available mutations and queries assuming your mounted model is `User`.
Expand Down Expand Up @@ -290,10 +282,10 @@ as comments. You can also use
**[DTA's docs](https://devise-token-auth.gitbook.io/devise-token-auth/config/initialization)** as a reference.
In this section the most important configurations will be highlighted.

- **change_headers_on_each_request:** This configurations defaults to `true`. This means that tokens will change on
each request you make, and the new values will be returned in the headers. So your client needs to handle this.
Setting this to `false` will allow you to store the credentials for as long as the token life_span permits. And
you can send the same credentials in each request.
- **change_headers_on_each_request:** This configurations defaults to `false`. This will allow you to store the
credentials for as long as the token life_span permits. And you can send the same credentials in each request.
Setting this to `true` means that tokens will change on each request you make, and the new values will be returned
in the headers. So your client needs to handle this.
- **batch_request_buffer_throttle:** When change_headers_on_each_request is set to true, you might still want your
credentials to be valid more than once as you might send parallel request. The duration you set here will
determine how long the same credentials work after the first request is received.
Expand Down
76 changes: 43 additions & 33 deletions lib/generators/graphql_devise/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,57 +10,67 @@ def execute_devise_installer
end

def execute_dta_installer
# Necessary in case of a re-run of the generator, for DTA to detect concerns already included
if File.exist?(File.expand_path("app/models/#{user_class.underscore}.rb", destination_root))
gsub_file(
"app/models/#{user_class.underscore}.rb",
'GraphqlDevise::Concerns::Model',
'DeviseTokenAuth::Concerns::User'
)
end
gsub_file(
'app/controllers/application_controller.rb',
'GraphqlDevise::Concerns::SetUserByToken',
'DeviseTokenAuth::Concerns::SetUserByToken'
)

generate 'devise_token_auth:install', "#{user_class} #{mount_path}"
end

def mount_resource_route
routes_file = 'config/routes.rb'
routes_path = File.join(destination_root, routes_file)
gem_helper = 'mount_graphql_devise_for'
gem_route = "#{gem_helper} '#{user_class}', at: '#{mount_path}'"
gem_route = "mount_graphql_devise_for '#{user_class}', at: '#{mount_path}'"
dta_route = "mount_devise_token_auth_for '#{user_class}', at: '#{mount_path}'"
file_start = 'Rails.application.routes.draw do'

if File.exist?(routes_path)
current_route = parse_file_for_line(routes_path, gem_route)

if current_route.present?
say_status('skipped', "Routes already exist for #{user_class} at #{mount_path}")
else
current_dta_route = parse_file_for_line(routes_path, dta_route)
if file_contains_str?(routes_file, gem_route)
gsub_file(routes_file, /^\s+#{Regexp.escape(dta_route + "\n")}/i, '')

if current_dta_route.present?
replace_line(routes_path, dta_route, gem_route)
else
insert_text_after_line(routes_path, file_start, gem_route)
end
end
say_status('skipped', "Routes already exist for #{user_class} at #{mount_path}")
else
say_status('skipped', "#{routes_file} not found. Add \"#{gem_route}\" to your routes file.")
gsub_file(routes_file, /#{Regexp.escape(dta_route)}/i, gem_route)
end
end

private
def replace_model_concern
gsub_file(
"app/models/#{user_class.underscore}.rb",
/^\s+include DeviseTokenAuth::Concerns::User/,
' include GraphqlDevise::Concerns::Model'
)
end

def insert_text_after_line(filename, line, str)
gsub_file(filename, /(#{Regexp.escape(line)})/mi) do |match|
"#{match}\n #{str}"
end
def replace_controller_concern
gsub_file(
'app/controllers/application_controller.rb',
/^\s+include DeviseTokenAuth::Concerns::SetUserByToken/,
' include GraphqlDevise::Concerns::SetUserByToken'
)
end

def replace_line(filename, old_line, new_line)
gsub_file(filename, /(#{Regexp.escape(old_line)})/mi, " #{new_line}")
def set_change_headers_on_each_request_false
gsub_file(
'config/initializers/devise_token_auth.rb',
'# config.change_headers_on_each_request = true',
'config.change_headers_on_each_request = false'
)
end

def parse_file_for_line(filename, str)
match = false
private

def file_contains_str?(filename, regex_str)
path = File.join(destination_root, filename)

File.open(filename) do |f|
f.each_line do |line|
match = line if line =~ /(#{Regexp.escape(str)})/mi
end
end
match
File.read(path) =~ /(#{Regexp.escape(regex_str)})/i
end
end
end
7 changes: 0 additions & 7 deletions spec/fixtures/rails

This file was deleted.

75 changes: 40 additions & 35 deletions spec/generators/graphql_devise/install_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,61 @@
require 'generators/graphql_devise/install_generator'

RSpec.describe GraphqlDevise::InstallGenerator, type: :generator do
destination File.expand_path('../../../tmp/dummy', __dir__)
destination File.expand_path('../../../../gqld_dummy', __dir__)

let(:routes_path) { "#{destination_root}/config/routes.rb" }
let(:routes_content) { File.read(routes_path) }
let(:dta_route) { 'mount_devise_token_auth_for' }

after(:all) { FileUtils.rm_rf(destination_root) }

before do
prepare_destination
copy_rails_bin
create_rails_project
run_generator(args)
end

let(:routes_path) { "#{destination_root}/config/routes.rb" }
let(:routes_content) { File.read(routes_path) }
let(:dta_route) { "mount_devise_token_auth_for 'User', at: 'auth'" }

context 'when the file exists' do
before do
create_file_with_content(
routes_path,
"Rails.application.routes.draw do\n#{dta_route}\nend"
)
end
context 'when passing no params to the generator' do
let(:args) { [] }

context 'when passing no params to the generator' do
before { run_generator }
it 'creates and updated required files' do
assert_file 'config/routes.rb', /^\s{2}mount_graphql_devise_for 'User', at: 'auth'/
expect(routes_content).not_to match(dta_route)

it 'replaces dta route using the default values for class and path' do
generator_added_route = / mount_graphql_devise_for 'User', at: 'auth'/
expect(routes_content).to match(generator_added_route)
expect(routes_content).not_to match(dta_route)
end
end
assert_file 'config/initializers/devise.rb'
assert_file 'config/initializers/devise_token_auth.rb', /^\s{2}#{Regexp.escape('config.change_headers_on_each_request = false')}/
assert_file 'config/locales/devise.en.yml'

assert_migration 'db/migrate/devise_token_auth_create_users.rb'

context 'when passing custom params to the generator' do
before { run_generator %w[Admin api] }
assert_file 'app/models/user.rb', /^\s{2}devise :.+include GraphqlDevise::Concerns::Model/m

it 'add the routes using the provided values for class and path and keeps dta route' do
generator_added_route = / mount_graphql_devise_for 'Admin', at: 'api'/
expect(routes_content).to match(generator_added_route)
expect(routes_content).to match(dta_route)
end
assert_file 'app/controllers/application_controller.rb', /^\s{2}include GraphqlDevise::Concerns::SetUserByToken/
end
end

context 'when file does *NOT* exist' do
before { run_generator }
context 'when passing custom params to the generator' do
let(:args) { %w[Admin api] }

it 'creates and updated required files' do
assert_file 'config/routes.rb', /^\s{2}mount_graphql_devise_for 'Admin', at: 'api'/
expect(routes_content).not_to match(dta_route)

assert_file 'config/initializers/devise.rb'
assert_file 'config/initializers/devise_token_auth.rb', /^\s{2}#{Regexp.escape('config.change_headers_on_each_request = false')}/
assert_file 'config/locales/devise.en.yml'

it 'does *NOT* create the file and throw no exception' do
expect(File).not_to exist(routes_path)
assert_migration 'db/migrate/devise_token_auth_create_admins.rb'

assert_file 'app/models/admin.rb', /^\s{2}devise :.+include GraphqlDevise::Concerns::Model/m

assert_file 'app/controllers/application_controller.rb', /^\s{2}include GraphqlDevise::Concerns::SetUserByToken/
end
end

def copy_rails_bin
FileUtils.mkdir_p(File.join(destination_root, 'bin'))
FileUtils.copy_file('spec/fixtures/rails', File.join(destination_root, 'bin/rails'))
def create_rails_project
FileUtils.cd(File.join(destination_root, '..')) do
`rails new gqld_dummy -S -C --skip-action-mailbox --skip-action-text -T --skip-spring --skip-bundle --skip-keeps -G --skip-active-storage -J --skip-listen --skip-bootsnap`
end
end
end