Resolvers in GraphQL do not seem to get context - graphql

I am using resolvers inside query_type:
module Types
class QueryType < Types::BaseObject
...
field :gather_things,
resolver: Resolvers::GatherThings,
null: true do
argument :scope, String, required: false
argument :scope_id, ID, required: false
...
And in my resolvers folder there is a base file:
module Resolvers
class Base < GraphQL::Schema::Resolver
def current_user_id
#current_user_id ||= context[:current_user].id
end
end
end
However, when I try to use current_user_id inside the resolver code, it breaks:
module Resolvers
class GatherThings < Resolvers::Base
...
def things_resolver(scope, scope_id)
if scope.nil?
Thing.from_user(current_user_id)
...
end
end
...
Saying : "undefined method 'id' for nil:NilClass"
Any clue?

I later found out that something was nullifying my session upstream.
This was due to the use of JWTs for session management and mixing them with cookies for OmniAuth.
I have to work out on a solution for using both depending on the intended use.

Related

How to dynamically define `prop`s on a `T::Struct` in sorbet at runtime?

I have a struct that is defined as:
# typed: true
require 'sorbet-runtime'
class MyStruct < T::Struct
MyPropType = T.type_alias { T::Hash[Symbol, Class] }
class << self
extend T::Sig
sig { params(props: MyPropType).void }
def register_props(props)
props.each do |prop_name, prop_type|
prop(prop_name, prop_type)
end
end
end
end
Notice how props are defined at runtime.
Then somewhere in my codebase, at startup, I do MyStruct.register_props({ foo: T.untyped, bar: T.nilable(T.untyped) }).
Initializing MyStruct gives error when passing the codebase through typecheck. MyStruct.new(foo: 'foo', bar: Bar.new).
$ ./bin/srb typecheck
/path/to/file.rb:66: Too many arguments provided for method MyStruct#initialize. Expected: 0, got: 1 https://srb.help/7004
How do I define props on T::Struct at runtime without the above typecheck error?
AFAIK T::Structs cannot be defined dynamically (I mean they can but...), since the typechecker needs to statically know which props it's going to have. For this case I think you should use T::InexactStruct. See https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/struct.rb
EDIT: Add snippet for future references
# typed: strict
class SomeStruct < T::InexactStruct
extend T::Sig
sig { params(props: T::Array[T.untyped]).void }
def self.register(props)
props.each do |name, type|
prop name, type
end
end
end
SomeStruct.register [[:one, String], [:two, String]]
SomeStruct.new() # This would raise an error on runtime because of missing arguments, but not on static check
SomeStruct.new(one: '', two: '') # works on runtime, no static error
SomeStruct.new(one: '', two: 1) # fails on runtime because of type mismatch, no static error
SomeStruct.new(one: '', two: '', three: '') # fails on runtime because of extra argument, no static error

What's the best practice to achieve dependency injection with ruby-graphql?

I want to use dependency injection with graphql-ruby.
I.e.
module CustomerCredits
module Types
class QueryType < GraphQL::Schema::Object
description 'The query root of this schema'
field :filter_users, [Types::UserType], null: false do
argument :parameters, InputTypes::UserFilterParameters, required: true
end
# resolvers
def filter_users(arguments)
repo = UserRepository.new(complex_arguments) # I want to inject the dependency UserRepository
repo.filtered_users(**arguments[:parameters])
end
end
end
end
Using dependency injection in initialize is not possible, because QueryType is instantiated by graphql-ruby.
As you've mentioned, injection through the initializer might not be super straight forward, so if you want to go fully into dependency injection and inversion of control, you could leverage an IOC Container library like Dry Auto Inject. I know it might be a full blown solution, and it could possibly be too heavy handed for your use case (not sure), but since you're already using repositories in Ruby, it might not be.
Following the schema definition from graphql-ruby, one solution I thought of for this problem was to inject your database reference into a controller class, then when your controller is hit, you pass the database reference as part of the context.
# app/controllers/graphql_controller.rb
result = MySchema.execute(
params[:query],
variables: params[:variables],
context: {
current_user: current_user,
db: my_database_ref # inject database ref here
},
)
render json: result
Then in your Query Type definiton you can pull the db from the context.
class QueryType < GraphQL::Schema::Object
description "The query root of this schema"
field :post, PostType, "Find a post by ID" do
argument :id, ID
end
def post(id:)
db = context[:db] # pull db reference from context here
db[:posts].where(id:).first
end
end

Authorization on a field in graphql-ruby

From this guide I don't understand how we can add authorization on a field in graphql-ruby.
I understand how we can deny access to an entire object but I don't see how we can prevent access to just a field of an object.
In my usecase, I want to prevent some queries to access a String field in my UserType.
Is it possible? With the field helper?
You could use scoping in the GraphQL-ruby or use gem graphql-guard.
For scoping to work, you should be either using Pundit scope or CanCan accessible_by.
FYI, I haven't used it yet anywhere but seems like both could solve your problem to me.
I used gem graphql-guard.
GUARD = lambda do |obj, args, ctx|
...some logics...
end
field :some_field, String, null: false, guard: GUARD
And when I wanted to use only some of the arguments like lambda do |_, args, _|
Here's an example:
module Types
class User < Types::BaseObject
field :private_string, String, null: true do
def authorized?(object, args, context)
# Do whatever logic you want and return true if the user is authorized.
object.id == context[:current_user].id
end
end
end
end
class MySchema < GraphQL::Schema
# You'll need this in your schema to handle an unauthorized field.
def self.unauthorized_field(error)
raise GraphQL::ExecutionError, "The field #{error.field.graphql_name} on an object of type #{error.type.graphql_name} was hidden due to permissions"
end
end

How to write spec for concern

I created a concern below. what i am doing is i am mapping the column from a table that has values 0, 1, 2 and returning strings for those in JSON. Can someone guide me how can i write unit test cases for it?
module User
extend ActiveSupport::Concern
included do
def user_mapping(user_in_number)
user_hash = {
'0'=> 'Support',
'1'=> 'Developer',
'2'=> 'Business Analyst'
}.freeze
user_hash[user_in_number]
end
end
end
Thanks
For any module you can do this:
subject = Class.new do
include User
end.new
assert subject.user_mapping('0'), 'Support'
Class.new creates an anonymous class, the block is evaluated in the context of the anonymous class, so acting like the body of a regular class.
Then create an instance of the anonymous class and call the methods as defined by the included module.
By the way the actual method does not need to assign so many vars:
UserMap = {
'0'=> 'Support',
'1'=> 'Developer',
'2'=> 'Business Analyst'
}.freeze
def user_mapping(user_in_number)
UserMap.fetch(user_in_number)
end
fetch will raise if the given key is not found.
I'd might also reconsider the name User for a module, will you never need a User class...

Reusable Custom DSL Extension in Grape

I have a custom Grape DSL method called scope that remembers the parameter then delegates some work to the params helper.
class API::V1::Walruses < Grape::API
resources :walruses do
scope :with_tusks, type: Integer,
desc: "Return walruses with the exact number of tusks"
scope :by_name, type: String,
desc: "Return walruses whose name matches the supplied parameter"
get do
# Equivalent to `Walrus.with_tusks(params[:with_tusks]).
# by_name(params[:by_name])`, but only calls scopes whose
# corresponding param was passed in.
apply_scopes(Walrus)
end
end
end
This works and is clean, which I like. The problem is that many child endpoints also want to use those same scopes. This means maintaining them in multiple places and keeping them all in sync.
I would like to do the following instead:
class API::V1::Walruses < Grape::API
helpers do
scopes :index do
scope :with_tusks, type: Integer,
desc: "Return walruses with the exact number of tusks"
scope :by_name, type: String,
desc: "Return walruses whose name matches the supplied parameter"
end
end
resources :walruses do
use_scopes :index
#...
end
end
Then API::V1::Walruses will have those scopes available, as will API::V1::Walruses::Analytics (if included), and any others that are nested within.
Params have a similar methodology but I've had trouble understanding how to inherit settings.

Resources