Class: GraphQL::Schema::Visibility::Profile

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

Overview

This class filters the types, fields, arguments, enum values, and directives in a schema based on the given context.

It’s like Warden, but has some differences:

Direct Known Subclasses

Migration

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name: nil, context:, schema:) ⇒ Profile

Returns a new instance of Profile.

[View source]

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/graphql/schema/visibility/profile.rb', line 34

def initialize(name: nil, context:, schema:)
  @name = name
  @context = context
  @schema = schema
  @all_types = {}
  @all_types_loaded = false
  @unvisited_types = []
  @all_directives = nil
  @cached_visible = Hash.new { |h, member| h[member] = @schema.visible?(member, @context) }.compare_by_identity

  @cached_visible_fields = Hash.new { |h, owner|
    h[owner] = Hash.new do |h2, field|
      h2[field] = visible_field_for(owner, field)
    end.compare_by_identity
  }.compare_by_identity

  @cached_visible_arguments = Hash.new do |h, arg|
    h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
      true
    else
      false
    end
  end.compare_by_identity

  @cached_parent_fields = Hash.new do |h, type|
    h[type] = Hash.new do |h2, field_name|
      h2[field_name] = type.get_field(field_name, @context)
    end
  end.compare_by_identity

  @cached_parent_arguments = Hash.new do |h, arg_owner|
    h[arg_owner] = Hash.new do |h2, arg_name|
      h2[arg_name] = arg_owner.get_argument(arg_name, @context)
    end
  end.compare_by_identity

  @cached_possible_types = Hash.new { |h, type| h[type] = possible_types_for(type) }.compare_by_identity

  @cached_enum_values = Hash.new do |h, enum_t|
    values = non_duplicate_items(enum_t.enum_values(@context), @cached_visible)
    if values.size == 0
      raise GraphQL::Schema::Enum::MissingValuesError.new(enum_t)
    end
    h[enum_t] = values
  end.compare_by_identity

  @cached_fields = Hash.new do |h, owner|
    h[owner] = non_duplicate_items(owner.all_field_definitions.each(&:ensure_loaded), @cached_visible_fields[owner])
  end.compare_by_identity

  @cached_arguments = Hash.new do |h, owner|
    h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
  end.compare_by_identity

  @loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
end

Instance Attribute Details

#nameSymbol? (readonly)

Returns:

  • (Symbol, nil)

32
33
34
# File 'lib/graphql/schema/visibility/profile.rb', line 32

def name
  @name
end

Class Method Details

.from_context(ctx, schema) ⇒ Schema::Visibility::Profile

[View source]

17
18
19
20
21
22
23
# File 'lib/graphql/schema/visibility/profile.rb', line 17

def self.from_context(ctx, schema)
  if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
    types
  else
    schema.visibility.profile_for(ctx)
  end
end

.null_profile(context:, schema:) ⇒ Object

[View source]

25
26
27
28
29
# File 'lib/graphql/schema/visibility/profile.rb', line 25

def self.null_profile(context:, schema:)
  profile = self.new(name: "NullProfile", context: context, schema: schema)
  profile.instance_variable_set(:@cached_visible, Hash.new { |k, v| k[v] = true }.compare_by_identity)
  profile
end

Instance Method Details

#all_typesObject

[View source]

225
226
227
228
# File 'lib/graphql/schema/visibility/profile.rb', line 225

def all_types
  load_all_types
  @all_types.values
end

#all_types_hObject

[View source]

230
231
232
233
# File 'lib/graphql/schema/visibility/profile.rb', line 230

def all_types_h
  load_all_types
  @all_types
end

#argument(owner, arg_name) ⇒ Object

[View source]

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/graphql/schema/visibility/profile.rb', line 178

def argument(owner, arg_name)
  arg = @cached_parent_arguments[owner][arg_name]
  if arg.is_a?(Array)
    visible_arg = nil
    arg.each do |arg_defn|
      if @cached_visible_arguments[arg_defn]
        if visible_arg.nil?
          visible_arg = arg_defn
        else
          raise_duplicate_definition(visible_arg, arg_defn)
        end
      end
    end
    visible_arg
  else
    if arg && @cached_visible_arguments[arg]
      arg
    else
      nil
    end
  end
end

#arguments(owner) ⇒ Object

[View source]

174
175
176
# File 'lib/graphql/schema/visibility/profile.rb', line 174

def arguments(owner)
  @cached_arguments[owner]
end

#directive_exists?(dir_name) ⇒ Boolean

Returns:

  • (Boolean)
[View source]

239
240
241
# File 'lib/graphql/schema/visibility/profile.rb', line 239

def directive_exists?(dir_name)
  directives.any? { |d| d.graphql_name == dir_name }
end

#directivesObject

[View source]

243
244
245
246
247
# File 'lib/graphql/schema/visibility/profile.rb', line 243

def directives
  @all_directives ||= @schema.visibility.all_directives.select { |dir|
    @cached_visible[dir] && @schema.visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) }
  }
end

#enum_values(owner) ⇒ Object

[View source]

235
236
237
# File 'lib/graphql/schema/visibility/profile.rb', line 235

def enum_values(owner)
  @cached_enum_values[owner]
end

#field(owner, field_name) ⇒ Object

[View source]

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/graphql/schema/visibility/profile.rb', line 140

def field(owner, field_name)
  f = if owner.kind.fields? && (field = @cached_parent_fields[owner][field_name])
    field
  elsif owner == query_root && (entry_point_field = @schema.introspection_system.entry_point(name: field_name))
    entry_point_field
  elsif (dynamic_field = @schema.introspection_system.dynamic_field(name: field_name))
    dynamic_field
  else
    nil
  end
  if f.is_a?(Array)
    visible_f = nil
    f.each do |f_defn|
      if @cached_visible_fields[owner][f_defn]

        if visible_f.nil?
          visible_f = f_defn
        else
          raise_duplicate_definition(visible_f, f_defn)
        end
      end
    end
    visible_f&.ensure_loaded
  elsif f && @cached_visible_fields[owner][f.ensure_loaded]
    f
  else
    nil
  end
end

#field_on_visible_interface?(field, owner) ⇒ Boolean

Returns:

  • (Boolean)
[View source]

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/graphql/schema/visibility/profile.rb', line 91

def field_on_visible_interface?(field, owner)
  ints = owner.interface_type_memberships.map(&:abstract_type)
  field_name = field.graphql_name
  filtered_ints = interfaces(owner)
  any_interface_has_field = false
  any_interface_has_visible_field = false
  ints.each do |int_t|
    if (_int_f_defn = @cached_parent_fields[int_t][field_name])
      any_interface_has_field = true

      if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field]
        any_interface_has_visible_field = true
        break
      end
    end
  end

  if any_interface_has_field
    any_interface_has_visible_field
  else
    true
  end
end

#fields(owner) ⇒ Object

[View source]

170
171
172
# File 'lib/graphql/schema/visibility/profile.rb', line 170

def fields(owner)
  @cached_fields[owner]
end

#interfaces(obj_or_int_type) ⇒ Object

[View source]

205
206
207
208
209
210
211
# File 'lib/graphql/schema/visibility/profile.rb', line 205

def interfaces(obj_or_int_type)
  ints = obj_or_int_type.interface_type_memberships
    .select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] }
    .map!(&:abstract_type)
  ints.uniq! # Remove any duplicate interfaces implemented via other interfaces
  ints
end

#loadable?(t, _ctx) ⇒ Boolean

Returns:

  • (Boolean)
[View source]

249
250
251
252
# File 'lib/graphql/schema/visibility/profile.rb', line 249

def loadable?(t, _ctx)
  load_all_types
  !@all_types[t.graphql_name] && @cached_visible[t]
end

#loadable_possible_types(t, _ctx) ⇒ Object

[View source]

254
255
256
# File 'lib/graphql/schema/visibility/profile.rb', line 254

def loadable_possible_types(t, _ctx)
  @loadable_possible_types[t]
end

#loaded_typesObject

[View source]

258
259
260
# File 'lib/graphql/schema/visibility/profile.rb', line 258

def loaded_types
  @all_types.values
end

#mutation_rootObject

[View source]

217
218
219
# File 'lib/graphql/schema/visibility/profile.rb', line 217

def mutation_root
  ((t = @schema.mutation) && @cached_visible[t]) ? t : nil
end

#possible_types(type) ⇒ Object

[View source]

201
202
203
# File 'lib/graphql/schema/visibility/profile.rb', line 201

def possible_types(type)
  @cached_possible_types[type]
end

#query_rootObject

[View source]

213
214
215
# File 'lib/graphql/schema/visibility/profile.rb', line 213

def query_root
  ((t = @schema.query) && @cached_visible[t]) ? t : nil
end

#reachable_type?(type_name) ⇒ Boolean

Returns:

  • (Boolean)
[View source]

262
263
264
265
# File 'lib/graphql/schema/visibility/profile.rb', line 262

def reachable_type?(type_name)
  load_all_types
  !!@all_types[type_name]
end

#subscription_rootObject

[View source]

221
222
223
# File 'lib/graphql/schema/visibility/profile.rb', line 221

def subscription_root
  ((t = @schema.subscription) && @cached_visible[t]) ? t : nil
end

#type(type_name) ⇒ Object

[View source]

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/graphql/schema/visibility/profile.rb', line 115

def type(type_name)
  t = @schema.visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
  if t
    if t.is_a?(Array)
      vis_t = nil
      t.each do |t_defn|
        if @cached_visible[t_defn] && referenced?(t_defn)
          if vis_t.nil?
            vis_t = t_defn
          else
            raise_duplicate_definition(vis_t, t_defn)
          end
        end
      end
      vis_t
    else
      if t && @cached_visible[t] && referenced?(t)
        t
      else
        nil
      end
    end
  end
end

#visible_enum_value?(enum_value, _ctx = nil) ⇒ Boolean

Returns:

  • (Boolean)
[View source]

267
268
269
# File 'lib/graphql/schema/visibility/profile.rb', line 267

def visible_enum_value?(enum_value, _ctx = nil)
  @cached_visible[enum_value]
end