Describe the bug
I've been trying to track down intermittent differences in visibility between Warden and Visibility. I "think" I have narrowed it down to the way Visibility traverses the schema. I could be way off here, but bear with me, and feel free to correct me if I am misunderstanding something.
Warden poplates a cumulative map from Schema.references_to. Visiblity does not use this cumulative map, instead it traverses the schema fresh starting from the root of each query. if at this time a type has not been created, it isn't attached to that schema graph and is unreachable and thus not visible. which would yield a "field does not exist" error message.
We are currently dynamically building types off of models using a gem we created called graphiform. The idea here is to make it easy and quick to expose model fields to the GraphQL layer without the boilerplate of building deeply nested types by hand. It allows us to quickly add new fields with very little code. The problem I believe we are seeing stems from the fact these types don't get built until the model classes themselves get autoloaded. this is only a problem therefore when not eager loading, which is very common in development.
In this specific use case depending on the order that models get autoloaded in Rails, and the specific query being utilized, this leads to the potential problem of fields that should be visible, not being visible.
Versions
graphql version: 2.6.2
rails (or other framework): rails 8.1
graphiform: 0.5.0
GraphQL schema
Unfortunately, I haven't been able to narrow this down to a specific schema that causes the issue. It's seemingly random. I suspect it has to do with the way lazy-loading interacts with our dynamic type definition library, but again I can't seem to narrow down specific reproduction steps.
Steps to reproduce
with eager_load disabled run queries against graphql schema
Expected behavior
query resolves all fields and visibility returns the correct fields.
Actual behavior
either migration error for visibility migration mode is raised or a query with a field that should be visible will raise a field doesn't exist on type error.
Place full backtrace here (if a Ruby exception is involved):
Click to view exception backtrace
3form-api-1 | Mismatch in types for `#type("String")`:
3form-api-1 |
3form-api-1 | - Warden returned: String (GraphQL::Types::String)
3form-api-1 |
3form-api-1 | - Visibility::Profile returned: nil
3form-api-1 |
3form-api-1 | Update your `.visible?` implementation to make these implementations return the same value.
3form-api-1 |
3form-api-1 | See: https://graphql-ruby.org/authorization/visibility_migration.html
3form-api-1 |
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/schema/visibility/migration.rb:148:in 'GraphQL::Schema::Visibility::Migration#call_method_and_compare'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/schema/visibility/migration.rb:132:in 'block (2 levels) in <class:Migration>'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/variables_are_input_types.rb:7:in 'GraphQL::StaticValidation::VariablesAreInputTypes#on_variable_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:70:in 'block in GraphQL::Language::StaticVisitor#on_operation_definition_children'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:69:in 'Array#each'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:69:in 'GraphQL::Language::StaticVisitor#on_operation_definition_children'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:111:in 'GraphQL::Language::StaticVisitor#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/definition_dependencies.rb:47:in 'GraphQL::StaticValidation::DefinitionDependencies#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb:21:in 'GraphQL::StaticValidation::SubscriptionRootExistsAndSingleSubscriptionSelection#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/query_root_exists.rb:12:in 'GraphQL::StaticValidation::QueryRootExists#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/mutation_root_exists.rb:12:in 'GraphQL::StaticValidation::MutationRootExists#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb:13:in 'GraphQL::StaticValidation::VariableUsagesAreAllowed#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb:44:in 'GraphQL::StaticValidation::VariablesAreUsedAndDefined#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/variable_names_are_unique.rb:20:in 'GraphQL::StaticValidation::VariableNamesAreUnique#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb:17:in 'GraphQL::StaticValidation::FieldsHaveAppropriateSelections#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/fields_will_merge.rb:56:in 'GraphQL::StaticValidation::FieldsWillMerge#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/operation_names_are_valid.rb:12:in 'GraphQL::StaticValidation::OperationNamesAreValid#on_operation_definition'
3form-api-1 | (eval at /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/unique_directives_per_location.rb:32):5:in 'GraphQL::StaticValidation::UniqueDirectivesPerLocation#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/base_visitor.rb:64:in 'GraphQL::StaticValidation::BaseVisitor::ContextMethods#on_operation_definition'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:28:in 'Kernel#public_send'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:28:in 'block in GraphQL::Language::StaticVisitor#on_document_children'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:26:in 'Array#each'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:26:in 'GraphQL::Language::StaticVisitor#on_document_children'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:111:in 'GraphQL::Language::StaticVisitor#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/definition_dependencies.rb:38:in 'GraphQL::StaticValidation::DefinitionDependencies#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb:79:in 'GraphQL::StaticValidation::VariablesAreUsedAndDefined#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb:26:in 'GraphQL::StaticValidation::FragmentSpreadsArePossible#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/fragments_are_used.rb:6:in 'GraphQL::StaticValidation::FragmentsAreUsed#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/fragments_are_finite.rb:6:in 'GraphQL::StaticValidation::FragmentsAreFinite#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/fragment_names_are_unique.rb:17:in 'GraphQL::StaticValidation::FragmentNamesAreUnique#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/operation_names_are_valid.rb:16:in 'GraphQL::StaticValidation::OperationNamesAreValid#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/rules/no_definitions_are_present.rb:34:in 'GraphQL::StaticValidation::NoDefinitionsArePresent#on_document'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:16:in 'Kernel#public_send'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/language/static_visitor.rb:16:in 'GraphQL::Language::StaticVisitor#visit'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/validator.rb:47:in 'block (3 levels) in GraphQL::StaticValidation::Validator#validate'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/validator.rb:46:in 'Kernel#catch'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/validator.rb:46:in 'block (2 levels) in GraphQL::StaticValidation::Validator#validate'
3form-api-1 | /bundle/ruby/3.4.0/gems/timeout-0.4.3/lib/timeout.rb:185:in 'block in Timeout.timeout'[12:22 PM]3form-api-1 | /bundle/ruby/3.4.0/gems/timeout-0.4.3/lib/timeout.rb:38:in 'Timeout::Error.handle_timeout'
3form-api-1 | /bundle/ruby/3.4.0/gems/timeout-0.4.3/lib/timeout.rb:194:in 'Timeout.timeout'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/validator.rb:45:in 'block in GraphQL::StaticValidation::Validator#validate'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/tracing/trace.rb:33:in 'GraphQL::Tracing::Trace#validate'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/static_validation/validator.rb:32:in 'GraphQL::StaticValidation::Validator#validate'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/query/validation_pipeline.rb:72:in 'GraphQL::Query::ValidationPipeline#ensure_has_validated'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/query/validation_pipeline.rb:33:in 'GraphQL::Query::ValidationPipeline#valid?'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/query.rb:373:in 'GraphQL::Query#valid?'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/analysis.rb:32:in 'block (2 levels) in GraphQL::Analysis.analyze_multiplex'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/analysis.rb:31:in 'Array#map'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/analysis.rb:31:in 'block in GraphQL::Analysis.analyze_multiplex'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/tracing/trace.rb:53:in 'GraphQL::Tracing::Trace#analyze_multiplex'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/analysis.rb:30:in 'GraphQL::Analysis.analyze_multiplex'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/execution/interpreter.rb:51:in 'block in GraphQL::Execution::Interpreter.run_all'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/tracing/trace.rb:64:in 'GraphQL::Tracing::Trace#execute_multiplex'
3form-api-1 | /api/app/graphql/app_schema.rb:6:in 'MultiplexCounterTrace#execute_multiplex'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/execution/interpreter.rb:42:in 'GraphQL::Execution::Interpreter.run_all'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/schema.rb:1639:in 'GraphQL::Schema.multiplex'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/schema.rb:1611:in 'GraphQL::Schema.execute_legacy'
3form-api-1 | /bundle/ruby/3.4.0/gems/graphql-2.6.2/lib/graphql/schema.rb:1590:in 'GraphQL::Schema.execute'
3form-api-1 | /api/app/controllers/graphql_controller.rb:19:in 'GraphqlController#execute'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_controller/metal/basic_implicit_render.rb:8:in 'ActionController::BasicImplicitRender#send_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/abstract_controller/base.rb:221:in 'AbstractController::Base#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_controller/metal/rendering.rb:199:in 'ActionController::Rendering#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/abstract_controller/callbacks.rb:267:in 'block in AbstractController::Callbacks#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/callbacks.rb:121:in 'block in ActiveSupport::Callbacks#run_callbacks'
3form-api-1 | /api/app/controllers/application_controller.rb:84:in 'ApplicationController#set_current_user'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/callbacks.rb:130:in 'block in ActiveSupport::Callbacks#run_callbacks'
3form-api-1 | /bundle/ruby/3.4.0/gems/actiontext-8.1.3/lib/action_text/rendering.rb:24:in 'ActionText::Rendering::ClassMethods#with_renderer'
3form-api-1 | /bundle/ruby/3.4.0/gems/actiontext-8.1.3/lib/action_text/engine.rb:72:in 'block (4 levels) in <class:Engine>'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/callbacks.rb:130:in 'BasicObject#instance_exec'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/callbacks.rb:130:in 'block in ActiveSupport::Callbacks#run_callbacks'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-rails-5.24.0/lib/sentry/rails/controller_transaction.rb:34:in 'block in Sentry::Rails::ControllerTransaction#sentry_around_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry/hub.rb:138:in 'Sentry::Hub#with_child_span'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry-ruby.rb:536:in 'Sentry.with_child_span'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-rails-5.24.0/lib/sentry/rails/controller_transaction.rb:18:in 'Sentry::Rails::ControllerTransaction#sentry_around_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/callbacks.rb:130:in 'block in ActiveSupport::Callbacks#run_callbacks'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/callbacks.rb:141:in 'ActiveSupport::Callbacks#run_callbacks'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/abstract_controller/callbacks.rb:266:in 'AbstractController::Callbacks#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_controller/metal/rescue.rb:36:in 'ActionController::Rescue#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_controller/metal/instrumentation.rb:76:in 'block in ActionController::Instrumentation#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/notifications.rb:210:in 'block in ActiveSupport::Notifications.instrument'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/notifications/instrumenter.rb:58:in 'ActiveSupport::Notifications::Instrumenter#instrument'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/notifications.rb:210:in 'ActiveSupport::Notifications.instrument'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_controller/metal/instrumentation.rb:75:in 'ActionController::Instrumentation#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_controller/metal/params_wrapper.rb:259:in 'ActionController::ParamsWrapper#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/activerecord-8.1.3/lib/active_record/railties/controller_runtime.rb:39:in 'ActiveRecord::Railties::ControllerRuntime#process_action'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/abstract_controller/base.rb:154:in 'AbstractController::Base#process'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionview-8.1.3/lib/action_view/rendering.rb:40:in 'ActionView::Rendering#process'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_controller/metal.rb:252:in 'ActionController::Metal#dispatch'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_controller/metal.rb:335:in 'ActionController::Metal.dispatch'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/routing/route_set.rb:65:in 'ActionDispatch::Routing::RouteSet::Dispatcher#dispatch'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/routing/route_set.rb:50:in 'ActionDispatch::Routing::RouteSet::Dispatcher#serve'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/journey/router.rb:35:in 'block in ActionDispatch::Journey::Router#serve'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/journey/router.rb:86:in 'block in ActionDispatch::Journey::Router#recognize'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/journey/router.rb:66:in 'Array#each'
3[12:22 PM]form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/journey/router.rb:66:in 'ActionDispatch::Journey::Router#recognize'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/journey/router.rb:31:in 'ActionDispatch::Journey::Router#serve'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/routing/route_set.rb:906:in 'ActionDispatch::Routing::RouteSet#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/railties-8.1.3/lib/rails/engine/lazy_route_set.rb:60:in 'Rails::Engine::LazyRouteSet#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/bullet-8.1.2/lib/bullet/rack.rb:21:in 'Bullet::Rack#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-3.2.6/lib/rack/tempfile_reaper.rb:20:in 'Rack::TempfileReaper#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-3.2.6/lib/rack/etag.rb:29:in 'Rack::ETag#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-3.2.6/lib/rack/conditional_get.rb:44:in 'Rack::ConditionalGet#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-3.2.6/lib/rack/head.rb:15:in 'Rack::Head#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/http/content_security_policy.rb:38:in 'ActionDispatch::ContentSecurityPolicy::Middleware#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-session-2.1.2/lib/rack/session/abstract/id.rb:274:in 'Rack::Session::Abstract::Persisted#context'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-session-2.1.2/lib/rack/session/abstract/id.rb:268:in 'Rack::Session::Abstract::Persisted#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/cookies.rb:708:in 'ActionDispatch::Cookies#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/activerecord-8.1.3/lib/active_record/migration.rb:671:in 'ActiveRecord::Migration::CheckPending#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/callbacks.rb:31:in 'block in ActionDispatch::Callbacks#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/callbacks.rb:101:in 'ActiveSupport::Callbacks#run_callbacks'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/callbacks.rb:30:in 'ActionDispatch::Callbacks#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/executor.rb:20:in 'ActionDispatch::Executor#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/actionable_exceptions.rb:18:in 'ActionDispatch::ActionableExceptions#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-rails-5.24.0/lib/sentry/rails/rescued_exception_interceptor.rb:14:in 'Sentry::Rails::RescuedExceptionInterceptor#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/better_errors-2.10.1/lib/better_errors/middleware.rb:62:in 'BetterErrors::Middleware#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/debug_exceptions.rb:31:in 'ActionDispatch::DebugExceptions#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry/rack/capture_exceptions.rb:30:in 'block (2 levels) in Sentry::Rack::CaptureExceptions#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry/hub.rb:306:in 'Sentry::Hub#with_session_tracking'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry-ruby.rb:430:in 'Sentry.with_session_tracking'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry/rack/capture_exceptions.rb:21:in 'block in Sentry::Rack::CaptureExceptions#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry/hub.rb:89:in 'Sentry::Hub#with_scope'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry-ruby.rb:410:in 'Sentry.with_scope'
3form-api-1 | /bundle/ruby/3.4.0/gems/sentry-ruby-5.24.0/lib/sentry/rack/capture_exceptions.rb:20:in 'Sentry::Rack::CaptureExceptions#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/show_exceptions.rb:32:in 'ActionDispatch::ShowExceptions#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/railties-8.1.3/lib/rails/rack/logger.rb:41:in 'Rails::Rack::Logger#call_app'
3form-api-1 | /bundle/ruby/3.4.0/gems/railties-8.1.3/lib/rails/rack/logger.rb:29:in 'Rails::Rack::Logger#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/remote_ip.rb:98:in 'ActionDispatch::RemoteIp#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/request_store-1.7.0/lib/request_store/middleware.rb:19:in 'RequestStore::Middleware#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/request_id.rb:34:in 'ActionDispatch::RequestId#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-3.2.6/lib/rack/method_override.rb:28:in 'Rack::MethodOverride#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-3.2.6/lib/rack/runtime.rb:24:in 'Rack::Runtime#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/activesupport-8.1.3/lib/active_support/cache/strategy/local_cache_middleware.rb:30:in 'ActiveSupport::Cache::Strategy::LocalCache::Middleware#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/executor.rb:20:in 'ActionDispatch::Executor#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/static.rb:27:in 'ActionDispatch::Static#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-3.2.6/lib/rack/sendfile.rb:131:in 'Rack::Sendfile#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/actionpack-8.1.3/lib/action_dispatch/middleware/host_authorization.rb:143:in 'ActionDispatch::HostAuthorization#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/rack-cors-3.0.0/lib/rack/cors.rb:102:in 'Rack::Cors#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/railties-8.1.3/lib/rails/engine.rb:534:in 'Rails::Engine#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/puma-6.6.0/lib/puma/configuration.rb:279:in 'Puma::Configuration::ConfigMiddleware#call'
3form-api-1 | /bundle/ruby/3.4.0/gems/puma-6.6.0/lib/puma/request.rb:99:in 'block in Puma::Request#handle_request'
3form-api-1 | /bundle/ruby/3.4.0/gems/puma-6.6.0/lib/puma/thread_pool.rb:390:in 'Puma::ThreadPool#with_force_shutdown'
3form-api-1 | /bundle/ruby/3.4.0/gems/puma-6.6.0/lib/puma/request.rb:98:in 'Puma::Request#handle_request'
3form-api-1 | /bundle/ruby/3.4.0/gems/puma-6.6.0/lib/puma/server.rb:472:in 'Puma::Server#process_client'
3form-api-1 | /bundle/ruby/3.4.0/gems/puma-6.6.0/lib/puma/server.rb:254:in 'block in Puma::Server#run'
3form-api-1 | /bundle/ruby/3.4.0/gems/puma-6.6.0/lib/puma/thread_pool.rb:167:in 'block in Puma::ThreadPool#spawn_thread'
3form-api-1 | Completed 500 Internal Server Error in 11824ms (Views: 1.2ms | ActiveRecord: 349.4ms (3 queries, 0 cached) | GC: 1447.1ms)
Additional context
all of our visibility checks stem from a single definition of visible?
def visible?(context)
return true if required_permission.blank?
context[:current_user]&.send("auth_#{required_permission}?".to_sym)
end
I'm happy to help debug this issue in anyway. For now we are just going to switch back to warden in our application, but definitely want to help resolve this for version 3.
Describe the bug
I've been trying to track down intermittent differences in visibility between Warden and Visibility. I "think" I have narrowed it down to the way Visibility traverses the schema. I could be way off here, but bear with me, and feel free to correct me if I am misunderstanding something.
Warden poplates a cumulative map from Schema.references_to. Visiblity does not use this cumulative map, instead it traverses the schema fresh starting from the root of each query. if at this time a type has not been created, it isn't attached to that schema graph and is unreachable and thus not visible. which would yield a "field does not exist" error message.
We are currently dynamically building types off of models using a gem we created called graphiform. The idea here is to make it easy and quick to expose model fields to the GraphQL layer without the boilerplate of building deeply nested types by hand. It allows us to quickly add new fields with very little code. The problem I believe we are seeing stems from the fact these types don't get built until the model classes themselves get autoloaded. this is only a problem therefore when not eager loading, which is very common in development.
In this specific use case depending on the order that models get autoloaded in Rails, and the specific query being utilized, this leads to the potential problem of fields that should be visible, not being visible.
Versions
graphqlversion: 2.6.2rails(or other framework): rails 8.1graphiform: 0.5.0GraphQL schema
Unfortunately, I haven't been able to narrow this down to a specific schema that causes the issue. It's seemingly random. I suspect it has to do with the way lazy-loading interacts with our dynamic type definition library, but again I can't seem to narrow down specific reproduction steps.
Steps to reproduce
with eager_load disabled run queries against graphql schema
Expected behavior
query resolves all fields and visibility returns the correct fields.
Actual behavior
either migration error for visibility migration mode is raised or a query with a field that should be visible will raise a field doesn't exist on type error.
Place full backtrace here (if a Ruby exception is involved):
Click to view exception backtrace
Additional context
all of our visibility checks stem from a single definition of visible?
I'm happy to help debug this issue in anyway. For now we are just going to switch back to warden in our application, but definitely want to help resolve this for version 3.