Skip to content

Commit e4cd329

Browse files
committed
Postpone set_resource_by_token until tracer
1 parent 55ec2a1 commit e4cd329

6 files changed

Lines changed: 56 additions & 29 deletions

File tree

README.md

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -343,38 +343,32 @@ and an error is returned in a REST format as the request never reaches your GQL
343343

344344
#### Authenticate in Your GQL Schema
345345
For this you will need to add the `GraphqlDevise::SchemaPlugin` to your schema as described
346-
[here](#mounting-operations-into-your-own-schema) and also set the authenticated resource
347-
in a `before_action` hook.
346+
[here](#mounting-operations-into-your-own-schema).
348347

349348
```ruby
350349
# app/controllers/my_controller.rb
351350

352351
class MyController < ApplicationController
353352
include GraphqlDevise::Concerns::SetUserByToken
354353

355-
before_action -> { set_resource_by_token(:user) }
356-
357354
def my_action
358-
render json: DummySchema.execute(params[:query], context: graphql_context)
355+
render json: DummySchema.execute(params[:query], context: graphql_context(:user))
359356
end
360357
end
361-
362-
# @resource.to_s.underscore.tr('/', '_').to_sym
363358
```
364-
The `set_resource_by_token` method receives a symbol identifying the resource you are trying
359+
The `graphql_context` method receives a symbol identifying the resource you are trying
365360
to authenticate. So if you mounted the `'User'` resource, the symbol is `:user`. You can use
366361
this snippet to find the symbol for more complex scenarios
367-
`resource_klass.to_s.underscore.tr('/', '_').to_sym`.
362+
`resource_klass.to_s.underscore.tr('/', '_').to_sym`. `graphql_context` can also take an
363+
array of resources if you mounted more than one into your schema. The gem will try to
364+
authenticate a resource for each element on the array until it finds one.
368365

369-
The `graphql_context` method is simply a helper method that returns a hash like this
370-
```ruby
371-
{ current_resource: @resource, controller: self }
372-
```
373-
These are the two values the gem needs to check if a user is authenticated and to perform
374-
other auth operations. All `set_resource_by_token` does is set the `@resource` variable if
375-
the provided authentication headers are valid. If authentication fails, resource will be `nil`
376-
and this is how `GraphqlDevise::SchemaPlugin` knows if a user is authenticated or not in
377-
each query.
366+
Internally in your own mutations and queries a key `current_resource` will be available in
367+
the context if a resource was successfully authenticated or `nil` otherwise.
368+
369+
Keep in mind that sending multiple values to the `graphql_context` method means that depending
370+
on who makes the request, the context value `current_resource` might contain instances of the
371+
different models you might have mounted into the schema.
378372

379373
Please note that by using this mechanism your GQL schema will be in control of what queries are
380374
restricted to authenticated users and you can only do this at the root level fields of your GQL

app/controllers/graphql_devise/concerns/set_user_by_token.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ module Concerns
55
SetUserByToken.module_eval do
66
attr_accessor :client_id, :token, :resource
77

8-
alias_method :set_resource_by_token, :set_user_by_token
8+
def set_resource_by_token(resource)
9+
set_user_by_token(resource)
10+
end
911

10-
def graphql_context
12+
def graphql_context(resource_name)
1113
{
12-
current_resource: @resource,
13-
controller: self
14+
resource_name: resource_name,
15+
controller: self
1416
}
1517
end
1618

lib/graphql_devise/schema_plugin.rb

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,42 @@ def trace(event, trace_data)
2323

2424
field = traced_field(trace_data)
2525
provided_value = authenticate_option(field, trace_data)
26+
context = set_current_resource(context_from_data(trace_data))
2627

2728
if !provided_value.nil?
28-
raise_on_missing_resource(context(trace_data), field) if provided_value
29+
raise_on_missing_resource(context, field) if provided_value
2930
elsif @authenticate_default
30-
raise_on_missing_resource(context(trace_data), field)
31+
raise_on_missing_resource(context, field)
3132
end
3233

3334
yield
3435
end
3536

3637
private
3738

39+
def set_current_resource(context)
40+
controller = context[:controller]
41+
resource_names = Array(context[:resource_name])
42+
context[:current_resource] = resource_names.find do |resource_name|
43+
unless Devise.mappings.key?(resource_name)
44+
raise(
45+
GraphqlDevise::Error,
46+
"Invalid resource_name `#{resource_name}` provided to `graphql_context`. Possible values are: #{Devise.mappings.keys}."
47+
)
48+
end
49+
50+
found = controller.set_resource_by_token(resource_name)
51+
break found if found
52+
end
53+
54+
context
55+
end
56+
3857
def raise_on_missing_resource(context, field)
3958
@unauthenticated_proc.call(field.name) if context[:current_resource].blank?
4059
end
4160

42-
def context(trace_data)
61+
def context_from_data(trace_data)
4362
query = if trace_data[:context]
4463
trace_data[:context].query
4564
else

spec/dummy/app/controllers/api/v1/graphql_controller.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ module V1
33
class GraphqlController < ApplicationController
44
include GraphqlDevise::Concerns::SetUserByToken
55

6-
before_action -> { set_resource_by_token(:user) }
7-
86
def graphql
9-
render json: DummySchema.execute(params[:query], context: graphql_context)
7+
render json: DummySchema.execute(params[:query], context: graphql_context(:user))
108
end
119

1210
def interpreter
13-
render json: InterpreterSchema.execute(params[:query], context: graphql_context)
11+
render json: InterpreterSchema.execute(params[:query], context: graphql_context(:user))
12+
end
13+
14+
def failing_resource_name
15+
render json: DummySchema.execute(params[:query], context: graphql_context([:user, :fail]))
1416
end
1517

1618
private

spec/dummy/config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@
2929

3030
post '/api/v1/graphql', to: 'api/v1/graphql#graphql'
3131
post '/api/v1/interpreter', to: 'api/v1/graphql#interpreter'
32+
post '/api/v1/failing', to: 'api/v1/graphql#failing_resource_name'
3233
end

spec/requests/user_controller_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929
expect(json_response[:data][:publicField]).to eq('Field does not require authentication')
3030
end
3131
end
32+
33+
context 'when using the failing route' do
34+
it 'raises an invalid resource_name error' do
35+
expect { post_request('/api/v1/failing') }.to raise_error(
36+
GraphqlDevise::Error,
37+
'Invalid resource_name `fail` provided to `graphql_context`. Possible values are: [:user, :admin, :guest, :users_customer].'
38+
)
39+
end
40+
end
3241
end
3342

3443
describe 'privateField' do

0 commit comments

Comments
 (0)