rails-graphql 1.0.0.beta → 1.0.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/gql_parser.c +1 -16
- data/ext/gql_parser.h +21 -0
- data/ext/shared.c +0 -5
- data/ext/shared.h +6 -6
- data/lib/generators/graphql/channel_generator.rb +27 -0
- data/lib/generators/graphql/controller_generator.rb +9 -4
- data/lib/generators/graphql/install_generator.rb +49 -0
- data/lib/generators/graphql/schema_generator.rb +9 -4
- data/lib/generators/graphql/templates/channel.erb +7 -0
- data/lib/generators/graphql/templates/config.rb +97 -0
- data/lib/generators/graphql/templates/controller.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -3
- data/lib/gql_parser.so +0 -0
- data/lib/rails/graphql/alternative/field_set.rb +12 -0
- data/lib/rails/graphql/alternative/query.rb +13 -8
- data/lib/rails/graphql/alternative/subscription.rb +2 -1
- data/lib/rails/graphql/alternative.rb +4 -0
- data/lib/rails/graphql/argument.rb +5 -3
- data/lib/rails/graphql/callback.rb +10 -8
- data/lib/rails/graphql/collectors/hash_collector.rb +12 -1
- data/lib/rails/graphql/collectors/json_collector.rb +21 -0
- data/lib/rails/graphql/config.rb +86 -59
- data/lib/rails/graphql/directive/include_directive.rb +0 -1
- data/lib/rails/graphql/directive/skip_directive.rb +0 -1
- data/lib/rails/graphql/directive/specified_by_directive.rb +24 -0
- data/lib/rails/graphql/directive.rb +31 -25
- data/lib/rails/graphql/event.rb +7 -6
- data/lib/rails/graphql/field/authorized_field.rb +0 -5
- data/lib/rails/graphql/field/input_field.rb +0 -5
- data/lib/rails/graphql/field/mutation_field.rb +5 -6
- data/lib/rails/graphql/field/output_field.rb +13 -2
- data/lib/rails/graphql/field/proxied_field.rb +6 -6
- data/lib/rails/graphql/field/resolved_field.rb +1 -1
- data/lib/rails/graphql/field/subscription_field.rb +35 -52
- data/lib/rails/graphql/field/typed_field.rb +26 -2
- data/lib/rails/graphql/field.rb +20 -19
- data/lib/rails/graphql/global_id.rb +5 -1
- data/lib/rails/graphql/helpers/inherited_collection/array.rb +1 -0
- data/lib/rails/graphql/helpers/inherited_collection/base.rb +3 -1
- data/lib/rails/graphql/helpers/inherited_collection/hash.rb +2 -1
- data/lib/rails/graphql/helpers/registerable.rb +1 -1
- data/lib/rails/graphql/helpers/with_arguments.rb +3 -2
- data/lib/rails/graphql/helpers/with_assignment.rb +5 -5
- data/lib/rails/graphql/helpers/with_callbacks.rb +3 -3
- data/lib/rails/graphql/helpers/with_description.rb +10 -8
- data/lib/rails/graphql/helpers/with_directives.rb +5 -1
- data/lib/rails/graphql/helpers/with_events.rb +1 -0
- data/lib/rails/graphql/helpers/with_fields.rb +30 -24
- data/lib/rails/graphql/helpers/with_name.rb +3 -2
- data/lib/rails/graphql/helpers/with_schema_fields.rb +75 -51
- data/lib/rails/graphql/introspection.rb +1 -1
- data/lib/rails/graphql/railtie.rb +3 -2
- data/lib/rails/graphql/railties/app/base_channel.rb +10 -0
- data/lib/rails/graphql/railties/app/base_controller.rb +12 -0
- data/lib/rails/graphql/railties/app/views/_cable.js.erb +56 -0
- data/lib/rails/graphql/railties/app/views/_fetch.js.erb +20 -0
- data/lib/rails/graphql/railties/app/views/graphiql.html.erb +101 -0
- data/lib/rails/graphql/railties/base_generator.rb +3 -9
- data/lib/rails/graphql/railties/channel.rb +8 -8
- data/lib/rails/graphql/railties/controller.rb +51 -26
- data/lib/rails/graphql/request/arguments.rb +2 -1
- data/lib/rails/graphql/request/backtrace.rb +31 -10
- data/lib/rails/graphql/request/component/field.rb +15 -8
- data/lib/rails/graphql/request/component/fragment.rb +13 -7
- data/lib/rails/graphql/request/component/operation/subscription.rb +4 -6
- data/lib/rails/graphql/request/component/operation.rb +12 -5
- data/lib/rails/graphql/request/component/spread.rb +13 -4
- data/lib/rails/graphql/request/component/typename.rb +1 -1
- data/lib/rails/graphql/request/component.rb +2 -0
- data/lib/rails/graphql/request/context.rb +1 -1
- data/lib/rails/graphql/request/event.rb +6 -2
- data/lib/rails/graphql/request/helpers/directives.rb +1 -0
- data/lib/rails/graphql/request/helpers/selection_set.rb +10 -4
- data/lib/rails/graphql/request/helpers/value_writers.rb +8 -5
- data/lib/rails/graphql/request/prepared_data.rb +3 -1
- data/lib/rails/graphql/request/steps/organizable.rb +1 -1
- data/lib/rails/graphql/request/steps/preparable.rb +1 -1
- data/lib/rails/graphql/request/steps/resolvable.rb +1 -1
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +3 -3
- data/lib/rails/graphql/request/strategy.rb +18 -4
- data/lib/rails/graphql/request/subscription.rb +18 -16
- data/lib/rails/graphql/request.rb +71 -41
- data/lib/rails/graphql/schema.rb +39 -86
- data/lib/rails/graphql/shortcuts.rb +11 -5
- data/lib/rails/graphql/source/active_record/builders.rb +22 -24
- data/lib/rails/graphql/source/active_record_source.rb +96 -34
- data/lib/rails/graphql/source/base.rb +13 -40
- data/lib/rails/graphql/source/builder.rb +14 -22
- data/lib/rails/graphql/source/scoped_arguments.rb +10 -4
- data/lib/rails/graphql/source.rb +31 -38
- data/lib/rails/graphql/subscription/provider/action_cable.rb +10 -9
- data/lib/rails/graphql/subscription/provider/base.rb +6 -5
- data/lib/rails/graphql/subscription/store/base.rb +5 -9
- data/lib/rails/graphql/subscription/store/memory.rb +18 -9
- data/lib/rails/graphql/type/creator.rb +198 -0
- data/lib/rails/graphql/type/enum.rb +17 -9
- data/lib/rails/graphql/type/input.rb +30 -7
- data/lib/rails/graphql/type/interface.rb +15 -4
- data/lib/rails/graphql/type/object/directive_object.rb +6 -5
- data/lib/rails/graphql/type/object/input_value_object.rb +3 -4
- data/lib/rails/graphql/type/object/type_object.rb +40 -13
- data/lib/rails/graphql/type/object.rb +11 -6
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/date_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/json_scalar.rb +3 -1
- data/lib/rails/graphql/type/scalar/time_scalar.rb +3 -1
- data/lib/rails/graphql/type/scalar.rb +2 -2
- data/lib/rails/graphql/type/union.rb +7 -2
- data/lib/rails/graphql/type.rb +10 -2
- data/lib/rails/graphql/type_map.rb +20 -7
- data/lib/rails/graphql/uri.rb +5 -4
- data/lib/rails/graphql/version.rb +6 -2
- data/lib/rails/graphql.rb +11 -8
- data/test/assets/introspection-mem.txt +1 -1
- data/test/assets/introspection.gql +2 -0
- data/test/assets/mem.gql +74 -60
- data/test/assets/mysql.gql +69 -55
- data/test/assets/sqlite.gql +78 -64
- data/test/assets/translate.gql +50 -39
- data/test/config.rb +2 -1
- data/test/graphql/schema_test.rb +2 -31
- data/test/graphql/source_test.rb +1 -11
- data/test/graphql/type/interface_test.rb +8 -5
- data/test/graphql/type/object_test.rb +8 -2
- data/test/graphql/type_map_test.rb +13 -16
- data/test/integration/global_id_test.rb +4 -4
- data/test/integration/memory/star_wars_validation_test.rb +2 -2
- data/test/integration/mysql/star_wars_introspection_test.rb +1 -1
- data/test/integration/resolver_precedence_test.rb +1 -1
- data/test/integration/schemas/memory.rb +3 -4
- data/test/integration/sqlite/star_wars_global_id_test.rb +27 -21
- data/test/integration/sqlite/star_wars_introspection_test.rb +1 -1
- data/test/integration/translate_test.rb +26 -14
- metadata +22 -9
@@ -65,6 +65,11 @@ module Rails
|
|
65
65
|
raise NotImplementedError, +"#{self.class.name} does not implement remove"
|
66
66
|
end
|
67
67
|
|
68
|
+
# Marks that a subscription has received an update
|
69
|
+
def update!(item)
|
70
|
+
raise NotImplementedError, +"#{self.class.name} does not implement update!"
|
71
|
+
end
|
72
|
+
|
68
73
|
# Check if a given sid or instance is stored
|
69
74
|
def has?(item)
|
70
75
|
raise NotImplementedError, +"#{self.class.name} does not implement has?"
|
@@ -123,21 +128,12 @@ module Rails
|
|
123
128
|
def hash_for(value, klass = nil)
|
124
129
|
if !klass.nil?
|
125
130
|
klass.hash ^ value.hash
|
126
|
-
elsif extract_class_from?(value)
|
127
|
-
value.class.hash ^ value.id.hash
|
128
131
|
elsif value.is_a?(Numeric)
|
129
132
|
value
|
130
133
|
else
|
131
134
|
value.hash
|
132
135
|
end
|
133
136
|
end
|
134
|
-
|
135
|
-
# Check if ActiveRecord::Base is available and then if the object
|
136
|
-
# provided is an instance of it, so that the serialize can work
|
137
|
-
# correctly
|
138
|
-
def extract_class_from?(value)
|
139
|
-
defined?(ActiveRecord) && value.is_a?(ActiveRecord::Base)
|
140
|
-
end
|
141
137
|
end
|
142
138
|
end
|
143
139
|
end
|
@@ -49,6 +49,11 @@ module Rails
|
|
49
49
|
raise ::ArgumentError, +"SID #{subscription.sid} is already taken."
|
50
50
|
end
|
51
51
|
|
52
|
+
# Rewrite the scope, to save memory
|
53
|
+
scope = possible_scopes(subscription.scope)&.first
|
54
|
+
subscription.instance_variable_set(:@scope, scope)
|
55
|
+
|
56
|
+
# Save to the list and to the index
|
52
57
|
list[subscription.sid] = subscription
|
53
58
|
index_set = subscription_to_index(subscription).reduce(index, &:[])
|
54
59
|
index_set << subscription.sid
|
@@ -56,13 +61,13 @@ module Rails
|
|
56
61
|
end
|
57
62
|
|
58
63
|
def fetch(*sids)
|
59
|
-
if sids.none?
|
60
|
-
|
61
|
-
|
62
|
-
list[
|
63
|
-
else
|
64
|
-
sids.map(&list.method(:[]))
|
64
|
+
return if sids.none?
|
65
|
+
|
66
|
+
items = sids.map do |item|
|
67
|
+
instance?(item) ? item : list[item]
|
65
68
|
end
|
69
|
+
|
70
|
+
items.one? ? items.first : items
|
66
71
|
end
|
67
72
|
|
68
73
|
def remove(item)
|
@@ -82,6 +87,10 @@ module Rails
|
|
82
87
|
index.delete(path[0]) if f_level.empty?
|
83
88
|
end
|
84
89
|
|
90
|
+
def update!(item)
|
91
|
+
(instance?(item) ? item : fetch(item)).update!
|
92
|
+
end
|
93
|
+
|
85
94
|
def has?(item)
|
86
95
|
list.key?(instance?(item) ? item.sid : item)
|
87
96
|
end
|
@@ -115,9 +124,9 @@ module Rails
|
|
115
124
|
# Turn the request subscription into into the path of the index
|
116
125
|
def subscription_to_index(subscription)
|
117
126
|
[
|
118
|
-
|
119
|
-
|
120
|
-
|
127
|
+
subscription.field.hash,
|
128
|
+
subscription.scope,
|
129
|
+
subscription.args.hash,
|
121
130
|
]
|
122
131
|
end
|
123
132
|
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
class Type
|
6
|
+
# = GraphQL Type Creator
|
7
|
+
#
|
8
|
+
# This class helps to dynamically create types using a large set of
|
9
|
+
# settings that are provided through the named arguments. There are
|
10
|
+
# setting that are general to all types, and some are specific per base
|
11
|
+
# type of the superclass
|
12
|
+
class Creator
|
13
|
+
NESTED_MODULE = :NestedTypes
|
14
|
+
SUPPORTED_KINDS = %i[scalar object interface union enum input_object source].freeze
|
15
|
+
|
16
|
+
attr_reader :name, :superclass, :klass, :settings
|
17
|
+
|
18
|
+
delegate :type_map, to: '::Rails::GraphQL'
|
19
|
+
delegate :kind, to: :superclass
|
20
|
+
|
21
|
+
# Simply instantiate the creator and run the process
|
22
|
+
def self.create!(*args, **xargs, &block)
|
23
|
+
new(*args, **xargs).create!(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(from, name_or_object, superclass, **settings)
|
27
|
+
@from = from
|
28
|
+
@settings = settings
|
29
|
+
@object = name_or_object if name_or_object.is_a?(Class)
|
30
|
+
|
31
|
+
@superclass = sanitize_superclass(superclass)
|
32
|
+
@name = sanitize_name(name_or_object)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Go over the create process
|
36
|
+
def create!(&block)
|
37
|
+
@klass = find_or_create_class
|
38
|
+
klass.instance_variable_set(:@gql_name, gql_name)
|
39
|
+
|
40
|
+
apply_general_settings
|
41
|
+
after_block = apply_specific_settings
|
42
|
+
|
43
|
+
klass.module_exec(&block) if block.present?
|
44
|
+
after_block.call if after_block.is_a?(Proc)
|
45
|
+
klass
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
# Use the type map to look for a type from the same namespace
|
51
|
+
def find_type!(value)
|
52
|
+
type_map.fetch!(value, namespaces: namespaces, base_class: :Type)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Same as above, but mapping the list
|
56
|
+
def find_all_types!(*list)
|
57
|
+
list.map { |item| item.is_a?(Class) ? item : find_type!(item) }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Apply settings that is common for any possible type created
|
61
|
+
def apply_general_settings
|
62
|
+
klass.abstract = settings[:abstract] if settings.key?(:abstract)
|
63
|
+
klass.set_namespace(*namespaces)
|
64
|
+
|
65
|
+
klass.use(*settings[:directives]) if settings.key?(:directives) &&
|
66
|
+
klass.is_a?(Helpers::WithDirectives)
|
67
|
+
|
68
|
+
if klass.is_a?(Helpers::WithAssignment)
|
69
|
+
assignment = settings.fetch(:assigned_to, @object)
|
70
|
+
klass.assigned_to = assignment unless assignment.nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
if settings.key?(:owner)
|
74
|
+
klass.include(Helpers::WithOwner) unless klass.is_a?(Helpers::WithOwner)
|
75
|
+
klass.owner = settings[:owner] == true ? @from : settings[:owner]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Using the kind, call of a specific method to further configure the
|
80
|
+
# created class
|
81
|
+
def apply_specific_settings
|
82
|
+
method_name = +"apply_#{kind}_settings"
|
83
|
+
send(method_name) if respond_to?(method_name, true)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Specific settings when creating an enum
|
87
|
+
def apply_enum_settings
|
88
|
+
klass.indexed! if settings[:indexed]
|
89
|
+
return if (values = settings[:values]).nil?
|
90
|
+
GraphQL.enumerate(values).each(&klass.method(:add))
|
91
|
+
end
|
92
|
+
|
93
|
+
# Specific settings when creating an union
|
94
|
+
def apply_union_settings
|
95
|
+
types = settings[:of_types]
|
96
|
+
klass.append(*find_all_types!(*types)) unless types.nil?
|
97
|
+
end
|
98
|
+
|
99
|
+
# Specific settings when creating a source
|
100
|
+
def apply_source_settings
|
101
|
+
build = settings[:build]
|
102
|
+
|
103
|
+
-> do
|
104
|
+
return klass.build_all if build == true
|
105
|
+
GraphQL.enumerate(build).each do |step|
|
106
|
+
klass.public_send(+"build_#{step}")
|
107
|
+
end
|
108
|
+
end if build
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# Either get the gql name from settings or properly resolve one from
|
114
|
+
# the name
|
115
|
+
def gql_name
|
116
|
+
settings[:gql_name].presence || begin
|
117
|
+
gql_name = name.dup
|
118
|
+
gql_name = gql_name.chomp(name_suffix) unless kind == :input_object
|
119
|
+
gql_name.tr('_', '')
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Add the nested module to the source of the creating and create the
|
124
|
+
# class. If any of those exist, return the constant instead
|
125
|
+
def find_or_create_class
|
126
|
+
base = base_module
|
127
|
+
|
128
|
+
# Create the class under the nested module
|
129
|
+
return base.const_set(name, Class.new(superclass)) \
|
130
|
+
unless base.const_defined?(name, false)
|
131
|
+
|
132
|
+
# Get the existing class and check for the once setting
|
133
|
+
klass = base.const_get(name, false)
|
134
|
+
return klass unless !once? && klass < superclass
|
135
|
+
|
136
|
+
# Created once or not from the same superclass
|
137
|
+
raise DuplicatedError, (+<<~MSG).squish
|
138
|
+
A type named "#{name}" already exists for the "#{base.name}" module.
|
139
|
+
MSG
|
140
|
+
end
|
141
|
+
|
142
|
+
# Make sure to properly get the superclass
|
143
|
+
def sanitize_superclass(value)
|
144
|
+
value = Type.const_get(value.to_s.classify, false) unless value.is_a?(Class)
|
145
|
+
|
146
|
+
valid_class = value.is_a?(Class) && value.respond_to?(:kind)
|
147
|
+
valid_class &= SUPPORTED_KINDS.include?(value.kind)
|
148
|
+
return value if valid_class
|
149
|
+
|
150
|
+
raise ::ArgumentError, +"The \"#{value.inspect}\" is not a valid superclass."
|
151
|
+
rescue ::NameError
|
152
|
+
raise ::ArgumentError, +"Unable to find a \"#{value}\" superclass."
|
153
|
+
end
|
154
|
+
|
155
|
+
# Let's clean up the name
|
156
|
+
def sanitize_name(name_or_object)
|
157
|
+
name = name_or_object.is_a?(Module) ? name_or_object.name : name_or_object.to_s
|
158
|
+
name = name.classify.delete_prefix('GraphQL::').gsub(/::/, '_')
|
159
|
+
name.end_with?(name_suffix) ? name : name + name_suffix
|
160
|
+
end
|
161
|
+
|
162
|
+
# Figure out the suffix that is supposed to be used for the name
|
163
|
+
def name_suffix
|
164
|
+
@name_suffix ||= @settings[:suffix] || begin
|
165
|
+
if kind == :input_object
|
166
|
+
GraphQL.config.auto_suffix_input_objects
|
167
|
+
else
|
168
|
+
superclass.kind.to_s.classify
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Get or set the base module using the from argument
|
174
|
+
def base_module
|
175
|
+
base = @from.is_a?(Module) ? @from : @from.class
|
176
|
+
if base.const_defined?(NESTED_MODULE, false)
|
177
|
+
base.const_get(NESTED_MODULE, false)
|
178
|
+
else
|
179
|
+
base.const_set(NESTED_MODULE, Module.new).tap do
|
180
|
+
base.private_constant(NESTED_MODULE)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Get the namespaces from the settings or from the source
|
186
|
+
def namespaces
|
187
|
+
@namespaces ||= settings[:namespace] || settings[:namespaces] || @from.namespace
|
188
|
+
end
|
189
|
+
|
190
|
+
# Check if the type should be create only once, meaning that returning
|
191
|
+
# an existing one is not an option
|
192
|
+
def once?
|
193
|
+
settings.fetch(:once, true)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -75,6 +75,7 @@ module Rails
|
|
75
75
|
# ==== Options
|
76
76
|
#
|
77
77
|
# * <tt>:desc</tt> - The description of the enum value (defaults to nil).
|
78
|
+
# * <tt>:description</tt> - Alias to the above.
|
78
79
|
# * <tt>:directives</tt> - The list of directives associated with the value
|
79
80
|
# (defaults to nil).
|
80
81
|
# * <tt>:deprecated</tt> - A shortcut that auto-attach a @deprecated
|
@@ -82,9 +83,9 @@ module Rails
|
|
82
83
|
# but provide a string so it can be used as the reason of the deprecation.
|
83
84
|
# See {DeprecatedDirective}[rdoc-ref:Rails::GraphQL::Directive::DeprecatedDirective]
|
84
85
|
# (defaults to false).
|
85
|
-
def add(value, desc: nil, directives: nil, deprecated: false)
|
86
|
+
def add(value, desc: nil, description: nil, directives: nil, deprecated: false)
|
86
87
|
value = value&.to_s
|
87
|
-
raise ArgumentError, (+<<~MSG).squish unless value.is_a?(String) && value.
|
88
|
+
raise ArgumentError, (+<<~MSG).squish unless value.is_a?(String) && !value.empty?
|
88
89
|
The "#{value}" is invalid.
|
89
90
|
MSG
|
90
91
|
|
@@ -103,6 +104,8 @@ module Rails
|
|
103
104
|
source: self,
|
104
105
|
)
|
105
106
|
|
107
|
+
desc = description if desc.nil?
|
108
|
+
|
106
109
|
values << value
|
107
110
|
value_description[value] = desc unless desc.nil?
|
108
111
|
value_directives[value] = directives if directives
|
@@ -130,10 +133,12 @@ module Rails
|
|
130
133
|
|
131
134
|
def inspect
|
132
135
|
return super if self.eql?(Type::Enum)
|
136
|
+
|
137
|
+
values = all_values.to_a
|
133
138
|
(+<<~INFO).squish << '>'
|
134
139
|
#<GraphQL::Enum #{gql_name}
|
135
|
-
(#{
|
136
|
-
{#{
|
140
|
+
(#{values.size})
|
141
|
+
{#{values.to_a.join(' | ')}}
|
137
142
|
#{inspect_directives}
|
138
143
|
INFO
|
139
144
|
end
|
@@ -143,6 +148,8 @@ module Rails
|
|
143
148
|
|
144
149
|
delegate :to_s, :inspect, to: :@value
|
145
150
|
|
151
|
+
# TODO: Maybe add delegate missing
|
152
|
+
|
146
153
|
def initialize(value)
|
147
154
|
@value = value
|
148
155
|
end
|
@@ -165,13 +172,15 @@ module Rails
|
|
165
172
|
# Gets all the description of the current value
|
166
173
|
def description
|
167
174
|
return unless @value
|
168
|
-
@description
|
175
|
+
return @description if defined?(@description)
|
176
|
+
@description = all_value_description.try(:[], @value)
|
169
177
|
end
|
170
178
|
|
171
179
|
# Gets all the directives associated with the current value
|
172
180
|
def directives
|
173
181
|
return unless @value
|
174
|
-
@directives
|
182
|
+
return @directives if defined?(@directives)
|
183
|
+
@directives = all_value_directives.try(:[], @value)
|
175
184
|
end
|
176
185
|
|
177
186
|
# Checks if the current value is marked as deprecated
|
@@ -181,10 +190,9 @@ module Rails
|
|
181
190
|
|
182
191
|
# Return the deprecated reason
|
183
192
|
def deprecated_reason
|
184
|
-
|
185
|
-
directives.find do |dir|
|
193
|
+
directives&.find do |dir|
|
186
194
|
dir.is_a?(Directive::DeprecatedDirective)
|
187
|
-
end
|
195
|
+
end&.args&.reason
|
188
196
|
end
|
189
197
|
end
|
190
198
|
end
|
@@ -105,7 +105,7 @@ module Rails
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
-
attr_reader :args
|
108
|
+
attr_reader :args, :assignment_error
|
109
109
|
attr_writer :resource
|
110
110
|
|
111
111
|
delegate :fields, to: :class
|
@@ -114,7 +114,7 @@ module Rails
|
|
114
114
|
delegate_missing_to :resource
|
115
115
|
|
116
116
|
def initialize(args = nil, **xargs)
|
117
|
-
@args = args ||
|
117
|
+
@args = args || build_ostruct(xargs)
|
118
118
|
@args.freeze
|
119
119
|
|
120
120
|
validate! if args.nil?
|
@@ -124,7 +124,10 @@ module Rails
|
|
124
124
|
# received arguments. It also accepts extra arguments for inheritance
|
125
125
|
# purposes
|
126
126
|
def resource(*args, **xargs, &block)
|
127
|
-
@resource
|
127
|
+
return @resource if defined?(@resource)
|
128
|
+
return if (klass = safe_assigned_class).nil?
|
129
|
+
|
130
|
+
@resource = begin
|
128
131
|
xargs = xargs.reverse_merge(params)
|
129
132
|
klass.new(*args, **xargs, &block)
|
130
133
|
end
|
@@ -132,21 +135,25 @@ module Rails
|
|
132
135
|
|
133
136
|
# Just return the arguments as an hash
|
134
137
|
def params
|
135
|
-
parametrize(
|
138
|
+
parametrize(@args.to_h)
|
136
139
|
end
|
137
140
|
|
138
|
-
#
|
141
|
+
# Correctly turn all the arguments into their +as_json+ version and
|
139
142
|
# return a hash of them
|
140
143
|
def args_as_json
|
141
144
|
self.class.as_json(@args.to_h)
|
142
145
|
end
|
143
146
|
|
144
|
-
|
147
|
+
alias as_json args_as_json
|
148
|
+
|
149
|
+
# Correctly turn all the arguments into their +to_json+ version and
|
145
150
|
# return a hash of them
|
146
151
|
def args_to_json
|
147
152
|
self.class.to_json(@args.to_h)
|
148
153
|
end
|
149
154
|
|
155
|
+
alias to_json args_to_json
|
156
|
+
|
150
157
|
# Checks if all the values provided to the input instance are valid
|
151
158
|
def validate!(*)
|
152
159
|
errors = []
|
@@ -162,18 +169,34 @@ module Rails
|
|
162
169
|
MSG
|
163
170
|
end
|
164
171
|
|
172
|
+
# Override this method to save any errors that could happen with loading
|
173
|
+
# the assigned class
|
174
|
+
def safe_assigned_class
|
175
|
+
assigned_class
|
176
|
+
rescue ::ArgumentError, ::NameError => error
|
177
|
+
@assignment_error = error
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
|
165
181
|
%i[to_global_id to_gid to_gid_param].each do |method_name|
|
166
182
|
define_method(method_name) do
|
167
183
|
self.class.public_send(method_name, args_as_json.compact)
|
168
184
|
end
|
169
185
|
end
|
170
186
|
|
187
|
+
protected
|
188
|
+
|
189
|
+
# A helper to turn a hash into a proper Open Struct instance
|
190
|
+
def build_ostruct(hash)
|
191
|
+
OpenStruct.new(hash.transform_keys { |key| key.to_s.underscore })
|
192
|
+
end
|
193
|
+
|
171
194
|
private
|
172
195
|
|
173
196
|
# Make sure to turn inputs into params
|
174
197
|
def parametrize(input)
|
175
198
|
case input
|
176
|
-
when Type::Input then
|
199
|
+
when Type::Input then input.params
|
177
200
|
when Array then input.map(&method(:parametrize))
|
178
201
|
when Hash then input.transform_values(&method(:parametrize))
|
179
202
|
else input
|
@@ -26,6 +26,11 @@ module Rails
|
|
26
26
|
inherited_collection :types, instance_reader: false
|
27
27
|
|
28
28
|
class << self
|
29
|
+
# Figure out which one of the types is compatible with the provided +value+
|
30
|
+
def type_for(value, *)
|
31
|
+
all_types&.reverse_each&.find { |t| t.valid_member?(value) }
|
32
|
+
end
|
33
|
+
|
29
34
|
# Check if the other type is equivalent, by checking if the other is
|
30
35
|
# an object and the object implements this interface
|
31
36
|
def =~(other)
|
@@ -35,16 +40,22 @@ module Rails
|
|
35
40
|
# When attaching an interface to an object, copy the fields and add to
|
36
41
|
# the list of types. Pre-existing same-named fields with are not
|
37
42
|
# equivalent produces an exception.
|
38
|
-
def implemented(object)
|
43
|
+
def implemented(object, import_fields: true)
|
44
|
+
import_fields = false if abstract?
|
45
|
+
|
39
46
|
fields.each do |name, field|
|
40
|
-
defined = object.
|
41
|
-
|
47
|
+
defined = object[field.name]
|
48
|
+
raise ArgumentError, (+<<~MSG).squish unless defined || import_fields
|
49
|
+
The "#{object.gql_name}" object must have a "#{field.gql_name}" field.
|
50
|
+
MSG
|
51
|
+
|
52
|
+
invalid = defined && defined !~ field
|
42
53
|
raise ArgumentError, (+<<~MSG).squish if invalid
|
43
54
|
The "#{object.gql_name}" object already has a "#{field.gql_name}" field and it
|
44
55
|
is not equivalent to the one defined on the "#{gql_name}" interface.
|
45
56
|
MSG
|
46
57
|
|
47
|
-
object.proxy_field(field)
|
58
|
+
object.proxy_field(field) if import_fields && !defined
|
48
59
|
end
|
49
60
|
|
50
61
|
types << object
|
@@ -20,13 +20,14 @@ module Rails
|
|
20
20
|
additional information to the executor.
|
21
21
|
DESC
|
22
22
|
|
23
|
-
field :name,
|
24
|
-
field :description,
|
25
|
-
field :locations,
|
26
|
-
field :args,
|
23
|
+
field :name, :string, null: false, method_name: :gql_name
|
24
|
+
field :description, :string
|
25
|
+
field :locations, '__DirectiveLocation', full: true
|
26
|
+
field :args, '__InputValue', full: true
|
27
|
+
field :is_repeatable, :boolean, null: false, method_name: :repeatable?
|
27
28
|
|
28
29
|
def args
|
29
|
-
all_arguments.
|
30
|
+
all_arguments.each_value
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
@@ -17,10 +17,9 @@ module Rails
|
|
17
17
|
rename! '__InputValue'
|
18
18
|
|
19
19
|
desc <<~DESC
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
a single value.
|
20
|
+
Arguments provided to Fields or Directives and the input fields of an
|
21
|
+
InputObject are represented as Input Values which describe their type
|
22
|
+
and optionally a default value.
|
24
23
|
DESC
|
25
24
|
|
26
25
|
field :name, :string, null: false, method_name: :gql_name
|
@@ -12,15 +12,19 @@ module Rails
|
|
12
12
|
kind: :list,
|
13
13
|
kind_enum: 'LIST',
|
14
14
|
name: 'List',
|
15
|
+
gql_name: nil,
|
15
16
|
object?: true,
|
16
17
|
description: nil,
|
18
|
+
of_type: nil,
|
17
19
|
},
|
18
20
|
non_null: {
|
19
21
|
kind: :non_null,
|
20
22
|
kind_enum: 'NON_NULL',
|
21
23
|
name: 'Non-Null',
|
24
|
+
gql_name: nil,
|
22
25
|
object?: true,
|
23
26
|
description: nil,
|
27
|
+
of_type: nil,
|
24
28
|
},
|
25
29
|
}.freeze
|
26
30
|
|
@@ -32,7 +36,7 @@ module Rails
|
|
32
36
|
end
|
33
37
|
|
34
38
|
def self.fake_type_object(type, subtype)
|
35
|
-
|
39
|
+
FAKE_TYPES[type].merge(of_type: subtype)
|
36
40
|
end
|
37
41
|
|
38
42
|
rename! '__Type'
|
@@ -59,13 +63,15 @@ module Rails
|
|
59
63
|
the schema to define exactly what data is expected.
|
60
64
|
DESC
|
61
65
|
|
62
|
-
field :kind,
|
66
|
+
field :kind, '__TypeKind', null: false,
|
63
67
|
method_name: :kind_enum
|
64
68
|
|
65
|
-
field :name,
|
69
|
+
field :name, :string,
|
66
70
|
method_name: :gql_name
|
67
71
|
|
68
|
-
field :description,
|
72
|
+
field :description, :string
|
73
|
+
|
74
|
+
field :specified_by_url, :string
|
69
75
|
|
70
76
|
field :fields, '__Field', array: true, nullable: false do
|
71
77
|
desc 'OBJECT and INTERFACE only'
|
@@ -89,8 +95,16 @@ module Rails
|
|
89
95
|
field :of_type, '__Type',
|
90
96
|
desc: 'NON_NULL and LIST only'
|
91
97
|
|
98
|
+
def specified_by_url
|
99
|
+
return if fake_type? || !current.scalar?
|
100
|
+
|
101
|
+
current.all_directives&.find do |dir|
|
102
|
+
dir.is_a?(Directive::SpecifiedByDirective)
|
103
|
+
end&.args&.url
|
104
|
+
end
|
105
|
+
|
92
106
|
def fields(include_deprecated:)
|
93
|
-
return
|
107
|
+
return if fake_type? || !(current.object? || current.interface?)
|
94
108
|
|
95
109
|
list =
|
96
110
|
if current.respond_to?(:enabled_fields)
|
@@ -109,7 +123,7 @@ module Rails
|
|
109
123
|
end
|
110
124
|
|
111
125
|
def enum_values(include_deprecated:)
|
112
|
-
return
|
126
|
+
return if fake_type? || !current.enum?
|
113
127
|
|
114
128
|
descriptions = all_value_description
|
115
129
|
deprecated = all_deprecated_values
|
@@ -119,26 +133,39 @@ module Rails
|
|
119
133
|
unless include_deprecated || deprecated.nil?
|
120
134
|
|
121
135
|
list.map do |value|
|
122
|
-
|
136
|
+
{
|
123
137
|
name: value,
|
124
|
-
description: descriptions[
|
138
|
+
description: descriptions.try(:[], value),
|
125
139
|
is_deprecated: (deprecated.nil? ? false : deprecated.key?(value)),
|
126
140
|
deprecation_reason: deprecated.try(:[], value),
|
127
|
-
|
141
|
+
}
|
128
142
|
end
|
129
143
|
end
|
130
144
|
|
131
145
|
def interfaces
|
132
|
-
|
146
|
+
return if fake_type? || !current.object?
|
147
|
+
current.all_interfaces || EMPTY_ARRAY
|
133
148
|
end
|
134
149
|
|
135
150
|
def possible_types
|
136
|
-
|
137
|
-
|
151
|
+
return if fake_type?
|
152
|
+
|
153
|
+
if current.interface?
|
154
|
+
current.all_types || EMPTY_ARRAY
|
155
|
+
elsif current.union?
|
156
|
+
current.all_members || EMPTY_ARRAY
|
157
|
+
end
|
138
158
|
end
|
139
159
|
|
140
160
|
def input_fields
|
141
|
-
|
161
|
+
return if fake_type? || !current.input?
|
162
|
+
current.enabled_fields || EMPTY_ARRAY
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def fake_type?
|
168
|
+
Hash === current
|
142
169
|
end
|
143
170
|
end
|
144
171
|
end
|