Class: GraphQL::Schema::Visibility::Migration

Inherits:
Subset
  • Object
show all
Defined in:
lib/graphql/schema/visibility/migration.rb

Overview

You can use this to see how Warden and Subset handle .visible? differently in your schema.

It runs the same method on both implementations and raises an error when the results diverge.

To fix the error, modify your schema so that both implementations return the same thing. Or, open an issue on GitHub to discuss the difference.

This plugin adds overhead to runtime and may cause unexpected crashes – don’t use it in production!

This plugin adds two keys to context when running:

  • visibility_migration_running: true
  • For the Warden which it instantiates, it adds visibility_migration_warden_running: true.

Use those keys to modify your visible? behavior as needed.

Also, in a pinch, you can set skip_visibility_migration_error: true in context to turn off this behavior per-query. (In that case, it uses Subset directly.)

Examples:

Adding this plugin


use GraphQL::Schema::Visibility::Migration

Defined Under Namespace

Classes: RuntimeTypesMismatchError

Constant Summary collapse

PUBLIC_SUBSET_METHODS =
[
  :enum_values,
  :interfaces,
  :all_types,
  :fields,
  :loadable?,
  :type,
  :arguments,
  :argument,
  :directive_exists?,
  :directives,
  :field,
  :query_root,
  :mutation_root,
  :possible_types,
  :subscription_root,
  :reachable_type?
]

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Subset

#all_types, #all_types_h, #argument, #arguments, #directive_exists?, #directives, #enum_values, #field, #field_on_visible_interface?, #fields, from_context, #interfaces, #loadable?, #mutation_root, pass_thru, #possible_types, #query_root, #reachable_type?, #subscription_root, #type

Constructor Details

#initialize(context:, schema:) ⇒ Migration

Returns a new instance of Migration.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/graphql/schema/visibility/migration.rb', line 83

def initialize(context:, schema:)
  @skip_error = context[:skip_visibility_migration_error]
  context[:visibility_migration_running] = true
  @subset_types = GraphQL::Schema::Visibility::Subset.new(context: context, schema: schema)
  if !@skip_error
    warden_ctx_vals = context.to_h.dup
    warden_ctx_vals[:visibility_migration_warden_running] = true
    if defined?(schema::WardenCompatSchema)
      warden_schema = schema::WardenCompatSchema
    else
      warden_schema = Class.new(schema)
      warden_schema.use_schema_visibility = false
      # TODO public API
      warden_schema.send(:add_type_and_traverse, [warden_schema.query, warden_schema.mutation, warden_schema.subscription].compact, root: true)
      warden_schema.send(:add_type_and_traverse, warden_schema.directives.values + warden_schema.orphan_types, root: false)
    end
    warden_ctx = GraphQL::Query::Context.new(query: context.query, values: warden_ctx_vals)
    example_warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx)
    @warden_types = example_warden.schema_subset
    warden_ctx.warden = example_warden
    warden_ctx.types = @warden_types
  end
end

Class Method Details

.use(schema) ⇒ Object



30
31
32
# File 'lib/graphql/schema/visibility/migration.rb', line 30

def self.use(schema)
  schema.subset_class = self
end

Instance Method Details

#call_method_and_compare(method, args) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/graphql/schema/visibility/migration.rb', line 136

def call_method_and_compare(method, args)
  res_1 = @subset_types.public_send(method, *args)
  if @skip_error
    return res_1
  end

  res_2 = @warden_types.public_send(method, *args)
  normalized_res_1 = res_1.is_a?(Array) ? Set.new(res_1) : res_1
  normalized_res_2 = res_2.is_a?(Array) ? Set.new(res_2) : res_2
  if !equivalent_schema_members?(normalized_res_1, normalized_res_2)
    # Raise the errors with the orignally returned values:
    err = RuntimeTypesMismatchError.new(method, res_2, res_1, args)
    raise err
  else
    res_1
  end
end

#equivalent_schema_members?(member1, member2) ⇒ Boolean

Returns:

  • (Boolean)


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/graphql/schema/visibility/migration.rb', line 154

def equivalent_schema_members?(member1, member2)
  if member1.class != member2.class
    return false
  end

  case member1
  when Set
    member1_array = member1.to_a.sort_by(&:graphql_name)
    member2_array = member2.to_a.sort_by(&:graphql_name)
    member1_array.each_with_index do |inner_member1, idx|
      inner_member2 = member2_array[idx]
      equivalent_schema_members?(inner_member1, inner_member2)
    end
  when GraphQL::Schema::Field
    member1.ensure_loaded
    member2.ensure_loaded
    if member1.introspection? && member2.introspection?
      member1.inspect == member2.inspect
    else
      member1 == member2
    end
  when Module
    if member1.introspection? && member2.introspection?
      member1.graphql_name == member2.graphql_name
    else
      member1 == member2
    end
  else
    member1 == member2
  end
end

#loaded_typesObject



107
108
109
# File 'lib/graphql/schema/visibility/migration.rb', line 107

def loaded_types
  @subset_types.loaded_types
end