graphql-ruby
graphql-ruby copied to clipboard
ObjectCache#private_context_fingerprint_for called for public: true fields/objects
Describe the bug
GraphQL Enterprise ObjectCache calls private_context_fingerprint_for method for fields configured with cacheable(public: true), causing crashes when no user authentication context is present. Public cached fields should not require user context or trigger private fingerprinting logic.
Versions
graphql version: 2.5.6
graphql-pro version: 1.29.7
graphql-enterprise version: 1.5.7
rails (or other framework): 7.2.2
ruby: 3.1.6
other applicable versions (graphql-batch, etc)
GraphQL schema
Include relevant types and fields (in Ruby is best, in GraphQL IDL is ok). Any custom extensions, etc?
class GraphqlSchema < GraphQL::Schema
default_max_page_size 50
mutation Types::MutationType
query Types::QueryType
use GraphQL::Enterprise::ObjectCache, redis: Redis.new(url: Config.redis_url)
def self.private_context_fingerprint_for(context)
current_user = context[:current_user]
if current_user.nil?
# This should never happen, but just in case:
raise("Invariant: No current_user in context! Can't create a private context fingerprint")
end
"user:#{current_user.id}:#{current_user.updated_at.to_i}"
end
....
end
class BaseObject < GraphQL::Schema::Object
include ActionPolicy::GraphQL::Behaviour
include GraphQL::Enterprise::ObjectCache::ObjectIntegration
cacheable(public: true)
end
class BaseField < GraphQL::Schema::Field
include GraphQL::Enterprise::ObjectCache::FieldIntegration
cacheable(public: true)
end
class Types::QueryType < Types::BaseObject
graphql_name "Query"
field :locations,
[Types::LocationType],
null: false,
description: "nearby locations",
oauth: "public",
cacheable: {
public: true
} do
argument :coordinates, Types::LngLatType, "coordinates for relative results", required: true
end
end
class Types::LocationType < Types::BaseObject
graphql_name "Location"
description "location"
cacheable(public: true)
implements GraphQL::Types::Relay::Node
field :address,
Types::AddressType,
null: false,
description: "address of location",
preload: :address,
cacheable: {
public: true
}
end
GraphQL query
Example GraphQL query and response (if query execution is involved)
query {
locations(coordinates: [-122.4194, 37.7749]) {
address {
street
}
}
}
error
Steps to reproduce
Add cacheable(public: true) and query any public query
Expected behavior
Public cached fields should not require user context and should not trigger private_context_fingerprint_for method.
Actual behavior
Public cached fields trigger private_context_fingerprint_for method.
Place full backtrace here (if a Ruby exception is involved):
Click to view exception backtrace
Failure/Error: raise("Invariant: No current_user in context! Can't create a private context fingerprint")
RuntimeError:
Invariant: No current_user in context! Can't create a private context fingerprint
# ./app/graphql/graphql_schema.rb:88:in `private_context_fingerprint_for'
Additional context
When cacheable(false) is set on BaseField/BaseObject, individual fields/objects cannot easily override this to enable caching. This makes it difficult to incrementally adopt caching in existing projects with many fields, as the base-level false setting appears to take precedence over field-specific cacheable: true configurations.
Expected Behavior: Field-level cacheable: true should override base-level cacheable(false)
P.S. Not sure, should I create separate issue for this or it's planned?
Thanks in advance!