-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
finder.rb
155 lines (123 loc) · 4.82 KB
/
finder.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# frozen_string_literal: true
module GraphQL
class Schema
# Find schema members using string paths
#
# @example Finding object types
# MySchema.find("SomeObjectType")
#
# @example Finding fields
# MySchema.find("SomeObjectType.myField")
#
# @example Finding arguments
# MySchema.find("SomeObjectType.myField.anArgument")
#
# @example Finding directives
# MySchema.find("@include")
#
class Finder
class MemberNotFoundError < ArgumentError; end
def initialize(schema)
@schema = schema
end
def find(path)
path = path.split(".")
type_or_directive = path.shift
if type_or_directive.start_with?("@")
directive = schema.directives[type_or_directive[1..-1]]
if directive.nil?
raise MemberNotFoundError, "Could not find directive `#{type_or_directive}` in schema."
end
return directive if path.empty?
find_in_directive(directive, path: path)
else
type = schema.get_type(type_or_directive) # rubocop:disable Development/ContextIsPassedCop -- build-time
if type.nil?
raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
end
return type if path.empty?
find_in_type(type, path: path)
end
end
private
attr_reader :schema
def find_in_directive(directive, path:)
argument_name = path.shift
argument = directive.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
if argument.nil?
raise MemberNotFoundError, "Could not find argument `#{argument_name}` on directive #{directive}."
end
argument
end
def find_in_type(type, path:)
case type.kind.name
when "OBJECT"
find_in_fields_type(type, kind: "object", path: path)
when "INTERFACE"
find_in_fields_type(type, kind: "interface", path: path)
when "INPUT_OBJECT"
find_in_input_object(type, path: path)
when "UNION"
# Error out if path that was provided is too long
# i.e UnionType.PossibleType.aField
# Use PossibleType.aField instead.
if invalid = path.first
raise MemberNotFoundError, "Cannot select union possible type `#{invalid}`. Select the type directly instead."
end
when "ENUM"
find_in_enum_type(type, path: path)
else
raise "Unexpected find_in_type: #{type.inspect} (#{path})"
end
end
def find_in_fields_type(type, kind:, path:)
field_name = path.shift
field = schema.get_field(type, field_name)
if field.nil?
raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type.graphql_name}`."
end
return field if path.empty?
find_in_field(field, path: path)
end
def find_in_field(field, path:)
argument_name = path.shift
argument = field.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
if argument.nil?
raise MemberNotFoundError, "Could not find argument `#{argument_name}` on field `#{field.name}`."
end
# Error out if path that was provided is too long
# i.e Type.field.argument.somethingBad
if invalid = path.first
raise MemberNotFoundError, "Cannot select member `#{invalid}` on a field."
end
argument
end
def find_in_input_object(input_object, path:)
field_name = path.shift
input_field = input_object.get_argument(field_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
if input_field.nil?
raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
end
# Error out if path that was provided is too long
# i.e InputType.inputField.bad
if invalid = path.first
raise MemberNotFoundError, "Cannot select member `#{invalid}` on an input field."
end
input_field
end
def find_in_enum_type(enum_type, path:)
value_name = path.shift
enum_value = enum_type.enum_values.find { |v| v.graphql_name == value_name } # rubocop:disable Development/ContextIsPassedCop -- build-time, not runtime
if enum_value.nil?
raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
end
# Error out if path that was provided is too long
# i.e Enum.VALUE.wat
if invalid = path.first
raise MemberNotFoundError, "Cannot select member `#{invalid}` on an enum value."
end
enum_value
end
end
end
end