lighthouse-graphql 0.1.1 → 0.2.0

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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +154 -1
  3. data/README.md +42 -2
  4. data/docs/authorization.md +116 -57
  5. data/docs/best-practices.md +16 -3
  6. data/docs/caching.md +161 -0
  7. data/docs/configuration.md +83 -4
  8. data/docs/custom-directives.md +5 -1
  9. data/docs/directives.md +117 -38
  10. data/docs/getting-started.md +84 -0
  11. data/docs/mutations.md +260 -0
  12. data/docs/performance.md +138 -0
  13. data/docs/relationships.md +80 -33
  14. data/docs/resolvers.md +179 -0
  15. data/docs/validation.md +132 -0
  16. data/lib/generators/lighthouse/builder_generator.rb +30 -0
  17. data/lib/generators/lighthouse/class_generator.rb +61 -0
  18. data/lib/generators/lighthouse/install/install_generator.rb +127 -0
  19. data/lib/generators/lighthouse/install/templates/graphql.config.yml.tt +8 -0
  20. data/lib/generators/lighthouse/install/templates/graphql_controller.rb.tt +57 -0
  21. data/lib/generators/lighthouse/install/templates/initializer.rb.tt +67 -0
  22. data/lib/generators/lighthouse/install/templates/schema.graphql.tt +19 -0
  23. data/lib/generators/lighthouse/install/templates/schema.rb.tt +10 -0
  24. data/lib/generators/lighthouse/mutation_generator.rb +30 -0
  25. data/lib/generators/lighthouse/query_generator.rb +29 -0
  26. data/lib/generators/lighthouse/validator_generator.rb +36 -0
  27. data/lib/lighthouse/cache_control.rb +126 -0
  28. data/lib/lighthouse/caching.rb +68 -0
  29. data/lib/lighthouse/config.rb +85 -19
  30. data/lib/lighthouse/contracts/arg_builder.rb +8 -12
  31. data/lib/lighthouse/contracts/arg_manipulator.rb +8 -12
  32. data/lib/lighthouse/contracts/directive.rb +10 -14
  33. data/lib/lighthouse/contracts/field_manipulator.rb +8 -12
  34. data/lib/lighthouse/contracts/field_middleware.rb +7 -11
  35. data/lib/lighthouse/contracts/field_resolver.rb +9 -13
  36. data/lib/lighthouse/dataloader_sources/association_loader.rb +28 -19
  37. data/lib/lighthouse/dataloader_sources/model_loader.rb +10 -14
  38. data/lib/lighthouse/dataloader_sources/relation_count_loader.rb +16 -0
  39. data/lib/lighthouse/dataloader_sources/relation_page_loader.rb +18 -0
  40. data/lib/lighthouse/directive_resolvers/aggregate_resolver.rb +57 -69
  41. data/lib/lighthouse/directive_resolvers/all_resolver.rb +26 -57
  42. data/lib/lighthouse/directive_resolvers/association_resolvers.rb +161 -0
  43. data/lib/lighthouse/directive_resolvers/auth_resolver.rb +41 -38
  44. data/lib/lighthouse/directive_resolvers/base.rb +142 -86
  45. data/lib/lighthouse/directive_resolvers/builder_applier.rb +58 -56
  46. data/lib/lighthouse/directive_resolvers/cache_resolvers.rb +133 -0
  47. data/lib/lighthouse/directive_resolvers/can_resolvers.rb +177 -0
  48. data/lib/lighthouse/directive_resolvers/convention_resolver.rb +70 -0
  49. data/lib/lighthouse/directive_resolvers/count_resolver.rb +42 -48
  50. data/lib/lighthouse/directive_resolvers/field_resolver.rb +27 -68
  51. data/lib/lighthouse/directive_resolvers/find_resolver.rb +42 -52
  52. data/lib/lighthouse/directive_resolvers/first_resolver.rb +39 -45
  53. data/lib/lighthouse/directive_resolvers/guard_resolver.rb +16 -22
  54. data/lib/lighthouse/directive_resolvers/method_resolver.rb +19 -25
  55. data/lib/lighthouse/directive_resolvers/model_mutation_resolver.rb +219 -0
  56. data/lib/lighthouse/directive_resolvers/paginate_resolver.rb +70 -128
  57. data/lib/lighthouse/directive_resolvers/rename_resolver.rb +18 -24
  58. data/lib/lighthouse/directive_resolvers/resolution.rb +31 -27
  59. data/lib/lighthouse/directive_resolvers/validator_resolver.rb +193 -0
  60. data/lib/lighthouse/directive_resolvers/where_conditions_applier.rb +164 -163
  61. data/lib/lighthouse/directives/arguments/base.rb +23 -31
  62. data/lib/lighthouse/directives/arguments/filters.rb +113 -93
  63. data/lib/lighthouse/directives/arguments/order_by.rb +41 -41
  64. data/lib/lighthouse/directives/pagination_info_types.rb +69 -0
  65. data/lib/lighthouse/directives/pagination_manipulator.rb +176 -0
  66. data/lib/lighthouse/directives/registry.rb +210 -141
  67. data/lib/lighthouse/directives/where_conditions_directive.rb +236 -200
  68. data/lib/lighthouse/errors.rb +27 -0
  69. data/lib/lighthouse/federation_directives.rb +53 -0
  70. data/lib/lighthouse/field_resolver_stack.rb +56 -0
  71. data/lib/lighthouse/generated_resolvers.rb +82 -0
  72. data/lib/lighthouse/graphql/version.rb +1 -1
  73. data/lib/lighthouse/graphql_ruby_patches.rb +18 -0
  74. data/lib/lighthouse/ide_helper.rb +98 -0
  75. data/lib/lighthouse/namespaces.rb +21 -0
  76. data/lib/lighthouse/nested_mutations/belongs_to_operations.rb +50 -0
  77. data/lib/lighthouse/nested_mutations/executor.rb +149 -0
  78. data/lib/lighthouse/nested_mutations/has_operations.rb +66 -0
  79. data/lib/lighthouse/nested_mutations/many_to_many_operations.rb +44 -0
  80. data/lib/lighthouse/nested_mutations/partitioner.rb +125 -0
  81. data/lib/lighthouse/nested_mutations.rb +210 -0
  82. data/lib/lighthouse/pagination.rb +221 -0
  83. data/lib/lighthouse/persisted_queries.rb +167 -0
  84. data/lib/lighthouse/railtie.rb +26 -0
  85. data/lib/lighthouse/rb_lighthouse.rb +50 -29
  86. data/lib/lighthouse/reference_resolver.rb +13 -15
  87. data/lib/lighthouse/relation_batch.rb +156 -0
  88. data/lib/lighthouse/resolver_dispatch.rb +26 -0
  89. data/lib/lighthouse/schema_cache.rb +120 -0
  90. data/lib/lighthouse/schema_factory.rb +27 -37
  91. data/lib/lighthouse/schema_generator.rb +196 -239
  92. data/lib/lighthouse/schema_implementation.rb +51 -48
  93. data/lib/lighthouse/sdl_loader.rb +17 -20
  94. data/lib/lighthouse/support/arguments.rb +119 -0
  95. data/lib/lighthouse/support/authorization.rb +18 -19
  96. data/lib/lighthouse/support/batching.rb +16 -0
  97. data/lib/lighthouse/support/directive_args.rb +26 -24
  98. data/lib/lighthouse/support/model_resolver.rb +56 -56
  99. data/lib/lighthouse/support/naming.rb +55 -59
  100. data/lib/lighthouse/tasks/lighthouse.rake +33 -0
  101. data/lib/lighthouse/where_conditions/operator_map.rb +25 -29
  102. metadata +51 -63
  103. data/lib/lighthouse/directive_resolvers/belongs_to_many_resolver.rb +0 -56
  104. data/lib/lighthouse/directive_resolvers/belongs_to_resolver.rb +0 -47
  105. data/lib/lighthouse/directive_resolvers/can_resolver.rb +0 -86
  106. data/lib/lighthouse/directive_resolvers/has_many_resolver.rb +0 -109
  107. data/lib/lighthouse/directive_resolvers/has_one_resolver.rb +0 -43
  108. data/lib/lighthouse/directives/relation_directive.rb +0 -332
@@ -1,168 +1,237 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Lighthouse
4
- # Maps directive names to the classes that implement them, so the
5
- # SchemaGenerator can dispatch by iterating the registry instead of a
6
- # hardcoded `case` statement. Adding a directive is "write one class and
7
- # register it" — no generator edits.
8
- #
9
- # Two registration styles are supported during the migration to clean,
10
- # self-describing directive classes:
11
- #
12
- # register('hasMany', SomeLegacyResolverClass) # explicit name
13
- # register(MyDirectiveClass) # class responds to .directive_name
14
- #
15
- # A registered class is classified as a field resolver when it includes
16
- # Contracts::FieldResolver OR responds to the legacy `#define_resolver`
17
- # instance method.
18
- class DirectiveRegistry
19
- class << self
20
- def default
21
- @default ||= new.tap(&:register_defaults)
22
- end
23
-
24
- # Allow the host app to extend the default registry from an initializer.
25
- def register(name_or_klass, klass = nil)
26
- default.register(name_or_klass, klass)
27
- end
3
+ # Maps directive names to the classes that implement them, so the
4
+ # SchemaGenerator can dispatch by iterating the registry instead of a
5
+ # hardcoded `case` statement. Adding a directive is "write one class and
6
+ # register it" — no generator edits.
7
+ #
8
+ # Two registration styles are supported during the migration to clean,
9
+ # self-describing directive classes:
10
+ #
11
+ # register('hasMany', SomeLegacyResolverClass) # explicit name
12
+ # register(MyDirectiveClass) # class responds to .directive_name
13
+ #
14
+ # A registered class is classified as a field resolver when it includes
15
+ # Contracts::FieldResolver OR responds to the legacy `#define_resolver`
16
+ # instance method.
17
+ class Lighthouse::DirectiveRegistry
18
+ class << self
19
+ def default
20
+ @default ||= new.tap(&:register_defaults)
21
+ end
22
+
23
+ # Allow the host app to extend the default registry from an initializer.
24
+ def register(name_or_klass, klass = nil)
25
+ default.register(name_or_klass, klass)
28
26
  end
27
+ end
29
28
 
30
- def initialize
31
- @resolution = {}
32
- @arg_builders = {}
33
- @sdl_manipulators = []
34
- end
29
+ def initialize
30
+ @resolution = {}
31
+ @arg_builders = {}
32
+ @sdl_manipulators = []
33
+ @definitions = {}
34
+ @type_definitions = {}
35
+ end
35
36
 
36
- def register(name_or_klass, klass = nil)
37
- if klass.nil? && name_or_klass.respond_to?(:directive_name)
38
- klass = name_or_klass
39
- name = klass.directive_name
40
- else
41
- name = name_or_klass
42
- end
43
-
44
- if arg_builder?(klass)
45
- @arg_builders[name.to_s] = klass
46
- elsif resolver?(klass)
47
- @resolution[name.to_s] = klass
48
- end
49
- self
37
+ def register(name_or_klass, klass = nil)
38
+ if klass.nil? && name_or_klass.respond_to?(:directive_name)
39
+ klass = name_or_klass
40
+ name = klass.directive_name
41
+ else
42
+ name = name_or_klass
50
43
  end
51
44
 
52
- def resolution_class(name)
53
- @resolution[name.to_s]
45
+ if arg_builder?(klass)
46
+ @arg_builders[name.to_s] = klass
47
+ elsif resolver?(klass)
48
+ @resolution[name.to_s] = klass
54
49
  end
50
+ self
51
+ end
55
52
 
56
- # Application order for wrapping/resolution directives on a field. Lower is
57
- # applied first (innermost, closest to the data); higher is applied last
58
- # (outermost, so it executes first). Authentication guards run outermost;
59
- # @can wraps the resolver to authorize its result.
60
- def resolution_order(name)
61
- case name.to_s
62
- when 'guard', 'auth' then 2
63
- when 'can' then 1
64
- else 0
65
- end
66
- end
53
+ def resolution_class(name)
54
+ @resolution[name.to_s]
55
+ end
67
56
 
68
- def resolution_names
69
- @resolution.keys
70
- end
57
+ # Application order for wrapping/resolution directives on a field. Lower is
58
+ # applied first (innermost, closest to the data); higher is applied last
59
+ # (outermost, so it executes first). From the outside in: authentication,
60
+ # authorization, argument validation, then @cache (so a cache hit is still
61
+ # authorized and validated), @clearCache (which acts once the resolver
62
+ # succeeded), and finally the data resolver (0).
63
+ RESOLUTION_ORDER = {
64
+ 'guard' => 5, 'auth' => 5,
65
+ 'canFind' => 4, 'canModel' => 4, 'canQuery' => 4, 'canResolved' => 4, 'canRoot' => 4,
66
+ 'validator' => 3,
67
+ 'cache' => 2,
68
+ 'clearCache' => 1
69
+ }.freeze
70
+
71
+ def resolution_order(name)
72
+ RESOLUTION_ORDER.fetch(name.to_s, 0)
73
+ end
71
74
 
72
- # Argument-builder directives (@eq, @neq, @in, @notIn, @like, @where,
73
- # @orderBy) compose constraints onto a relation at resolve time.
74
- def arg_builder_class(name)
75
- @arg_builders[name.to_s]
76
- end
75
+ # A wrapper decorates whatever resolves the field; anything else resolves it.
76
+ def wrapper?(name)
77
+ resolution_order(name).positive?
78
+ end
77
79
 
78
- def arg_builder_names
79
- @arg_builders.keys
80
- end
80
+ def resolution_names
81
+ @resolution.keys
82
+ end
81
83
 
82
- # SDL `directive @name(...) on ...` definitions contributed by registered,
83
- # self-describing directive classes. The generator injects these so the app
84
- # does not have to hand-declare every directive it uses.
85
- # Returns { "name" => "directive @name(...) on ..." }.
86
- def directive_definitions
87
- definitions = {}
88
- (@resolution.values + @arg_builders.values).uniq.each do |klass|
89
- next unless klass.respond_to?(:directive_name) && klass.respond_to?(:definition)
90
-
91
- text = safe_definition(klass)
92
- definitions[klass.directive_name.to_s] = text if text
93
- end
94
- definitions
95
- end
84
+ # Argument-builder directives (@eq, @neq, @in, @notIn, @like, @where,
85
+ # @orderBy) compose constraints onto a relation at resolve time.
86
+ def arg_builder_class(name)
87
+ @arg_builders[name.to_s]
88
+ end
96
89
 
97
- # Register an SDL manipulator: any object responding to `manipulate(sdl)` and
98
- # returning the (possibly transformed) SDL string. These run before the
99
- # schema is built to inject Connection/Paginator types, whereConditions
100
- # inputs, orderBy enums, etc. Order of registration is preserved.
101
- def register_manipulator(manipulator)
102
- @sdl_manipulators << manipulator unless @sdl_manipulators.include?(manipulator)
103
- self
104
- end
90
+ def arg_builder_names
91
+ @arg_builders.keys
92
+ end
105
93
 
106
- def sdl_manipulators
107
- @sdl_manipulators
108
- end
94
+ # SDL `directive @name(...) on ...` definitions contributed by registered,
95
+ # self-describing directive classes. The generator injects these so the app
96
+ # does not have to hand-declare every directive it uses.
97
+ # Returns { "name" => "directive @name(...) on ..." }.
98
+ def directive_definitions
99
+ definitions = @definitions.dup
100
+ (@resolution.values + @arg_builders.values).uniq.each do |klass|
101
+ next unless klass.respond_to?(:directive_name) && klass.respond_to?(:definition)
109
102
 
110
- # Fold every registered manipulator over the SDL string.
111
- def manipulate_sdl(sdl)
112
- @sdl_manipulators.reduce(sdl) { |acc, manipulator| manipulator.manipulate(acc) }
103
+ text = safe_definition(klass)
104
+ definitions[klass.directive_name.to_s] = text if text
113
105
  end
106
+ definitions
107
+ end
114
108
 
115
- def register_defaults
116
- dr = Lighthouse::GraphQL::DirectiveResolvers
117
- {
118
- 'hasMany' => dr::HasManyDirectiveResolver,
119
- 'belongsTo' => dr::BelongsToDirectiveResolver,
120
- 'belongsToMany' => dr::BelongsToManyDirectiveResolver,
121
- 'hasOne' => dr::HasOneDirectiveResolver,
122
- 'all' => dr::AllDirectiveResolver,
123
- 'find' => dr::FindDirectiveResolver,
124
- 'first' => dr::FirstDirectiveResolver,
125
- 'paginate' => dr::PaginateDirectiveResolver,
126
- 'aggregate' => dr::AggregateDirectiveResolver,
127
- 'count' => dr::CountDirectiveResolver,
128
- 'field' => dr::FieldDirectiveResolver,
129
- 'rename' => dr::RenameDirectiveResolver,
130
- 'method' => dr::MethodDirectiveResolver,
131
- 'auth' => dr::AuthDirectiveResolver,
132
- 'guard' => dr::GuardDirectiveResolver,
133
- 'can' => dr::CanDirectiveResolver
134
- }.each { |name, k| register(name, k) }
135
-
136
- # Argument-builder filter/sort directives.
137
- args = Lighthouse::GraphQL::Directives::Arguments
138
- [args::Eq, args::Neq, args::In, args::NotIn, args::Like, args::Where, args::OrderBy]
139
- .each { |k| register(k) }
140
-
141
- # SDL manipulators run in registration order during the manipulation phase.
142
- register_manipulator(Lighthouse::GraphQL::Directives::RelationDirective)
143
- register_manipulator(Lighthouse::GraphQL::Directives::WhereConditionsDirective)
144
-
145
- self
146
- end
109
+ # Contribute the SDL definition of a directive that has no registered class
110
+ # of its own — one implemented inside another directive or manipulator
111
+ # (e.g. @builder, @whereConditions) — so it is injected like the rest.
112
+ def register_definition(name, definition)
113
+ @definitions[name.to_s] = definition
114
+ self
115
+ end
147
116
 
148
- private
117
+ # Types that directive definitions refer to (`enum CanAction`, `input
118
+ # ClearCacheIdSource`, ...), contributed by a class's `type_definitions`
119
+ # ({ "Name" => "enum Name { ... }" }) or registered here. Injected next to
120
+ # the directives that need them; shared by name, so declared only once.
121
+ def register_type_definition(name, definition)
122
+ @type_definitions[name.to_s] = definition
123
+ self
124
+ end
149
125
 
150
- def safe_definition(klass)
151
- klass.definition
152
- rescue StandardError
153
- nil
126
+ def type_definitions
127
+ types = @type_definitions.dup
128
+ (@resolution.values + @arg_builders.values).uniq.each do |klass|
129
+ types.merge!(klass.type_definitions) if klass.respond_to?(:type_definitions)
154
130
  end
131
+ types
132
+ end
155
133
 
156
- def arg_builder?(klass)
157
- klass.respond_to?(:include?) && klass.include?(Lighthouse::Contracts::ArgBuilder)
158
- end
134
+ # Register an SDL manipulator: any object responding to `manipulate(sdl)` and
135
+ # returning the (possibly transformed) SDL string. These run before the
136
+ # schema is built to inject Connection/Paginator types, whereConditions
137
+ # inputs, orderBy enums, etc. Order of registration is preserved.
138
+ def register_manipulator(manipulator)
139
+ @sdl_manipulators << manipulator unless @sdl_manipulators.include?(manipulator)
140
+ self
141
+ end
159
142
 
160
- def resolver?(klass)
161
- return false unless klass
143
+ attr_reader :sdl_manipulators
162
144
 
163
- (klass.respond_to?(:include?) && klass.include?(Lighthouse::Contracts::FieldResolver)) ||
164
- klass.method_defined?(:define_resolver) ||
165
- klass.method_defined?(:resolve_field)
145
+ # Fold every registered manipulator over the SDL string.
146
+ def manipulate_sdl(sdl)
147
+ @sdl_manipulators.reduce(sdl) { |acc, manipulator| manipulator.manipulate(acc) }
148
+ end
149
+
150
+ # The built-in directives, by constant name under DirectiveResolvers (and
151
+ # Directives::Arguments for the argument builders). Names rather than
152
+ # classes, so the classes are looked up only when the defaults are
153
+ # registered, after every file has loaded.
154
+ NAMED_RESOLVERS = {
155
+ 'all' => :AllDirectiveResolver,
156
+ 'find' => :FindDirectiveResolver,
157
+ 'first' => :FirstDirectiveResolver,
158
+ 'paginate' => :PaginateDirectiveResolver,
159
+ 'aggregate' => :AggregateDirectiveResolver,
160
+ 'count' => :CountDirectiveResolver,
161
+ 'field' => :FieldDirectiveResolver,
162
+ 'rename' => :RenameDirectiveResolver,
163
+ 'method' => :MethodDirectiveResolver,
164
+ 'auth' => :AuthDirectiveResolver,
165
+ 'guard' => :GuardDirectiveResolver
166
+ }.freeze
167
+
168
+ # Relationships, mutations, authorization, caching and validation: self-describing classes.
169
+ SELF_DESCRIBING_RESOLVERS = %i[
170
+ HasManyDirectiveResolver BelongsToManyDirectiveResolver MorphManyDirectiveResolver
171
+ MorphToManyDirectiveResolver HasOneDirectiveResolver MorphOneDirectiveResolver
172
+ BelongsToDirectiveResolver MorphToDirectiveResolver
173
+ CreateDirectiveResolver CreateManyDirectiveResolver
174
+ UpdateDirectiveResolver UpdateManyDirectiveResolver
175
+ UpsertDirectiveResolver UpsertManyDirectiveResolver DeleteDirectiveResolver
176
+ CanFindDirectiveResolver CanModelDirectiveResolver CanQueryDirectiveResolver
177
+ CanResolvedDirectiveResolver CanRootDirectiveResolver
178
+ CacheDirectiveResolver ClearCacheDirectiveResolver
179
+ ValidatorDirectiveResolver
180
+ ].freeze
181
+
182
+ # Argument-builder filter/sort directives.
183
+ ARG_BUILDERS = %i[Eq Neq In NotIn Like Where WhereKey OrderBy].freeze
184
+
185
+ # Read by other directives rather than resolving anything themselves.
186
+ PASSIVE_DEFINITIONS = {
187
+ 'spread' => 'directive @spread on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION',
188
+ 'inject' => 'directive @inject(context: String!, name: String!) repeatable on FIELD_DEFINITION',
189
+ 'cacheKey' => 'directive @cacheKey on FIELD_DEFINITION'
190
+ }.freeze
191
+
192
+ def register_defaults
193
+ resolvers = Lighthouse::GraphQL::DirectiveResolvers
194
+ NAMED_RESOLVERS.each { |name, constant| register(name, resolvers.const_get(constant, false)) }
195
+ SELF_DESCRIBING_RESOLVERS.each { |constant| register(resolvers.const_get(constant, false)) }
196
+ ARG_BUILDERS.each { |constant| register(Lighthouse::GraphQL::Directives::Arguments.const_get(constant, false)) }
197
+
198
+ # SDL manipulators run in registration order during the manipulation phase.
199
+ register_manipulator(Lighthouse::GraphQL::Directives::PaginationManipulator)
200
+ register_manipulator(Lighthouse::GraphQL::Directives::WhereConditionsDirective)
201
+
202
+ register_default_definitions
203
+ self
204
+ end
205
+
206
+ private
207
+
208
+ # SDL of the directives (and the types they refer to) that no registered
209
+ # class contributes.
210
+ def register_default_definitions
211
+ register_definition('builder', Lighthouse::GraphQL::DirectiveResolvers::BuilderApplier::DEFINITION)
212
+ PASSIVE_DEFINITIONS.each { |name, definition| register_definition(name, definition) }
213
+ register_definition('cacheControl', Lighthouse::GraphQL::CacheControl::DEFINITION)
214
+ Lighthouse::GraphQL::CacheControl::TYPE_DEFINITIONS.each { |name, sdl| register_type_definition(name, sdl) }
215
+ Lighthouse::GraphQL::Directives::WhereConditionsDirective::DEFINITIONS.each do |name, definition|
216
+ register_definition(name, definition)
166
217
  end
167
218
  end
219
+
220
+ def safe_definition(klass)
221
+ klass.definition
222
+ rescue StandardError
223
+ nil
224
+ end
225
+
226
+ def arg_builder?(klass)
227
+ klass.respond_to?(:include?) && klass.include?(Lighthouse::Contracts::ArgBuilder)
228
+ end
229
+
230
+ def resolver?(klass)
231
+ return false unless klass
232
+
233
+ (klass.respond_to?(:include?) && klass.include?(Lighthouse::Contracts::FieldResolver)) ||
234
+ klass.method_defined?(:define_resolver) ||
235
+ klass.method_defined?(:resolve_field)
236
+ end
168
237
  end