Skip to content

Commit

Permalink
Merge pull request #4998 from rmosolgo/types-as-you-go
Browse files Browse the repository at this point in the history
Add an alternative type visiblity/filtering system
  • Loading branch information
rmosolgo authored Jul 12, 2024
2 parents 337eaa3 + 5a80b37 commit a3aee8c
Show file tree
Hide file tree
Showing 60 changed files with 775 additions and 190 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
- gemfile: gemfiles/rails_7.0.gemfile
ruby: 3.1
- gemfile: gemfiles/rails_master.gemfile
ruby: 3.1
ruby: 3.2
- gemfile: gemfiles/rails_7.0.gemfile
ruby: 3.2
- gemfile: gemfiles/rails_7.1.gemfile
Expand Down
2 changes: 1 addition & 1 deletion lib/graphql/analysis/field_usage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def extract_deprecated_arguments(argument_values)
end

def extract_deprecated_enum_value(enum_type, value)
enum_value = @query.warden.enum_values(enum_type).find { |ev| ev.value == value }
enum_value = @query.types.enum_values(enum_type).find { |ev| ev.value == value }
if enum_value&.deprecation_reason
@used_deprecated_enum_values << enum_value.path
end
Expand Down
6 changes: 3 additions & 3 deletions lib/graphql/analysis/query_complexity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def merged_max_complexity_for_scopes(query, scopes)
possible_scope_types.keys.each do |possible_scope_type|
next unless possible_scope_type.kind.abstract?

query.possible_types(possible_scope_type).each do |impl_type|
query.types.possible_types(possible_scope_type).each do |impl_type|
possible_scope_types[impl_type] ||= true
end
possible_scope_types.delete(possible_scope_type)
Expand All @@ -123,8 +123,8 @@ def merged_max_complexity_for_scopes(query, scopes)
def types_intersect?(query, a, b)
return true if a == b

a_types = query.possible_types(a)
query.possible_types(b).any? { |t| a_types.include?(t) }
a_types = query.types.possible_types(a)
query.types.possible_types(b).any? { |t| a_types.include?(t) }
end

# A hook which is called whenever a field's max complexity is calculated.
Expand Down
13 changes: 7 additions & 6 deletions lib/graphql/analysis/visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def initialize(query:, analyzers:)
@rescued_errors = []
@query = query
@schema = query.schema
@types = query.types
@response_path = []
@skip_stack = [false]
super(query.selected_operation)
Expand Down Expand Up @@ -131,7 +132,7 @@ def on_field(node, parent)
@response_path.push(node.alias || node.name)
parent_type = @object_types.last
# This could be nil if the previous field wasn't found:
field_definition = parent_type && @schema.get_field(parent_type, node.name, @query.context)
field_definition = parent_type && @types.field(parent_type, node.name)
@field_definitions.push(field_definition)
if !field_definition.nil?
next_object_type = field_definition.type.unwrap
Expand Down Expand Up @@ -167,14 +168,14 @@ def on_argument(node, parent)
argument_defn = if (arg = @argument_definitions.last)
arg_type = arg.type.unwrap
if arg_type.kind.input_object?
arg_type.get_argument(node.name, @query.context)
@types.argument(arg_type, node.name)
else
nil
end
elsif (directive_defn = @directive_definitions.last)
directive_defn.get_argument(node.name, @query.context)
@types.argument(directive_defn, node.name)
elsif (field_defn = @field_definitions.last)
field_defn.get_argument(node.name, @query.context)
@types.argument(field_defn, node.name)
else
nil
end
Expand Down Expand Up @@ -245,7 +246,7 @@ def enter_fragment_spread_inline(fragment_spread)
fragment_def = query.fragments[fragment_spread.name]

object_type = if fragment_def.type
@query.warden.get_type(fragment_def.type.name)
@types.type(fragment_def.type.name)
else
object_types.last
end
Expand All @@ -268,7 +269,7 @@ def skip?(ast_node)

def on_fragment_with_type(node)
object_type = if node.type
@query.warden.get_type(node.type.name)
@types.type(node.type.name)
else
@object_types.last
end
Expand Down
12 changes: 6 additions & 6 deletions lib/graphql/execution/interpreter/runtime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ def gather_selections(owner_object, owner_type, selections, selections_to_run =
case node
when GraphQL::Language::Nodes::InlineFragment
if node.type
type_defn = schema.get_type(node.type.name, context)
type_defn = query.types.type(node.type.name)

if query.warden.possible_types(type_defn).include?(owner_type)
if query.types.possible_types(type_defn).include?(owner_type)
result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
if !result.equal?(next_selections)
selections_to_run = result
Expand All @@ -177,8 +177,8 @@ def gather_selections(owner_object, owner_type, selections, selections_to_run =
end
when GraphQL::Language::Nodes::FragmentSpread
fragment_def = query.fragments[node.name]
type_defn = query.get_type(fragment_def.type.name)
if query.warden.possible_types(type_defn).include?(owner_type)
type_defn = query.types.type(fragment_def.type.name)
if query.types.possible_types(type_defn).include?(owner_type)
result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
if !result.equal?(next_selections)
selections_to_run = result
Expand Down Expand Up @@ -245,7 +245,7 @@ def evaluate_selection(result_name, field_ast_nodes_or_ast_node, selections_resu
end
field_name = ast_node.name
owner_type = selections_result.graphql_result_type
field_defn = query.warden.get_field(owner_type, field_name)
field_defn = query.types.field(owner_type, field_name)

# Set this before calling `run_with_directives`, so that the directive can have the latest path
runtime_state = get_current_runtime_state
Expand Down Expand Up @@ -579,7 +579,7 @@ def continue_field(value, owner_type, field, current_type, ast_node, next_select
resolved_value = value
end

possible_types = query.possible_types(current_type)
possible_types = query.types.possible_types(current_type)
if !possible_types.include?(resolved_type)
parent_type = field.owner_type
err_class = current_type::UnresolvedTypeError
Expand Down
20 changes: 10 additions & 10 deletions lib/graphql/execution/lookahead.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,16 @@ def selected?
def selection(field_name, selected_type: @selected_type, arguments: nil)
next_field_defn = case field_name
when String
@query.get_field(selected_type, field_name)
@query.types.field(selected_type, field_name)
when Symbol
# Try to avoid the `.to_s` below, if possible
all_fields = if selected_type.kind.fields?
@query.warden.fields(selected_type)
@query.types.fields(selected_type)
else
# Handle unions by checking possible
@query.warden
@query.types
.possible_types(selected_type)
.map { |t| @query.warden.fields(t) }
.map { |t| @query.types.fields(t) }
.tap(&:flatten!)
end

Expand All @@ -128,7 +128,7 @@ def selection(field_name, selected_type: @selected_type, arguments: nil)
# Symbol#name is only present on 3.0+
sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s
guessed_name = Schema::Member::BuildType.camelize(sym_s)
@query.get_field(selected_type, guessed_name)
@query.types.field(selected_type, guessed_name)
end
end
lookahead_for_selection(next_field_defn, selected_type, arguments)
Expand All @@ -144,7 +144,7 @@ def alias_selection(alias_name, selected_type: @selected_type, arguments: nil)
alias_node = lookup_alias_node(ast_nodes, alias_name)
return NULL_LOOKAHEAD unless alias_node

next_field_defn = @query.get_field(selected_type, alias_node.name)
next_field_defn = @query.types.field(selected_type, alias_node.name)

alias_arguments = @query.arguments_for(alias_node, next_field_defn)
if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments)
Expand Down Expand Up @@ -183,7 +183,7 @@ def selections(arguments: nil)

subselections_by_type.each do |type, ast_nodes_by_response_key|
ast_nodes_by_response_key.each do |response_key, ast_nodes|
field_defn = @query.get_field(type, ast_nodes.first.name)
field_defn = @query.types.field(type, ast_nodes.first.name)
lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
subselections.push(lookahead)
end
Expand Down Expand Up @@ -266,7 +266,7 @@ def find_selections(subselections_by_type, selections_on_type, selected_type, as
elsif arguments.nil? || arguments.empty?
selections_on_type[response_key] = [ast_selection]
else
field_defn = @query.get_field(selected_type, ast_selection.name)
field_defn = @query.types.field(selected_type, ast_selection.name)
if arguments_match?(arguments, field_defn, ast_selection)
selections_on_type[response_key] = [ast_selection]
end
Expand All @@ -276,14 +276,14 @@ def find_selections(subselections_by_type, selections_on_type, selected_type, as
subselections_on_type = selections_on_type
if (t = ast_selection.type)
# Assuming this is valid, that `t` will be found.
on_type = @query.get_type(t.name)
on_type = @query.types.type(t.name)
subselections_on_type = subselections_by_type[on_type] ||= {}
end
find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
when GraphQL::Language::Nodes::FragmentSpread
frag_defn = lookup_fragment(ast_selection)
# Again, assuming a valid AST
on_type = @query.get_type(frag_defn.type.name)
on_type = @query.types.type(frag_defn.type.name)
subselections_on_type = subselections_by_type[on_type] ||= {}
find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
else
Expand Down
2 changes: 1 addition & 1 deletion lib/graphql/introspection/directive_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DirectiveType < Introspection::BaseObject
field :is_repeatable, Boolean, method: :repeatable?

def args(include_deprecated:)
args = @context.warden.arguments(@object)
args = @context.types.arguments(@object)
args = args.reject(&:deprecation_reason) unless include_deprecated
args
end
Expand Down
4 changes: 2 additions & 2 deletions lib/graphql/introspection/entry_points.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def __schema
end

def __type(name:)
if context.warden.reachable_type?(name)
context.warden.get_type(name)
if context.types.reachable_type?(name)
context.types.type(name)
elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
type
else
Expand Down
2 changes: 1 addition & 1 deletion lib/graphql/introspection/field_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def is_deprecated
end

def args(include_deprecated:)
args = @context.warden.arguments(@object)
args = @context.types.arguments(@object)
args = args.reject(&:deprecation_reason) unless include_deprecated
args
end
Expand Down
16 changes: 13 additions & 3 deletions lib/graphql/introspection/schema_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ def schema_description
end

def types
types = context.warden.reachable_types + context.schema.extra_types
query_types = context.types.all_types
types = query_types + context.schema.extra_types
types.sort_by!(&:graphql_name)
types
end
Expand All @@ -38,13 +39,22 @@ def subscription_type
end

def directives
@context.warden.directives
@context.types.directives
end

private

def permitted_root_type(op_type)
@context.warden.root_type_for_operation(op_type)
case op_type
when "query"
@context.types.query_root
when "mutation"
@context.types.mutation_root
when "subcription"
@context.types.subscription_root
else
nil
end
end
end
end
Expand Down
10 changes: 5 additions & 5 deletions lib/graphql/introspection/type_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def enum_values(include_deprecated:)
if !@object.kind.enum?
nil
else
enum_values = @context.warden.enum_values(@object)
enum_values = @context.types.enum_values(@object)

if !include_deprecated
enum_values = enum_values.select {|f| !f.deprecation_reason }
Expand All @@ -64,15 +64,15 @@ def enum_values(include_deprecated:)

def interfaces
if @object.kind.object? || @object.kind.interface?
@context.warden.interfaces(@object).sort_by(&:graphql_name)
@context.types.interfaces(@object).sort_by(&:graphql_name)
else
nil
end
end

def input_fields(include_deprecated:)
if @object.kind.input_object?
args = @context.warden.arguments(@object)
args = @context.types.arguments(@object)
args = args.reject(&:deprecation_reason) unless include_deprecated
args
else
Expand All @@ -82,7 +82,7 @@ def input_fields(include_deprecated:)

def possible_types
if @object.kind.abstract?
@context.warden.possible_types(@object).sort_by(&:graphql_name)
@context.types.possible_types(@object).sort_by(&:graphql_name)
else
nil
end
Expand All @@ -92,7 +92,7 @@ def fields(include_deprecated:)
if !@object.kind.fields?
nil
else
fields = @context.warden.fields(@object)
fields = @context.types.fields(@object)
if !include_deprecated
fields = fields.select {|f| !f.deprecation_reason }
end
Expand Down
Loading

0 comments on commit a3aee8c

Please sign in to comment.