Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
32 changes: 31 additions & 1 deletion app/controllers/graphql_devise/concerns/set_user_by_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ module Concerns
SetUserByToken.module_eval do
attr_accessor :client_id, :token, :resource

class_methods do
def set_resource_by_model(models, **kwargs)
Comment thread
00dav00 marked this conversation as resolved.
Outdated
Array(models).each do |model|
GraphqlDevise.configure_warden_serializer_for_model(model)
end

before_action(**kwargs) do
authenticate_model(models)
end
end
end

def authenticate_model(models)
Array(models).each do |model|
set_resource_by_token(model)
return @resource if @resource.present?
end

nil
end

def resource_class(resource = nil)
return resource if resource.respond_to?(:find_by)

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.

Can we add a comment here indicating this is meant to assert an AR or MongoId instance?


super
end

def full_url_without_params
request.base_url + request.path
end
Expand All @@ -16,10 +43,13 @@ def set_resource_by_token(resource)
end

def graphql_context(resource_name)
{
context = {
resource_name: resource_name,
controller: self
}
context[:current_resource] = @resource if @resource.present?

context
end

def build_redirect_headers(access_token, client, redirect_header_options = {})
Expand Down
4 changes: 2 additions & 2 deletions app/helpers/graphql_devise/mailer_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module GraphqlDevise
module MailerHelper
def confirmation_query(resource_name:, token:, redirect_url:)
name = "#{resource_name.underscore.tr('/', '_').camelize(:lower)}ConfirmAccount"
name = "#{GraphqlDevise.to_mapping_name(resource_name).camelize(:lower)}ConfirmAccount"
raw = <<-GRAPHQL
query($token:String!,$redirectUrl:String!){
#{name}(confirmationToken:$token,redirectUrl:$redirectUrl){
Expand All @@ -19,7 +19,7 @@ def confirmation_query(resource_name:, token:, redirect_url:)
end

def password_reset_query(token:, redirect_url:, resource_name:)
name = "#{resource_name.underscore.tr('/', '_').camelize(:lower)}CheckPasswordToken"
name = "#{GraphqlDevise.to_mapping_name(resource_name).camelize(:lower)}CheckPasswordToken"
raw = <<-GRAPHQL
query($token:String!,$redirectUrl:String!){
#{name}(resetPasswordToken:$token,redirectUrl:$redirectUrl){
Expand Down
24 changes: 19 additions & 5 deletions lib/graphql_devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,36 @@ def self.load_schema
@schema_loaded = true
end

def self.resource_mounted?(mapping_name)
@mounted_resources.include?(mapping_name)
def self.resource_mounted?(model)
@mounted_resources.include?(model)
end

def self.mount_resource(mapping_name)
@mounted_resources << mapping_name
def self.mount_resource(model)
@mounted_resources << model

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.

Should we check the resource is not already there before adding?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

we do on the resource_loader

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.

I think it would less error prone if the check is closer to the assignation, like with add_mapping bellow where the key is looked among the devise mapping before adding

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

With add_mapping it is important to prevent adding a new Devise mapping as it has mnay implications, with this one, no real problem if we ever mount the same model more than once. I think it would just add unnecessary overhead to check the list each time we mount a resource. This array is only used to later check the model is or not already on the list.

end

def self.add_mapping(mapping_name, resource)
return if Devise.mappings.key?(mapping_name)
return if Devise.mappings.key?(mapping_name.to_sym)

Devise.add_mapping(
mapping_name.to_s.pluralize.to_sym,
module: :devise, class_name: resource
)
end

def self.to_mapping_name(resource)
resource.to_s.underscore.tr('/', '_')
end

def self.configure_warden_serializer_for_model(model)
Devise.warden_config.serialize_into_session(to_mapping_name(model)) do |record|
model.serialize_into_session(record)
end

Devise.warden_config.serialize_from_session(to_mapping_name(model)) do |args|
model.serialize_from_session(*args)
end
end
end

require 'graphql_devise/engine'
Expand Down
6 changes: 3 additions & 3 deletions lib/graphql_devise/concerns/controller_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ def controller
end

def resource_name
self.class.instance_variable_get(:@resource_name)
GraphqlDevise.to_mapping_name(resource_class)
end

def resource_class
controller.send(:resource_class, resource_name)
self.class.instance_variable_get(:@resource_klass)
end

def recoverable_enabled?
Expand All @@ -60,7 +60,7 @@ def blacklisted_redirect_url?(redirect_url)
end

def current_resource
@current_resource ||= controller.send(:set_user_by_token, resource_name)
@current_resource ||= controller.send(:set_user_by_token, resource_class)
end

def client
Expand Down
12 changes: 6 additions & 6 deletions lib/graphql_devise/mount_method/operation_preparer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
require_relative 'operation_preparers/gql_name_setter'
require_relative 'operation_preparers/mutation_field_setter'
require_relative 'operation_preparers/resolver_type_setter'
require_relative 'operation_preparers/resource_name_setter'
require_relative 'operation_preparers/resource_klass_setter'
require_relative 'operation_preparers/default_operation_preparer'
require_relative 'operation_preparers/custom_operation_preparer'

module GraphqlDevise
module MountMethod
class OperationPreparer
def initialize(mapping_name:, selected_operations:, preparer:, custom:, additional_operations:)
def initialize(model:, selected_operations:, preparer:, custom:, additional_operations:)
@selected_operations = selected_operations
@preparer = preparer
@mapping_name = mapping_name
@model = model
@custom = custom
@additional_operations = additional_operations
end
Expand All @@ -22,18 +22,18 @@ def call
default_operations = OperationPreparers::DefaultOperationPreparer.new(
selected_operations: @selected_operations,
custom_keys: @custom.keys,
mapping_name: @mapping_name,
model: @model,
preparer: @preparer
).call

custom_operations = OperationPreparers::CustomOperationPreparer.new(
selected_keys: @selected_operations.keys,
custom_operations: @custom,
mapping_name: @mapping_name
model: @model
).call

additional_operations = @additional_operations.each_with_object({}) do |(action, operation), result|
result[action] = OperationPreparers::ResourceNameSetter.new(@mapping_name).call(operation)
result[action] = OperationPreparers::ResourceKlassSetter.new(@model).call(operation)
end

default_operations.merge(custom_operations).merge(additional_operations)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ module GraphqlDevise
module MountMethod
module OperationPreparers
class CustomOperationPreparer
def initialize(selected_keys:, custom_operations:, mapping_name:)
def initialize(selected_keys:, custom_operations:, model:)
@selected_keys = selected_keys
@custom_operations = custom_operations
@mapping_name = mapping_name
@model = model
end

def call
mapping_name = GraphqlDevise.to_mapping_name(@model)

@custom_operations.slice(*@selected_keys).each_with_object({}) do |(action, operation), result|
mapped_action = "#{@mapping_name}_#{action}"
mapped_action = "#{mapping_name}_#{action}"

result[mapped_action.to_sym] = [
OperationPreparers::GqlNameSetter.new(mapped_action),
OperationPreparers::ResourceNameSetter.new(@mapping_name)
OperationPreparers::ResourceKlassSetter.new(@model)
].reduce(operation) { |prepared_operation, preparer| preparer.call(prepared_operation) }
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@ module GraphqlDevise
module MountMethod
module OperationPreparers
class DefaultOperationPreparer
def initialize(selected_operations:, custom_keys:, mapping_name:, preparer:)
def initialize(selected_operations:, custom_keys:, model:, preparer:)
@selected_operations = selected_operations
@custom_keys = custom_keys
@mapping_name = mapping_name
@model = model
@preparer = preparer
end

def call
mapping_name = GraphqlDevise.to_mapping_name(@model)

@selected_operations.except(*@custom_keys).each_with_object({}) do |(action, operation_info), result|
mapped_action = "#{@mapping_name}_#{action}"
mapped_action = "#{mapping_name}_#{action}"
operation = operation_info[:klass]
options = operation_info.except(:klass)

result[mapped_action.to_sym] = [
OperationPreparers::GqlNameSetter.new(mapped_action),
@preparer,
OperationPreparers::ResourceNameSetter.new(@mapping_name)
OperationPreparers::ResourceKlassSetter.new(@model)
].reduce(child_class(operation)) do |prepared_operation, preparer|
preparer.call(prepared_operation, **options)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
module GraphqlDevise
module MountMethod
module OperationPreparers
class ResourceNameSetter
def initialize(name)
@name = name
class ResourceKlassSetter
def initialize(klass)
@klass = klass
end

def call(operation, **)
operation.instance_variable_set(:@resource_name, @name)
operation.instance_variable_set(:@resource_klass, @klass)

operation
end
Expand Down
23 changes: 12 additions & 11 deletions lib/graphql_devise/resource_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ def initialize(resource, options = {}, routing = false)
end

def call(query, mutation)
mapping_name = @resource.to_s.underscore.tr('/', '_').to_sym

# clean_options responds to all keys defined in GraphqlDevise::MountMethod::SUPPORTED_OPTIONS
clean_options = GraphqlDevise::MountMethod::OptionSanitizer.new(@options).call!

return clean_options if GraphqlDevise.resource_mounted?(mapping_name) && @routing
model = @resource.is_a?(String) ? @resource.constantize : @resource
Comment thread
00dav00 marked this conversation as resolved.

return clean_options if GraphqlDevise.resource_mounted?(model) && @routing

validate_options!(clean_options)

authenticatable_type = clean_options.authenticatable_type.presence ||
"Types::#{@resource}Type".safe_constantize ||
GraphqlDevise::Types::AuthenticatableType

prepared_mutations = prepare_mutations(mapping_name, clean_options, authenticatable_type)
prepared_mutations = prepare_mutations(model, clean_options, authenticatable_type)

if prepared_mutations.any? && mutation.blank?
raise GraphqlDevise::Error, 'You need to provide a mutation type unless all mutations are skipped'
Expand All @@ -33,7 +33,7 @@ def call(query, mutation)
mutation.field(action, mutation: prepared_mutation, authenticate: false)
end

prepared_resolvers = prepare_resolvers(mapping_name, clean_options, authenticatable_type)
prepared_resolvers = prepare_resolvers(model, clean_options, authenticatable_type)

if prepared_resolvers.any? && query.blank?
raise GraphqlDevise::Error, 'You need to provide a query type unless all queries are skipped'
Expand All @@ -43,17 +43,18 @@ def call(query, mutation)
query.field(action, resolver: resolver, authenticate: false)
end

GraphqlDevise.add_mapping(mapping_name, @resource)
GraphqlDevise.mount_resource(mapping_name) if @routing
GraphqlDevise.configure_warden_serializer_for_model(model)
GraphqlDevise.add_mapping(GraphqlDevise.to_mapping_name(@resource).to_sym, @resource)
GraphqlDevise.mount_resource(model) if @routing

clean_options
end

private

def prepare_resolvers(mapping_name, clean_options, authenticatable_type)
def prepare_resolvers(model, clean_options, authenticatable_type)
GraphqlDevise::MountMethod::OperationPreparer.new(
mapping_name: mapping_name,
model: model,
custom: clean_options.operations,
additional_operations: clean_options.additional_queries,
preparer: GraphqlDevise::MountMethod::OperationPreparers::ResolverTypeSetter.new(authenticatable_type),
Expand All @@ -63,9 +64,9 @@ def prepare_resolvers(mapping_name, clean_options, authenticatable_type)
).call
end

def prepare_mutations(mapping_name, clean_options, authenticatable_type)
def prepare_mutations(model, clean_options, authenticatable_type)
GraphqlDevise::MountMethod::OperationPreparer.new(
mapping_name: mapping_name,
model: model,
custom: clean_options.operations,
additional_operations: clean_options.additional_mutations,
preparer: GraphqlDevise::MountMethod::OperationPreparers::MutationFieldSetter.new(authenticatable_type),
Expand Down
6 changes: 0 additions & 6 deletions lib/graphql_devise/schema_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ def initialize(query: nil, mutation: nil, authenticate_default: true, public_int

# Must happen on initialize so operations are loaded before the types are added to the schema on GQL < 1.10
load_fields
reconfigure_warden!
end

def use(schema_definition)
Expand Down Expand Up @@ -102,11 +101,6 @@ def authenticate_option(field, trace_data)
auth_required.nil? ? @authenticate_default : auth_required
end

def reconfigure_warden!
Devise.class_variable_set(:@@warden_configured, nil)
Devise.configure_warden!
end

def load_fields
@resource_loaders.each do |resource_loader|
raise Error, 'Invalid resource loader instance' unless resource_loader.instance_of?(GraphqlDevise::ResourceLoader)
Expand Down
8 changes: 8 additions & 0 deletions spec/dummy/app/controllers/api/v1/graphql_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module V1
class GraphqlController < ApplicationController
include GraphqlDevise::Concerns::SetUserByToken

set_resource_by_model SchemaUser, only: [:controller_auth]

def graphql
result = DummySchema.execute(params[:query], **execute_params(params))

Expand All @@ -19,6 +21,12 @@ def failing_resource_name
render json: DummySchema.execute(params[:query], context: graphql_context([:user, :fail]))
end

def controller_auth
result = DummySchema.execute(params[:query], **execute_params(params))

render json: result unless performed?
end

private

def execute_params(item)
Expand Down
3 changes: 2 additions & 1 deletion spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
}

mount_graphql_devise_for(
'Admin',
Admin,
authenticatable_type: Types::CustomAdminType,
skip: [:sign_up, :check_password_token],
operations: {
Expand All @@ -37,4 +37,5 @@
post '/api/v1/graphql', to: 'api/v1/graphql#graphql'
post '/api/v1/interpreter', to: 'api/v1/graphql#interpreter'
post '/api/v1/failing', to: 'api/v1/graphql#failing_resource_name'
post '/api/v1/controller_auth', to: 'api/v1/graphql#controller_auth'
end
4 changes: 3 additions & 1 deletion spec/graphql/user_queries_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@
let(:query) do
<<-GRAPHQL
query {
user(id: #{user.id}) {
user(
id: #{user.id}
) {
id
email
}
Expand Down
Loading