graphql_devise 0.11.2 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/.travis.yml +9 -3
- data/CHANGELOG.md +50 -1
- data/README.md +185 -32
- data/app/controllers/graphql_devise/application_controller.rb +4 -1
- data/app/controllers/graphql_devise/concerns/set_user_by_token.rb +25 -0
- data/app/controllers/graphql_devise/graphql_controller.rb +2 -0
- data/app/helpers/graphql_devise/mailer_helper.rb +2 -2
- data/app/views/graphql_devise/mailer/confirmation_instructions.html.erb +1 -1
- data/app/views/graphql_devise/mailer/reset_password_instructions.html.erb +1 -1
- data/config/locales/en.yml +1 -0
- data/config/routes.rb +2 -0
- data/graphql_devise.gemspec +6 -4
- data/lib/generators/graphql_devise/install_generator.rb +63 -30
- data/lib/graphql_devise.rb +24 -10
- data/lib/graphql_devise/default_operations/mutations.rb +6 -6
- data/lib/graphql_devise/default_operations/resolvers.rb +2 -2
- data/lib/graphql_devise/errors/authentication_error.rb +7 -0
- data/lib/graphql_devise/{detailed_user_error.rb → errors/detailed_user_error.rb} +1 -1
- data/lib/graphql_devise/errors/error_codes.rb +6 -0
- data/lib/graphql_devise/errors/execution_error.rb +4 -0
- data/lib/graphql_devise/{user_error.rb → errors/user_error.rb} +1 -1
- data/lib/graphql_devise/mount_method/operation_preparer.rb +2 -2
- data/lib/graphql_devise/mount_method/operation_preparers/default_operation_preparer.rb +6 -2
- data/lib/graphql_devise/mount_method/operation_preparers/gql_name_setter.rb +1 -1
- data/lib/graphql_devise/mount_method/operation_preparers/mutation_field_setter.rb +3 -2
- data/lib/graphql_devise/mount_method/operation_preparers/resolver_type_setter.rb +1 -1
- data/lib/graphql_devise/mount_method/operation_preparers/resource_name_setter.rb +2 -2
- data/lib/graphql_devise/mutations/resend_confirmation.rb +3 -5
- data/lib/graphql_devise/mutations/send_password_reset.rb +5 -2
- data/lib/graphql_devise/mutations/sign_up.rb +3 -6
- data/lib/graphql_devise/rails/routes.rb +5 -72
- data/lib/graphql_devise/resource_loader.rb +87 -0
- data/lib/graphql_devise/schema_plugin.rb +106 -0
- data/lib/graphql_devise/version.rb +1 -1
- data/spec/dummy/app/controllers/api/v1/graphql_controller.rb +41 -3
- data/spec/dummy/app/controllers/application_controller.rb +1 -0
- data/spec/dummy/app/graphql/dummy_schema.rb +18 -0
- data/spec/dummy/app/graphql/interpreter_schema.rb +9 -0
- data/spec/dummy/app/graphql/types/mutation_type.rb +1 -1
- data/spec/dummy/app/graphql/types/query_type.rb +10 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/generators/graphql_devise/install_generator_spec.rb +62 -30
- data/spec/rails_helper.rb +4 -1
- data/spec/requests/graphql_controller_spec.rb +80 -0
- data/spec/requests/mutations/resend_confirmation_spec.rb +44 -29
- data/spec/requests/mutations/send_password_reset_spec.rb +40 -12
- data/spec/requests/queries/confirm_account_spec.rb +7 -1
- data/spec/requests/user_controller_spec.rb +189 -24
- data/spec/services/mount_method/operation_preparer_spec.rb +8 -3
- data/spec/services/mount_method/operation_preparers/custom_operation_preparer_spec.rb +1 -1
- data/spec/services/mount_method/operation_preparers/default_operation_preparer_spec.rb +15 -8
- data/spec/services/mount_method/operation_preparers/mutation_field_setter_spec.rb +18 -4
- data/spec/services/mount_method/operation_preparers/resource_name_setter_spec.rb +1 -1
- data/spec/services/resource_loader_spec.rb +82 -0
- data/spec/services/schema_plugin_spec.rb +26 -0
- data/spec/spec_helper.rb +1 -1
- metadata +107 -89
- data/lib/graphql_devise/error_codes.rb +0 -5
- data/spec/support/generators/file_helpers.rb +0 -12
@@ -1,5 +1,30 @@
|
|
1
1
|
module GraphqlDevise
|
2
2
|
module Concerns
|
3
3
|
SetUserByToken = DeviseTokenAuth::Concerns::SetUserByToken
|
4
|
+
|
5
|
+
SetUserByToken.module_eval do
|
6
|
+
attr_accessor :client_id, :token, :resource
|
7
|
+
|
8
|
+
def set_resource_by_token(resource)
|
9
|
+
set_user_by_token(resource)
|
10
|
+
end
|
11
|
+
|
12
|
+
def graphql_context(resource_name)
|
13
|
+
{
|
14
|
+
resource_name: resource_name,
|
15
|
+
controller: self
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_redirect_headers(access_token, client, redirect_header_options = {})
|
20
|
+
{
|
21
|
+
DeviseTokenAuth.headers_names[:"access-token"] => access_token,
|
22
|
+
DeviseTokenAuth.headers_names[:client] => client,
|
23
|
+
:config => params[:config],
|
24
|
+
:client_id => client,
|
25
|
+
:token => access_token
|
26
|
+
}.merge(redirect_header_options)
|
27
|
+
end
|
28
|
+
end
|
4
29
|
end
|
5
30
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module GraphqlDevise
|
2
2
|
module MailerHelper
|
3
3
|
def confirmation_query(resource_name:, token:, redirect_url:)
|
4
|
-
name = "#{resource_name.camelize(:lower)}ConfirmAccount"
|
4
|
+
name = "#{resource_name.underscore.tr('/', '_').camelize(:lower)}ConfirmAccount"
|
5
5
|
raw = <<-GRAPHQL
|
6
6
|
query($token:String!,$redirectUrl:String!){
|
7
7
|
#{name}(confirmationToken:$token,redirectUrl:$redirectUrl){
|
@@ -17,7 +17,7 @@ module GraphqlDevise
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def password_reset_query(token:, redirect_url:, resource_name:)
|
20
|
-
name = "#{resource_name.camelize(:lower)}CheckPasswordToken"
|
20
|
+
name = "#{resource_name.underscore.tr('/', '_').camelize(:lower)}CheckPasswordToken"
|
21
21
|
raw = <<-GRAPHQL
|
22
22
|
query($token:String!,$redirectUrl:String!){
|
23
23
|
#{name}(resetPasswordToken:$token,redirectUrl:$redirectUrl){
|
@@ -2,4 +2,4 @@
|
|
2
2
|
|
3
3
|
<p><%= t('.confirm_link_msg') %></p>
|
4
4
|
|
5
|
-
<p><%= link_to t('.confirm_account_link'), url_for(controller: '
|
5
|
+
<p><%= link_to t('.confirm_account_link'), url_for(controller: message['controller'], action: message['action'], **confirmation_query(resource_name: @resource.class.to_s, redirect_url: message['redirect-url'], token: @token)) %></p>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<p><%= t('.request_reset_link_msg') %></p>
|
4
4
|
|
5
|
-
<p><%= link_to t('.password_change_link'), url_for(controller: '
|
5
|
+
<p><%= link_to t('.password_change_link'), url_for(controller: message['controller'], action: message['action'], **password_reset_query(token: @token, redirect_url: message['redirect-url'], resource_name: @resource.class.to_s)) %></p>
|
6
6
|
|
7
7
|
<p><%= t('.ignore_mail_msg') %></p>
|
8
8
|
<p><%= t('.no_changes_msg') %></p>
|
data/config/locales/en.yml
CHANGED
@@ -14,6 +14,7 @@ en:
|
|
14
14
|
password_not_required: "This account does not require a password. Sign in using your '%{provider}' account instead."
|
15
15
|
reset_token_not_found: "No user found for the specified reset token."
|
16
16
|
reset_token_expired: "Reset password token is no longer valid."
|
17
|
+
send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
|
17
18
|
sessions:
|
18
19
|
bad_credentials: "Invalid login credentials. Please try again."
|
19
20
|
not_confirmed: "A confirmation email was sent to your account at '%{email}'. You must follow the instructions in the email before your account can be activated"
|
data/config/routes.rb
CHANGED
data/graphql_devise.gemspec
CHANGED
@@ -21,13 +21,15 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.bindir = 'exe'
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ['lib']
|
24
|
-
spec.test_files = Dir
|
24
|
+
spec.test_files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
`git ls-files -z`.split("\x0").select { |f| f.match(%r{^spec/}) }
|
26
|
+
end
|
25
27
|
|
26
28
|
spec.required_ruby_version = '>= 2.2.0'
|
27
29
|
|
28
|
-
spec.add_dependency 'devise_token_auth', '>= 0.1.43'
|
29
|
-
spec.add_dependency 'graphql', '>= 1.8'
|
30
|
-
spec.add_dependency 'rails', '>= 4.2'
|
30
|
+
spec.add_dependency 'devise_token_auth', '>= 0.1.43', '< 2.0'
|
31
|
+
spec.add_dependency 'graphql', '>= 1.8', '< 1.11.0'
|
32
|
+
spec.add_dependency 'rails', '>= 4.2', '< 6.2'
|
31
33
|
|
32
34
|
spec.add_development_dependency 'appraisal'
|
33
35
|
spec.add_development_dependency 'coveralls'
|
@@ -5,62 +5,95 @@ module GraphqlDevise
|
|
5
5
|
argument :user_class, type: :string, default: 'User'
|
6
6
|
argument :mount_path, type: :string, default: 'auth'
|
7
7
|
|
8
|
+
class_option :mount, type: :string, default: 'separate_route'
|
9
|
+
|
8
10
|
def execute_devise_installer
|
9
11
|
generate 'devise:install'
|
10
12
|
end
|
11
13
|
|
12
14
|
def execute_dta_installer
|
15
|
+
# Necessary in case of a re-run of the generator, for DTA to detect concerns already included
|
16
|
+
if File.exist?(File.expand_path("app/models/#{user_class.underscore}.rb", destination_root))
|
17
|
+
gsub_file(
|
18
|
+
"app/models/#{user_class.underscore}.rb",
|
19
|
+
'GraphqlDevise::Concerns::Model',
|
20
|
+
'DeviseTokenAuth::Concerns::User'
|
21
|
+
)
|
22
|
+
end
|
23
|
+
gsub_file(
|
24
|
+
'app/controllers/application_controller.rb',
|
25
|
+
'GraphqlDevise::Concerns::SetUserByToken',
|
26
|
+
'DeviseTokenAuth::Concerns::SetUserByToken'
|
27
|
+
)
|
28
|
+
|
13
29
|
generate 'devise_token_auth:install', "#{user_class} #{mount_path}"
|
14
30
|
end
|
15
31
|
|
16
32
|
def mount_resource_route
|
17
33
|
routes_file = 'config/routes.rb'
|
18
|
-
routes_path = File.join(destination_root, routes_file)
|
19
|
-
gem_helper = 'mount_graphql_devise_for'
|
20
|
-
gem_route = "#{gem_helper} '#{user_class}', at: '#{mount_path}'"
|
21
34
|
dta_route = "mount_devise_token_auth_for '#{user_class}', at: '#{mount_path}'"
|
22
|
-
file_start = 'Rails.application.routes.draw do'
|
23
35
|
|
24
|
-
if
|
25
|
-
|
36
|
+
if options['mount'] != 'separate_route'
|
37
|
+
gsub_file(routes_file, /^\s+#{Regexp.escape(dta_route + "\n")}/i, '')
|
38
|
+
else
|
39
|
+
gem_route = "mount_graphql_devise_for '#{user_class}', at: '#{mount_path}'"
|
40
|
+
|
41
|
+
if file_contains_str?(routes_file, gem_route)
|
42
|
+
gsub_file(routes_file, /^\s+#{Regexp.escape(dta_route + "\n")}/i, '')
|
26
43
|
|
27
|
-
if current_route.present?
|
28
44
|
say_status('skipped', "Routes already exist for #{user_class} at #{mount_path}")
|
29
45
|
else
|
30
|
-
|
31
|
-
|
32
|
-
if current_dta_route.present?
|
33
|
-
replace_line(routes_path, dta_route, gem_route)
|
34
|
-
else
|
35
|
-
insert_text_after_line(routes_path, file_start, gem_route)
|
36
|
-
end
|
46
|
+
gsub_file(routes_file, /#{Regexp.escape(dta_route)}/i, gem_route)
|
37
47
|
end
|
38
|
-
else
|
39
|
-
say_status('skipped', "#{routes_file} not found. Add \"#{gem_route}\" to your routes file.")
|
40
48
|
end
|
41
49
|
end
|
42
50
|
|
43
|
-
|
51
|
+
def replace_model_concern
|
52
|
+
gsub_file(
|
53
|
+
"app/models/#{user_class.underscore}.rb",
|
54
|
+
/^\s+include DeviseTokenAuth::Concerns::User/,
|
55
|
+
' include GraphqlDevise::Concerns::Model'
|
56
|
+
)
|
57
|
+
end
|
44
58
|
|
45
|
-
def
|
46
|
-
gsub_file(
|
47
|
-
|
48
|
-
|
59
|
+
def replace_controller_concern
|
60
|
+
gsub_file(
|
61
|
+
'app/controllers/application_controller.rb',
|
62
|
+
/^\s+include DeviseTokenAuth::Concerns::SetUserByToken/,
|
63
|
+
' include GraphqlDevise::Concerns::SetUserByToken'
|
64
|
+
)
|
49
65
|
end
|
50
66
|
|
51
|
-
def
|
52
|
-
gsub_file(
|
67
|
+
def set_change_headers_on_each_request_false
|
68
|
+
gsub_file(
|
69
|
+
'config/initializers/devise_token_auth.rb',
|
70
|
+
'# config.change_headers_on_each_request = true',
|
71
|
+
'config.change_headers_on_each_request = false'
|
72
|
+
)
|
53
73
|
end
|
54
74
|
|
55
|
-
def
|
56
|
-
|
75
|
+
def mount_in_schema
|
76
|
+
return if options['mount'] == 'separate_route'
|
57
77
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
78
|
+
inject_into_file "app/graphql/#{options['mount'].underscore}.rb", after: "< GraphQL::Schema\n" do
|
79
|
+
<<-RUBY
|
80
|
+
use GraphqlDevise::SchemaPlugin.new(
|
81
|
+
query: Types::QueryType,
|
82
|
+
mutation: Types::MutationType,
|
83
|
+
resource_loaders: [
|
84
|
+
GraphqlDevise::ResourceLoader.new('#{user_class}'),
|
85
|
+
]
|
86
|
+
)
|
87
|
+
RUBY
|
62
88
|
end
|
63
|
-
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def file_contains_str?(filename, regex_str)
|
94
|
+
path = File.join(destination_root, filename)
|
95
|
+
|
96
|
+
File.read(path) =~ /(#{Regexp.escape(regex_str)})/i
|
64
97
|
end
|
65
98
|
end
|
66
99
|
end
|
data/lib/graphql_devise.rb
CHANGED
@@ -18,15 +18,32 @@ module GraphqlDevise
|
|
18
18
|
@schema_loaded = true
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.
|
22
|
-
@mounted_resources
|
21
|
+
def self.resource_mounted?(mapping_name)
|
22
|
+
@mounted_resources.include?(mapping_name)
|
23
23
|
end
|
24
24
|
|
25
|
-
def self.
|
26
|
-
@mounted_resources
|
25
|
+
def self.mount_resource(mapping_name)
|
26
|
+
@mounted_resources << mapping_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.add_mapping(mapping_name, resource)
|
30
|
+
return if Devise.mappings.key?(mapping_name)
|
31
|
+
|
32
|
+
Devise.add_mapping(
|
33
|
+
mapping_name.to_s.pluralize.to_sym,
|
34
|
+
module: :devise, class_name: resource
|
35
|
+
)
|
27
36
|
end
|
28
37
|
end
|
29
38
|
|
39
|
+
require 'graphql_devise/engine'
|
40
|
+
require 'graphql_devise/version'
|
41
|
+
require 'graphql_devise/errors/error_codes'
|
42
|
+
require 'graphql_devise/errors/execution_error'
|
43
|
+
require 'graphql_devise/errors/user_error'
|
44
|
+
require 'graphql_devise/errors/authentication_error'
|
45
|
+
require 'graphql_devise/errors/detailed_user_error'
|
46
|
+
|
30
47
|
require 'graphql_devise/concerns/controller_methods'
|
31
48
|
require 'graphql_devise/schema'
|
32
49
|
require 'graphql_devise/types/authenticatable_type'
|
@@ -37,13 +54,10 @@ require 'graphql_devise/default_operations/mutations'
|
|
37
54
|
require 'graphql_devise/default_operations/resolvers'
|
38
55
|
require 'graphql_devise/resolvers/dummy'
|
39
56
|
|
40
|
-
require 'graphql_devise/engine'
|
41
|
-
require 'graphql_devise/version'
|
42
|
-
require 'graphql_devise/error_codes'
|
43
|
-
require 'graphql_devise/user_error'
|
44
|
-
require 'graphql_devise/detailed_user_error'
|
45
|
-
|
46
57
|
require 'graphql_devise/mount_method/option_sanitizer'
|
47
58
|
require 'graphql_devise/mount_method/options_validator'
|
48
59
|
require 'graphql_devise/mount_method/operation_preparer'
|
49
60
|
require 'graphql_devise/mount_method/operation_sanitizer'
|
61
|
+
|
62
|
+
require 'graphql_devise/resource_loader'
|
63
|
+
require 'graphql_devise/schema_plugin'
|
@@ -9,12 +9,12 @@ require 'graphql_devise/mutations/update_password'
|
|
9
9
|
module GraphqlDevise
|
10
10
|
module DefaultOperations
|
11
11
|
MUTATIONS = {
|
12
|
-
login: GraphqlDevise::Mutations::Login,
|
13
|
-
logout: GraphqlDevise::Mutations::Logout,
|
14
|
-
sign_up: GraphqlDevise::Mutations::SignUp,
|
15
|
-
update_password: GraphqlDevise::Mutations::UpdatePassword,
|
16
|
-
send_password_reset: GraphqlDevise::Mutations::SendPasswordReset,
|
17
|
-
resend_confirmation: GraphqlDevise::Mutations::ResendConfirmation
|
12
|
+
login: { klass: GraphqlDevise::Mutations::Login, authenticatable: true },
|
13
|
+
logout: { klass: GraphqlDevise::Mutations::Logout, authenticatable: true },
|
14
|
+
sign_up: { klass: GraphqlDevise::Mutations::SignUp, authenticatable: true },
|
15
|
+
update_password: { klass: GraphqlDevise::Mutations::UpdatePassword, authenticatable: true },
|
16
|
+
send_password_reset: { klass: GraphqlDevise::Mutations::SendPasswordReset, authenticatable: false },
|
17
|
+
resend_confirmation: { klass: GraphqlDevise::Mutations::ResendConfirmation, authenticatable: false }
|
18
18
|
}.freeze
|
19
19
|
end
|
20
20
|
end
|
@@ -5,8 +5,8 @@ require 'graphql_devise/resolvers/confirm_account'
|
|
5
5
|
module GraphqlDevise
|
6
6
|
module DefaultOperations
|
7
7
|
QUERIES = {
|
8
|
-
confirm_account: GraphqlDevise::Resolvers::ConfirmAccount,
|
9
|
-
check_password_token: GraphqlDevise::Resolvers::CheckPasswordToken
|
8
|
+
confirm_account: { klass: GraphqlDevise::Resolvers::ConfirmAccount },
|
9
|
+
check_password_token: { klass: GraphqlDevise::Resolvers::CheckPasswordToken }
|
10
10
|
}.freeze
|
11
11
|
end
|
12
12
|
end
|
@@ -8,10 +8,10 @@ require_relative 'operation_preparers/custom_operation_preparer'
|
|
8
8
|
module GraphqlDevise
|
9
9
|
module MountMethod
|
10
10
|
class OperationPreparer
|
11
|
-
def initialize(
|
11
|
+
def initialize(mapping_name:, selected_operations:, preparer:, custom:, additional_operations:)
|
12
12
|
@selected_operations = selected_operations
|
13
13
|
@preparer = preparer
|
14
|
-
@mapping_name =
|
14
|
+
@mapping_name = mapping_name
|
15
15
|
@custom = custom
|
16
16
|
@additional_operations = additional_operations
|
17
17
|
end
|
@@ -10,14 +10,18 @@ module GraphqlDevise
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def call
|
13
|
-
@selected_operations.except(*@custom_keys).each_with_object({}) do |(action,
|
13
|
+
@selected_operations.except(*@custom_keys).each_with_object({}) do |(action, operation_info), result|
|
14
14
|
mapped_action = "#{@mapping_name}_#{action}"
|
15
|
+
operation = operation_info[:klass]
|
16
|
+
options = operation_info.except(:klass)
|
15
17
|
|
16
18
|
result[mapped_action.to_sym] = [
|
17
19
|
OperationPreparers::GqlNameSetter.new(mapped_action),
|
18
20
|
@preparer,
|
19
21
|
OperationPreparers::ResourceNameSetter.new(@mapping_name)
|
20
|
-
].reduce(child_class(operation))
|
22
|
+
].reduce(child_class(operation)) do |prepared_operation, preparer|
|
23
|
+
preparer.call(prepared_operation, **options)
|
24
|
+
end
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
@@ -6,9 +6,10 @@ module GraphqlDevise
|
|
6
6
|
@authenticatable_type = authenticatable_type
|
7
7
|
end
|
8
8
|
|
9
|
-
def call(mutation)
|
10
|
-
mutation
|
9
|
+
def call(mutation, authenticatable: true)
|
10
|
+
return mutation unless authenticatable
|
11
11
|
|
12
|
+
mutation.field(:authenticatable, @authenticatable_type, null: false)
|
12
13
|
mutation
|
13
14
|
end
|
14
15
|
end
|
@@ -19,13 +19,11 @@ module GraphqlDevise
|
|
19
19
|
|
20
20
|
resource.send_confirmation_instructions(
|
21
21
|
redirect_url: redirect_url,
|
22
|
-
template_path: ['graphql_devise/mailer']
|
22
|
+
template_path: ['graphql_devise/mailer'],
|
23
|
+
**controller.params.permit('controller', 'action').to_h.symbolize_keys
|
23
24
|
)
|
24
25
|
|
25
|
-
{
|
26
|
-
authenticatable: resource,
|
27
|
-
message: I18n.t('graphql_devise.confirmations.send_instructions', email: email)
|
28
|
-
}
|
26
|
+
{ message: I18n.t('graphql_devise.confirmations.send_instructions', email: email) }
|
29
27
|
else
|
30
28
|
raise_user_error(I18n.t('graphql_devise.confirmations.user_not_found', email: email))
|
31
29
|
end
|