lighthouse-graphql 0.4.0 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +256 -1
- data/README.md +26 -15
- data/docs/caching.md +4 -3
- data/docs/configuration.md +16 -12
- data/docs/directives.md +294 -4
- data/docs/generators.md +84 -0
- data/docs/getting-started.md +93 -60
- data/docs/performance.md +2 -2
- data/docs/relationships.md +47 -4
- data/docs/relay.md +200 -0
- data/docs/resolvers.md +326 -34
- data/docs/security.md +73 -8
- data/docs/subscriptions.md +16 -9
- data/docs/types.md +218 -0
- data/docs/uploads.md +107 -0
- data/lib/generators/lighthouse/base_classes.rb +27 -49
- data/lib/generators/lighthouse/install/graphql_ruby_install.rb +113 -0
- data/lib/generators/lighthouse/install/install_generator.rb +125 -45
- data/lib/generators/lighthouse/install/templates/initializer.rb.tt +10 -8
- data/lib/lighthouse/async_mutation_job.rb +17 -0
- data/lib/lighthouse/caching.rb +9 -3
- data/lib/lighthouse/complexity.rb +5 -4
- data/lib/lighthouse/config.rb +12 -18
- data/lib/lighthouse/controller.rb +53 -12
- data/lib/lighthouse/dataloader_sources/preload_loader.rb +16 -0
- data/lib/lighthouse/dataloader_sources/relation_counts_loader.rb +46 -0
- data/lib/lighthouse/default_resolve_dispatcher.rb +26 -0
- data/lib/lighthouse/directive_resolvers/all_resolver.rb +1 -1
- data/lib/lighthouse/directive_resolvers/argument_binding.rb +61 -0
- data/lib/lighthouse/directive_resolvers/argument_transforms_resolver.rb +220 -0
- data/lib/lighthouse/directive_resolvers/association_resolvers.rb +2 -2
- data/lib/lighthouse/directive_resolvers/async_resolver.rb +90 -0
- data/lib/lighthouse/directive_resolvers/base.rb +63 -14
- data/lib/lighthouse/directive_resolvers/builder_applier.rb +89 -19
- data/lib/lighthouse/directive_resolvers/cache_resolvers.rb +1 -1
- data/lib/lighthouse/directive_resolvers/can_resolvers.rb +1 -1
- data/lib/lighthouse/directive_resolvers/convention_resolver.rb +2 -1
- data/lib/lighthouse/directive_resolvers/count_resolver.rb +1 -1
- data/lib/lighthouse/directive_resolvers/field_resolver.rb +35 -7
- data/lib/lighthouse/directive_resolvers/first_resolver.rb +1 -1
- data/lib/lighthouse/directive_resolvers/global_id_resolver.rb +59 -0
- data/lib/lighthouse/directive_resolvers/lazy_load_resolver.rb +61 -0
- data/lib/lighthouse/directive_resolvers/limit_resolver.rb +59 -0
- data/lib/lighthouse/directive_resolvers/method_resolver.rb +38 -3
- data/lib/lighthouse/directive_resolvers/model_mutation_resolver.rb +1 -1
- data/lib/lighthouse/directive_resolvers/namespaced_resolver.rb +31 -0
- data/lib/lighthouse/directive_resolvers/paginate_resolver.rb +1 -1
- data/lib/lighthouse/directive_resolvers/resolution.rb +19 -4
- data/lib/lighthouse/directive_resolvers/subscription_resolver.rb +2 -1
- data/lib/lighthouse/directive_resolvers/throttle_resolver.rb +136 -0
- data/lib/lighthouse/directive_resolvers/type_method_resolver.rb +87 -0
- data/lib/lighthouse/directive_resolvers/with_count_resolver.rb +43 -0
- data/lib/lighthouse/directive_resolvers/with_resolver.rb +42 -0
- data/lib/lighthouse/directives/arguments/more_filters.rb +215 -0
- data/lib/lighthouse/directives/definitions.rb +52 -0
- data/lib/lighthouse/directives/model_directive.rb +131 -0
- data/lib/lighthouse/directives/node_directive.rb +134 -0
- data/lib/lighthouse/directives/pagination_manipulator.rb +1 -1
- data/lib/lighthouse/directives/registry.rb +21 -21
- data/lib/lighthouse/directives/visibility_directive.rb +71 -0
- data/lib/lighthouse/enum_directive.rb +41 -0
- data/lib/lighthouse/field_resolver_stack.rb +56 -6
- data/lib/lighthouse/generated_resolvers.rb +13 -8
- data/lib/lighthouse/global_id.rb +57 -0
- data/lib/lighthouse/graphql/version.rb +1 -1
- data/lib/lighthouse/graphql_ruby_patches.rb +45 -0
- data/lib/lighthouse/multipart_request.rb +43 -0
- data/lib/lighthouse/node_registry.rb +68 -0
- data/lib/lighthouse/pagination.rb +3 -0
- data/lib/lighthouse/rb_lighthouse.rb +34 -0
- data/lib/lighthouse/relation_counts.rb +29 -0
- data/lib/lighthouse/resolve_info.rb +80 -0
- data/lib/lighthouse/resolver_bases.rb +25 -4
- data/lib/lighthouse/resolver_dispatch.rb +51 -11
- data/lib/lighthouse/root_types.rb +117 -0
- data/lib/lighthouse/scalar_coercion.rb +72 -0
- data/lib/lighthouse/schema_cache.rb +2 -1
- data/lib/lighthouse/schema_generator.rb +13 -11
- data/lib/lighthouse/schema_implementation.rb +4 -8
- data/lib/lighthouse/support/environment.rb +13 -0
- data/lib/lighthouse/support/model_resolver.rb +7 -5
- data/lib/lighthouse/support/preload.rb +49 -0
- data/lib/lighthouse/type.rb +103 -0
- data/lib/lighthouse/type_resolution.rb +150 -0
- data/lib/lighthouse/upload.rb +28 -0
- data/lib/lighthouse/upload_store.rb +57 -0
- data/sig/lighthouse/directives.rbs +3 -2
- data/sig/lighthouse.rbs +10 -2
- metadata +40 -4
- data/lib/generators/lighthouse/install/templates/schema.rb.tt +0 -22
- data/lib/generators/lighthouse/mutation_generator.rb +0 -31
- data/lib/generators/lighthouse/query_generator.rb +0 -30
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Eager loading for @with and @lazyLoad: ActiveRecord's preloader, over records
|
|
4
|
+
# of any classes, with relation names as PHP Lighthouse writes them
|
|
5
|
+
# ("comments.author" is comments, and each comment's author).
|
|
6
|
+
module Lighthouse::Support::Preload
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
# ["comments.author", "tags"] -> [{ comments: :author }, :tags]
|
|
10
|
+
def associations(names)
|
|
11
|
+
Array(names).map do |name|
|
|
12
|
+
*path, last = name.to_s.split('.').map(&:to_sym)
|
|
13
|
+
path.reverse.reduce(last) { |nested, segment| { segment => nested } }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Load `associations` into `records`, grouped by class; a class that has no
|
|
18
|
+
# such association is left alone. `scopes` (model scope names) narrow the
|
|
19
|
+
# loaded records, as Laravel's constrained eager loads do.
|
|
20
|
+
def records(records, associations, scopes = [])
|
|
21
|
+
records.grep(::ActiveRecord::Base).group_by(&:class).each do |klass, group|
|
|
22
|
+
own = associations.select { |association| klass.reflect_on_association(root(association)) }
|
|
23
|
+
own.each { |association| preload(klass, group, association, scopes) }
|
|
24
|
+
end
|
|
25
|
+
records
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def root(association)
|
|
29
|
+
association.is_a?(Hash) ? association.keys.first : association
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def preload(klass, records, association, scopes)
|
|
33
|
+
scope = scoped(klass, association, scopes)
|
|
34
|
+
preloader = ::ActiveRecord::Associations::Preloader
|
|
35
|
+
if preloader.method_defined?(:call)
|
|
36
|
+
preloader.new(records: records, associations: association, scope: scope).call # Rails >= 7
|
|
37
|
+
else
|
|
38
|
+
preloader.new.preload(records, association, scope) # Rails 6
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The scopes applied to the association's model, or nil for none.
|
|
43
|
+
def scoped(klass, association, scopes)
|
|
44
|
+
return if scopes.blank?
|
|
45
|
+
|
|
46
|
+
target = klass.reflect_on_association(root(association)).klass
|
|
47
|
+
scopes.reduce(target.all) { |relation, scope| relation.public_send(scope) }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/concern'
|
|
4
|
+
|
|
5
|
+
# Hands a graphql-ruby type class to Lighthouse, as `include
|
|
6
|
+
# Lighthouse::GraphQL::Subscription` does a subscription. The class keeps
|
|
7
|
+
# graphql-ruby's folder structure and naming (app/graphql/types/post_type.rb,
|
|
8
|
+
# Types::PostType for the SDL's Post type) and base classes, and does for the
|
|
9
|
+
# SDL type of its name what it does in a graphql-ruby schema:
|
|
10
|
+
#
|
|
11
|
+
# class Types::PostType < Types::BaseObject # an object type
|
|
12
|
+
# include Lighthouse::GraphQL::Type
|
|
13
|
+
#
|
|
14
|
+
# def reading_time = { words: object.body.to_s.split.size }
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# class Types::SearchResultType < Types::BaseUnion # a union
|
|
18
|
+
# include Lighthouse::GraphQL::Type
|
|
19
|
+
#
|
|
20
|
+
# def self.resolve_type(object, context) = object.is_a?(Post) ? 'Post' : 'User'
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# module Types::NamedType # an interface
|
|
24
|
+
# include Types::BaseInterface
|
|
25
|
+
# include Lighthouse::GraphQL::Type
|
|
26
|
+
#
|
|
27
|
+
# definition_methods do
|
|
28
|
+
# def resolve_type(object, context) = ...
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# class Types::DateTimeType < Types::BaseScalar # a scalar
|
|
33
|
+
# include Lighthouse::GraphQL::Type
|
|
34
|
+
#
|
|
35
|
+
# def self.coerce_input(value, context) = Time.iso8601(value)
|
|
36
|
+
# def self.coerce_result(value, context) = value.iso8601
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# An object type's methods resolve its fields that have no resolving
|
|
40
|
+
# directive, with graphql-ruby's `object` (the parent), `context`, the
|
|
41
|
+
# `dataload*` helpers, and the field's arguments as snake_case keywords. A
|
|
42
|
+
# union's or interface's `resolve_type` picks the object type of a value (a
|
|
43
|
+
# type name, or a type class such as Types::PostType). A scalar's
|
|
44
|
+
# `coerce_input` / `coerce_result` convert its values. The SDL still declares
|
|
45
|
+
# the types and their fields: the class only lends its behaviour, so `field`
|
|
46
|
+
# declarations in it are ignored, and an object type's `authorized?` does not run.
|
|
47
|
+
#
|
|
48
|
+
# The model an object type resolves is the SDL's to say, with @model(class:).
|
|
49
|
+
#
|
|
50
|
+
# A Types::<Name>Type that does not include the concern is not Lighthouse's,
|
|
51
|
+
# and is left alone.
|
|
52
|
+
module Lighthouse::GraphQL::Type
|
|
53
|
+
extend ActiveSupport::Concern
|
|
54
|
+
|
|
55
|
+
# The kinds of graphql-ruby class the concern can be included in.
|
|
56
|
+
KINDS = {
|
|
57
|
+
object: ::GraphQL::Schema::Object,
|
|
58
|
+
union: ::GraphQL::Schema::Union,
|
|
59
|
+
scalar: ::GraphQL::Schema::Scalar,
|
|
60
|
+
interface: ::GraphQL::Schema::Interface
|
|
61
|
+
}.freeze
|
|
62
|
+
|
|
63
|
+
# :object, :union, :scalar or :interface for a graphql-ruby type class or
|
|
64
|
+
# interface module; nil for anything else.
|
|
65
|
+
def self.kind_of(mod)
|
|
66
|
+
return :interface if !mod.is_a?(Class) && mod.is_a?(Module) && mod.include?(KINDS[:interface])
|
|
67
|
+
return unless mod.is_a?(Class)
|
|
68
|
+
|
|
69
|
+
%i[object union scalar].find { |kind| mod < KINDS[kind] }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# The class of the SDL type `type_name` (`Types::PostType` for `Post`) in
|
|
73
|
+
# Config.namespaces[:types], when it includes the concern; nil otherwise. The
|
|
74
|
+
# class must be of the same `kind` as the SDL type.
|
|
75
|
+
def self.class_for(type_name, kind)
|
|
76
|
+
klass = Lighthouse::Support::ModelResolver.constantize("#{type_name}Type", namespaces: Lighthouse::Config.namespaces[:types],
|
|
77
|
+
fallback: false)
|
|
78
|
+
return unless klass.is_a?(Module) && klass.include?(self) && kind_of(klass)
|
|
79
|
+
return klass if kind_of(klass) == kind
|
|
80
|
+
|
|
81
|
+
raise Lighthouse::DefinitionError, "#{klass} is a type class of kind #{kind_of(klass)}, but #{type_name} is of kind #{kind} in the schema."
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# What the SDL's types take from their classes and directives, once the
|
|
85
|
+
# schema is built: every @model names a model (ModelDirective), every @union /
|
|
86
|
+
# @interface class exists and every type class matches its kind and base
|
|
87
|
+
# (TypeResolution), and every scalar with a class reads and writes its values
|
|
88
|
+
# through it (ScalarCoercion). `bases`: the schema's base_types, by kind.
|
|
89
|
+
def self.apply(schema, bases = {})
|
|
90
|
+
Lighthouse::GraphQL::Directives::ModelDirective.validate!(schema)
|
|
91
|
+
Lighthouse::GraphQL::TypeResolution.validate!(schema, bases)
|
|
92
|
+
Lighthouse::GraphQL::ScalarCoercion.apply(schema, bases[:scalar])
|
|
93
|
+
Lighthouse::GraphQL::EnumDirective.apply(schema)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
included do
|
|
97
|
+
unless Lighthouse::GraphQL::Type.kind_of(self)
|
|
98
|
+
raise Lighthouse::DefinitionError,
|
|
99
|
+
"#{self} includes Lighthouse::GraphQL::Type, which is for graphql-ruby types: a subclass of GraphQL::Schema::Object, " \
|
|
100
|
+
'Union or Scalar, or a module including GraphQL::Schema::Interface (class Types::PostType < Types::BaseObject).'
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Which object type a value of a union or interface is: graphql-ruby asks the
|
|
4
|
+
# schema (its `resolve_type`) each time a field returns one. In order:
|
|
5
|
+
#
|
|
6
|
+
# 1. `@union(resolveType: "Name")` / `@interface(resolveType: "Name")` on the
|
|
7
|
+
# SDL type names a class, looked up in Config.namespaces[:types] (nested
|
|
8
|
+
# names too) and then as written, with `resolve_type(object, context)` or
|
|
9
|
+
# `call(object, context)`. PHP Lighthouse's directives.
|
|
10
|
+
# 2. Its type class, Types::<Name>Type including Lighthouse::GraphQL::Type: a
|
|
11
|
+
# union's `self.resolve_type(object, context)`, or an interface's
|
|
12
|
+
# `definition_methods { def resolve_type(object, context) }`, as in graphql-ruby.
|
|
13
|
+
# 3. The default, as PHP Lighthouse's: a Hash's `__typename`; else the type
|
|
14
|
+
# named after the object's class, or one of its superclasses (STI); else the
|
|
15
|
+
# type whose @model names the object's model (`type Article @model(class: "Post")`).
|
|
16
|
+
#
|
|
17
|
+
# A class returns a type name, the SDL type, or a type class (Types::PostType),
|
|
18
|
+
# which stands for the SDL type of its graphql_name; or, as graphql-ruby allows,
|
|
19
|
+
# [type, value] to resolve the type's fields on `value`. nil falls through to the
|
|
20
|
+
# next step. A value no step resolves, or one resolved to a type the union or
|
|
21
|
+
# interface does not include, raises Lighthouse::DefinitionError.
|
|
22
|
+
module Lighthouse::GraphQL::TypeResolution
|
|
23
|
+
DEFINITIONS = {
|
|
24
|
+
'union' => <<~GRAPHQL.chomp,
|
|
25
|
+
"""
|
|
26
|
+
The class that picks the object type of this union's values: `resolve_type(object, context)`
|
|
27
|
+
or `call(object, context)`, returning a type name or a type class.
|
|
28
|
+
"""
|
|
29
|
+
directive @union(resolveType: String!) on UNION
|
|
30
|
+
GRAPHQL
|
|
31
|
+
'interface' => <<~GRAPHQL.chomp
|
|
32
|
+
"""
|
|
33
|
+
The class that picks the object type of this interface's values: `resolve_type(object, context)`
|
|
34
|
+
or `call(object, context)`, returning a type name or a type class.
|
|
35
|
+
"""
|
|
36
|
+
directive @interface(resolveType: String!) on INTERFACE
|
|
37
|
+
GRAPHQL
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
DIRECTIVES = DEFINITIONS.keys.freeze
|
|
41
|
+
|
|
42
|
+
module_function
|
|
43
|
+
|
|
44
|
+
# The object type, or [object type, value] when a class answered with a
|
|
45
|
+
# value to resolve the fields on instead of `object` (as graphql-ruby allows).
|
|
46
|
+
def resolve(schema, abstract_type, object, context)
|
|
47
|
+
answer = custom(abstract_type, object, context)
|
|
48
|
+
answer, value = answer if answer.is_a?(Array) && answer.size == 2
|
|
49
|
+
type = type_named(schema, answer) || default(schema, abstract_type, object)
|
|
50
|
+
unless type && schema.possible_types(abstract_type).include?(type)
|
|
51
|
+
raise Lighthouse::DefinitionError, unresolved_message(schema, abstract_type, object, type)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
value.nil? ? type : [type, value]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# What the directive's class or the type class says, or nil.
|
|
58
|
+
def custom(abstract_type, object, context)
|
|
59
|
+
resolver = directive_class(abstract_type) || type_class(abstract_type)
|
|
60
|
+
return unless resolver
|
|
61
|
+
|
|
62
|
+
resolver.respond_to?(:resolve_type) ? resolver.resolve_type(object, context) : resolver.call(object, context)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def default(schema, abstract_type, object)
|
|
66
|
+
candidates = schema.possible_types(abstract_type)
|
|
67
|
+
by_name = candidates.index_by(&:graphql_name)
|
|
68
|
+
|
|
69
|
+
typename = typename_of(object)
|
|
70
|
+
return by_name[typename] if typename
|
|
71
|
+
|
|
72
|
+
class_chain(object.class).each { |name| return by_name[name] if by_name.key?(name) }
|
|
73
|
+
candidates.find { |type| model_of(type)&.then { |model| object.is_a?(model) } }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Fail at build time, not on the first query: every class a @union /
|
|
77
|
+
# @interface names exists and answers, and every type class matches its kind.
|
|
78
|
+
# `bases`: the schema's base_types, by kind, that type classes inherit from.
|
|
79
|
+
def validate!(schema, bases = {})
|
|
80
|
+
schema.types.each_value do |type|
|
|
81
|
+
next unless type.kind.union? || type.kind.interface?
|
|
82
|
+
|
|
83
|
+
directive_class(type)
|
|
84
|
+
kind = type.kind.union? ? :union : :interface
|
|
85
|
+
klass = Lighthouse::GraphQL::Type.class_for(type.graphql_name, kind)
|
|
86
|
+
Lighthouse::GraphQL::ResolverBases.check!(klass, bases[kind], "the #{type.graphql_name} #{kind}") if klass
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The SDL type a resolver's answer stands for: a name, a type, or a type class.
|
|
91
|
+
def type_named(schema, answer)
|
|
92
|
+
return if answer.nil?
|
|
93
|
+
|
|
94
|
+
schema.get_type(answer.respond_to?(:graphql_name) ? answer.graphql_name : answer.to_s)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def directive_class(abstract_type)
|
|
98
|
+
directive, name = resolve_type_directive(abstract_type)
|
|
99
|
+
return unless name
|
|
100
|
+
|
|
101
|
+
klass = Lighthouse::Support::ModelResolver.constantize(name, namespaces: Lighthouse::Config.namespaces[:types], nested: true)
|
|
102
|
+
return klass if klass.respond_to?(:resolve_type) || klass.respond_to?(:call)
|
|
103
|
+
|
|
104
|
+
raise Lighthouse::DefinitionError,
|
|
105
|
+
"@#{directive.name}(resolveType: #{name.inspect}) on #{abstract_type.graphql_name} names no class with resolve_type(object, context) " \
|
|
106
|
+
'or call(object, context).'
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The @union / @interface on the type and the class name it gives, or nil.
|
|
110
|
+
def resolve_type_directive(abstract_type)
|
|
111
|
+
directive = (abstract_type.ast_node&.directives || []).find { |d| DIRECTIVES.include?(d.name) }
|
|
112
|
+
[directive, Lighthouse::Support::DirectiveArgs.literal(directive, 'resolveType')] if directive
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def type_class(abstract_type)
|
|
116
|
+
kind = abstract_type.kind.union? ? :union : :interface
|
|
117
|
+
klass = Lighthouse::GraphQL::Type.class_for(abstract_type.graphql_name, kind)
|
|
118
|
+
klass if klass.respond_to?(:resolve_type)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def typename_of(object)
|
|
122
|
+
return unless object.is_a?(Hash)
|
|
123
|
+
|
|
124
|
+
(object['__typename'] || object[:__typename])&.to_s
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Admin, User, ApplicationRecord, ... for an Admin < User record.
|
|
128
|
+
def class_chain(klass)
|
|
129
|
+
names = []
|
|
130
|
+
while klass
|
|
131
|
+
names << klass.name.demodulize if klass.name
|
|
132
|
+
klass = klass.superclass
|
|
133
|
+
end
|
|
134
|
+
names
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def model_of(type)
|
|
138
|
+
Lighthouse::GraphQL::Directives::ModelDirective.declared_model(type)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def unresolved_message(schema, abstract_type, object, type)
|
|
142
|
+
possible = schema.possible_types(abstract_type).map(&:graphql_name).join(' | ')
|
|
143
|
+
kind = abstract_type.kind.union? ? 'union' : 'interface'
|
|
144
|
+
return "#{abstract_type.graphql_name} resolved a #{object.class} to #{type.graphql_name}, which is not one of its types (#{possible})." if type
|
|
145
|
+
|
|
146
|
+
"Lighthouse could not tell the type of a #{object.class} in the #{kind} #{abstract_type.graphql_name} (#{possible}): " \
|
|
147
|
+
"name a type after its class, map one to it with @model(class: \"#{object.class}\"), or resolve it in " \
|
|
148
|
+
"Types::#{abstract_type.graphql_name}Type.resolve_type or with @#{kind}(resolveType:)."
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The `Upload` scalar: a file a client sends with a multipart request (the
|
|
4
|
+
# GraphQL multipart request specification, MultipartRequest). `scalar Upload`
|
|
5
|
+
# in the SDL reads its values through it unless the SDL or a type class names
|
|
6
|
+
# another class. The resolver gets the uploaded file (an
|
|
7
|
+
# ActionDispatch::Http::UploadedFile in Rails): attach it to a model
|
|
8
|
+
# (`avatar: Upload` with @create on a model with has_one_attached :avatar), or
|
|
9
|
+
# store it with @upload.
|
|
10
|
+
class Lighthouse::GraphQL::Upload < GraphQL::Schema::Scalar
|
|
11
|
+
graphql_name 'Upload'
|
|
12
|
+
|
|
13
|
+
def self.coerce_input(value, _context)
|
|
14
|
+
return value if file?(value)
|
|
15
|
+
|
|
16
|
+
raise ::GraphQL::CoercionError,
|
|
17
|
+
'Could not get uploaded file, be sure to conform to GraphQL multipart request specification: ' \
|
|
18
|
+
"https://github.com/jaydenseric/graphql-multipart-request-spec. Instead got: #{value.inspect}."
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.coerce_result(_value, _context)
|
|
22
|
+
raise ::GraphQL::Error, '"Upload" cannot be serialized, it can only be used as an argument.'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.file?(value)
|
|
26
|
+
value.respond_to?(:read) && (value.respond_to?(:original_filename) || value.respond_to?(:path))
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Where @upload puts a file, as PHP Lighthouse's @upload puts it on one of the
|
|
4
|
+
# app's Laravel disks: an ActiveStorage blob on one of the app's services
|
|
5
|
+
# (config/storage.yml). `disk` names the service (config.active_storage.service
|
|
6
|
+
# without it), `path` prefixes the blob's key. ActiveStorage sets visibility per
|
|
7
|
+
# service (`public: true` in storage.yml), not per file, so `public` must agree
|
|
8
|
+
# with the service's: the schema never says a file is private that anyone can
|
|
9
|
+
# read, or public that no one can. The argument then holds the blob's signed id,
|
|
10
|
+
# which a has_one_attached / has_many_attached attribute attaches (where Laravel
|
|
11
|
+
# hands the stored path, for a model to keep). To store files elsewhere, declare
|
|
12
|
+
# an ActiveStorage service for it, as a Laravel app declares a disk.
|
|
13
|
+
module Lighthouse::GraphQL::UploadStore
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def call(file, argument:, disk: nil, path: nil, public: nil)
|
|
17
|
+
unless defined?(::ActiveStorage::Blob)
|
|
18
|
+
raise Lighthouse::DefinitionError, '@upload stores files with ActiveStorage: install it (bin/rails active_storage:install).'
|
|
19
|
+
end
|
|
20
|
+
unless Lighthouse::GraphQL::Upload.file?(file)
|
|
21
|
+
raise Lighthouse::DefinitionError, "Expected argument `#{argument}` to be an uploaded file (Upload)."
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
service = service(argument, disk, public == true)
|
|
25
|
+
file.rewind if file.respond_to?(:rewind) # a @validator, or another argument the multipart map sent it to, may have read it
|
|
26
|
+
::ActiveStorage::Blob.create_and_upload!(
|
|
27
|
+
io: file, filename: filename(file), content_type: file.try(:content_type), key: key(path), service_name: service.name
|
|
28
|
+
).signed_id
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The service `disk` names, or the default one, checked to be as public as asked.
|
|
32
|
+
def service(argument, disk, public)
|
|
33
|
+
service = disk ? ::ActiveStorage::Blob.services.fetch(disk) : ::ActiveStorage::Blob.service
|
|
34
|
+
return service if (service.public? == true) == public
|
|
35
|
+
|
|
36
|
+
raise Lighthouse::DefinitionError, visibility_mismatch(argument, service.name, public)
|
|
37
|
+
rescue KeyError => e
|
|
38
|
+
raise Lighthouse::DefinitionError, "@upload on `#{argument}`: #{e.message}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def visibility_mismatch(argument, service_name, public)
|
|
42
|
+
fix = public ? 'name a public one (public: true in config/storage.yml) with disk:' : 'pass public: true, or name a private one with disk:'
|
|
43
|
+
"@upload#{'(public: true)' if public} on `#{argument}` stores on the ActiveStorage service `#{service_name}`, " \
|
|
44
|
+
"which is #{'not ' if public}public: #{fix}."
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def filename(file)
|
|
48
|
+
file.respond_to?(:original_filename) ? file.original_filename : File.basename(file.path)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# `path`'s directory, then a fresh token, as Laravel's `storeAs(path, hashName)`;
|
|
52
|
+
# under the root ("/", the default), ActiveStorage's own key.
|
|
53
|
+
def key(path)
|
|
54
|
+
prefix = path.to_s.delete_prefix('/').delete_suffix('/')
|
|
55
|
+
prefix.empty? ? nil : "#{prefix}/#{::ActiveStorage::Blob.generate_unique_secure_token}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -104,8 +104,9 @@ module Lighthouse
|
|
|
104
104
|
# Install the field's resolver on `target_type_class` (a graphql-ruby type class).
|
|
105
105
|
def define_resolver: (untyped target_type_class) -> untyped
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
def
|
|
107
|
+
# `ctx`: the query's context, which @builder on an argument receives.
|
|
108
|
+
def apply_where_filters: (relation relation, untyped args, ?context? ctx) -> relation
|
|
109
|
+
def apply_arg_builders: (relation relation, arguments args_h, ?context? ctx) -> relation
|
|
109
110
|
def extract_relation_name_from_directive: (untyped arg_node) -> String?
|
|
110
111
|
def constantize_model: (String? name) -> Class?
|
|
111
112
|
def apply_scopes: (relation relation) -> relation
|
data/sig/lighthouse.rbs
CHANGED
|
@@ -39,6 +39,12 @@ module Lighthouse
|
|
|
39
39
|
def call: (StandardError error, context? context) -> void
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# Config.global_id: encodes and decodes Relay global ids (@globalId, @node).
|
|
43
|
+
interface _GlobalId
|
|
44
|
+
def encode: (String type, untyped id) -> String
|
|
45
|
+
def decode: (String global_id) -> Array[String]
|
|
46
|
+
end
|
|
47
|
+
|
|
42
48
|
# Config.authenticated_user and Config.policy_user: the user of a request.
|
|
43
49
|
interface _UserReader
|
|
44
50
|
def call: (context? context) -> untyped
|
|
@@ -100,8 +106,6 @@ module Lighthouse
|
|
|
100
106
|
def self.directive_map: () -> Hash[untyped, untyped]
|
|
101
107
|
|
|
102
108
|
# An ActiveSupport::Cache::Store; Rails.cache by default.
|
|
103
|
-
def self.cache_store: () -> untyped
|
|
104
|
-
def self.cache_store=: (untyped store) -> untyped
|
|
105
109
|
|
|
106
110
|
def self.transactional_mutations: () -> bool
|
|
107
111
|
def self.transactional_mutations=: (bool? enabled) -> bool?
|
|
@@ -132,6 +136,10 @@ module Lighthouse
|
|
|
132
136
|
def self.convert_empty_strings_to_null: () -> bool
|
|
133
137
|
def self.convert_empty_strings_to_null=: (bool? enabled) -> bool?
|
|
134
138
|
|
|
139
|
+
def self.global_id: () -> _GlobalId
|
|
140
|
+
def self.global_id=: (_GlobalId? codec) -> _GlobalId?
|
|
141
|
+
|
|
142
|
+
|
|
135
143
|
# Whether @broadcast triggers in an ActiveJob (Lighthouse::GraphQL::BroadcastJob)
|
|
136
144
|
# unless the directive says shouldQueue; and the job's queue (nil: ActiveJob's default).
|
|
137
145
|
def self.queue_broadcasts: () -> bool
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lighthouse-graphql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vaz Innovation
|
|
@@ -62,29 +62,32 @@ files:
|
|
|
62
62
|
- docs/events.md
|
|
63
63
|
- docs/federation.md
|
|
64
64
|
- docs/filtering-and-ordering.md
|
|
65
|
+
- docs/generators.md
|
|
65
66
|
- docs/getting-started.md
|
|
66
67
|
- docs/motivation.md
|
|
67
68
|
- docs/mutations.md
|
|
68
69
|
- docs/performance.md
|
|
69
70
|
- docs/relationships.md
|
|
71
|
+
- docs/relay.md
|
|
70
72
|
- docs/resolvers.md
|
|
71
73
|
- docs/security.md
|
|
72
74
|
- docs/subscriptions.md
|
|
75
|
+
- docs/types.md
|
|
76
|
+
- docs/uploads.md
|
|
73
77
|
- docs/validation.md
|
|
74
78
|
- lib/generators/lighthouse/base_classes.rb
|
|
75
79
|
- lib/generators/lighthouse/builder_generator.rb
|
|
76
80
|
- lib/generators/lighthouse/class_generator.rb
|
|
81
|
+
- lib/generators/lighthouse/install/graphql_ruby_install.rb
|
|
77
82
|
- lib/generators/lighthouse/install/install_generator.rb
|
|
78
83
|
- lib/generators/lighthouse/install/templates/graphql.config.yml.tt
|
|
79
84
|
- lib/generators/lighthouse/install/templates/graphql_controller.rb.tt
|
|
80
85
|
- lib/generators/lighthouse/install/templates/initializer.rb.tt
|
|
81
86
|
- lib/generators/lighthouse/install/templates/schema.graphql.tt
|
|
82
|
-
- lib/generators/lighthouse/install/templates/schema.rb.tt
|
|
83
|
-
- lib/generators/lighthouse/mutation_generator.rb
|
|
84
|
-
- lib/generators/lighthouse/query_generator.rb
|
|
85
87
|
- lib/generators/lighthouse/subscription_generator.rb
|
|
86
88
|
- lib/generators/lighthouse/validator_generator.rb
|
|
87
89
|
- lib/lighthouse-graphql.rb
|
|
90
|
+
- lib/lighthouse/async_mutation_job.rb
|
|
88
91
|
- lib/lighthouse/broadcast_job.rb
|
|
89
92
|
- lib/lighthouse/broadcasting.rb
|
|
90
93
|
- lib/lighthouse/cache_control.rb
|
|
@@ -101,11 +104,17 @@ files:
|
|
|
101
104
|
- lib/lighthouse/controller.rb
|
|
102
105
|
- lib/lighthouse/dataloader_sources/association_loader.rb
|
|
103
106
|
- lib/lighthouse/dataloader_sources/model_loader.rb
|
|
107
|
+
- lib/lighthouse/dataloader_sources/preload_loader.rb
|
|
104
108
|
- lib/lighthouse/dataloader_sources/relation_count_loader.rb
|
|
109
|
+
- lib/lighthouse/dataloader_sources/relation_counts_loader.rb
|
|
105
110
|
- lib/lighthouse/dataloader_sources/relation_page_loader.rb
|
|
111
|
+
- lib/lighthouse/default_resolve_dispatcher.rb
|
|
106
112
|
- lib/lighthouse/directive_resolvers/aggregate_resolver.rb
|
|
107
113
|
- lib/lighthouse/directive_resolvers/all_resolver.rb
|
|
114
|
+
- lib/lighthouse/directive_resolvers/argument_binding.rb
|
|
115
|
+
- lib/lighthouse/directive_resolvers/argument_transforms_resolver.rb
|
|
108
116
|
- lib/lighthouse/directive_resolvers/association_resolvers.rb
|
|
117
|
+
- lib/lighthouse/directive_resolvers/async_resolver.rb
|
|
109
118
|
- lib/lighthouse/directive_resolvers/auth_resolver.rb
|
|
110
119
|
- lib/lighthouse/directive_resolvers/base.rb
|
|
111
120
|
- lib/lighthouse/directive_resolvers/broadcast_resolver.rb
|
|
@@ -119,25 +128,39 @@ files:
|
|
|
119
128
|
- lib/lighthouse/directive_resolvers/field_resolver.rb
|
|
120
129
|
- lib/lighthouse/directive_resolvers/find_resolver.rb
|
|
121
130
|
- lib/lighthouse/directive_resolvers/first_resolver.rb
|
|
131
|
+
- lib/lighthouse/directive_resolvers/global_id_resolver.rb
|
|
122
132
|
- lib/lighthouse/directive_resolvers/guard_resolver.rb
|
|
133
|
+
- lib/lighthouse/directive_resolvers/lazy_load_resolver.rb
|
|
134
|
+
- lib/lighthouse/directive_resolvers/limit_resolver.rb
|
|
123
135
|
- lib/lighthouse/directive_resolvers/method_resolver.rb
|
|
124
136
|
- lib/lighthouse/directive_resolvers/model_mutation_resolver.rb
|
|
137
|
+
- lib/lighthouse/directive_resolvers/namespaced_resolver.rb
|
|
125
138
|
- lib/lighthouse/directive_resolvers/paginate_resolver.rb
|
|
126
139
|
- lib/lighthouse/directive_resolvers/rename_resolver.rb
|
|
127
140
|
- lib/lighthouse/directive_resolvers/resolution.rb
|
|
128
141
|
- lib/lighthouse/directive_resolvers/soft_delete_resolvers.rb
|
|
129
142
|
- lib/lighthouse/directive_resolvers/subscription_resolver.rb
|
|
143
|
+
- lib/lighthouse/directive_resolvers/throttle_resolver.rb
|
|
144
|
+
- lib/lighthouse/directive_resolvers/type_method_resolver.rb
|
|
130
145
|
- lib/lighthouse/directive_resolvers/validator_resolver.rb
|
|
131
146
|
- lib/lighthouse/directive_resolvers/where_conditions_applier.rb
|
|
147
|
+
- lib/lighthouse/directive_resolvers/with_count_resolver.rb
|
|
148
|
+
- lib/lighthouse/directive_resolvers/with_resolver.rb
|
|
132
149
|
- lib/lighthouse/directives/arguments/base.rb
|
|
133
150
|
- lib/lighthouse/directives/arguments/filters.rb
|
|
151
|
+
- lib/lighthouse/directives/arguments/more_filters.rb
|
|
134
152
|
- lib/lighthouse/directives/arguments/order_by.rb
|
|
135
153
|
- lib/lighthouse/directives/arguments/soft_deletes.rb
|
|
154
|
+
- lib/lighthouse/directives/definitions.rb
|
|
155
|
+
- lib/lighthouse/directives/model_directive.rb
|
|
156
|
+
- lib/lighthouse/directives/node_directive.rb
|
|
136
157
|
- lib/lighthouse/directives/pagination_info_types.rb
|
|
137
158
|
- lib/lighthouse/directives/pagination_manipulator.rb
|
|
138
159
|
- lib/lighthouse/directives/registry.rb
|
|
139
160
|
- lib/lighthouse/directives/soft_deletes_directive.rb
|
|
161
|
+
- lib/lighthouse/directives/visibility_directive.rb
|
|
140
162
|
- lib/lighthouse/directives/where_conditions_directive.rb
|
|
163
|
+
- lib/lighthouse/enum_directive.rb
|
|
141
164
|
- lib/lighthouse/errors.rb
|
|
142
165
|
- lib/lighthouse/events.rb
|
|
143
166
|
- lib/lighthouse/events/execution_trace.rb
|
|
@@ -147,10 +170,12 @@ files:
|
|
|
147
170
|
- lib/lighthouse/federation_directives.rb
|
|
148
171
|
- lib/lighthouse/field_resolver_stack.rb
|
|
149
172
|
- lib/lighthouse/generated_resolvers.rb
|
|
173
|
+
- lib/lighthouse/global_id.rb
|
|
150
174
|
- lib/lighthouse/graphql.rb
|
|
151
175
|
- lib/lighthouse/graphql/version.rb
|
|
152
176
|
- lib/lighthouse/graphql_ruby_patches.rb
|
|
153
177
|
- lib/lighthouse/ide_helper.rb
|
|
178
|
+
- lib/lighthouse/multipart_request.rb
|
|
154
179
|
- lib/lighthouse/namespaces.rb
|
|
155
180
|
- lib/lighthouse/nested_mutations.rb
|
|
156
181
|
- lib/lighthouse/nested_mutations/belongs_to_operations.rb
|
|
@@ -158,6 +183,7 @@ files:
|
|
|
158
183
|
- lib/lighthouse/nested_mutations/has_operations.rb
|
|
159
184
|
- lib/lighthouse/nested_mutations/many_to_many_operations.rb
|
|
160
185
|
- lib/lighthouse/nested_mutations/partitioner.rb
|
|
186
|
+
- lib/lighthouse/node_registry.rb
|
|
161
187
|
- lib/lighthouse/pagination.rb
|
|
162
188
|
- lib/lighthouse/persisted_queries.rb
|
|
163
189
|
- lib/lighthouse/persisted_queries/batch.rb
|
|
@@ -165,8 +191,12 @@ files:
|
|
|
165
191
|
- lib/lighthouse/rb_lighthouse.rb
|
|
166
192
|
- lib/lighthouse/reference_resolver.rb
|
|
167
193
|
- lib/lighthouse/relation_batch.rb
|
|
194
|
+
- lib/lighthouse/relation_counts.rb
|
|
195
|
+
- lib/lighthouse/resolve_info.rb
|
|
168
196
|
- lib/lighthouse/resolver_bases.rb
|
|
169
197
|
- lib/lighthouse/resolver_dispatch.rb
|
|
198
|
+
- lib/lighthouse/root_types.rb
|
|
199
|
+
- lib/lighthouse/scalar_coercion.rb
|
|
170
200
|
- lib/lighthouse/schema_cache.rb
|
|
171
201
|
- lib/lighthouse/schema_factory.rb
|
|
172
202
|
- lib/lighthouse/schema_generator.rb
|
|
@@ -177,10 +207,16 @@ files:
|
|
|
177
207
|
- lib/lighthouse/support/authorization.rb
|
|
178
208
|
- lib/lighthouse/support/batching.rb
|
|
179
209
|
- lib/lighthouse/support/directive_args.rb
|
|
210
|
+
- lib/lighthouse/support/environment.rb
|
|
180
211
|
- lib/lighthouse/support/model_resolver.rb
|
|
181
212
|
- lib/lighthouse/support/naming.rb
|
|
213
|
+
- lib/lighthouse/support/preload.rb
|
|
182
214
|
- lib/lighthouse/support/soft_deletes.rb
|
|
183
215
|
- lib/lighthouse/tasks/lighthouse.rake
|
|
216
|
+
- lib/lighthouse/type.rb
|
|
217
|
+
- lib/lighthouse/type_resolution.rb
|
|
218
|
+
- lib/lighthouse/upload.rb
|
|
219
|
+
- lib/lighthouse/upload_store.rb
|
|
184
220
|
- lib/lighthouse/where_conditions/operator_map.rb
|
|
185
221
|
- sig/lighthouse.rbs
|
|
186
222
|
- sig/lighthouse/controller.rbs
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Executable schema built by Lighthouse from the SDL in <%= sdl_folder %>/. The
|
|
4
|
-
# Dataloader batches relationship loads (@hasMany, @belongsTo, ...) to avoid N+1.
|
|
5
|
-
# base_types names the base classes: every class a Query, Mutation or Subscription
|
|
6
|
-
# field resolves to must inherit from its kind's, and a Subscription field with no
|
|
7
|
-
# class of its own resolves with the subscription one.
|
|
8
|
-
<%= schema_name %> = Lighthouse::GraphQL::RbLightHouse.get_schema(
|
|
9
|
-
options: {
|
|
10
|
-
using: { GraphQL::Dataloader => {} },
|
|
11
|
-
base_types: {
|
|
12
|
-
resolver: <%= base_class_constant(:queries) %>,
|
|
13
|
-
mutation: <%= base_class_constant(:mutations) %>,
|
|
14
|
-
subscription: <%= base_class_constant(:subscriptions) %>
|
|
15
|
-
# object: Types::BaseObject, ... # graphql-ruby's base classes of the SDL's types
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
# Refuse expensive queries before they run (see the gem's docs/security.md):
|
|
21
|
-
# <%= schema_name %>.max_complexity(300)
|
|
22
|
-
# <%= schema_name %>.max_depth(12)
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'class_generator'
|
|
4
|
-
|
|
5
|
-
# `rails generate lighthouse:mutation PublishPost` — the class (a
|
|
6
|
-
# Mutations::BaseMutation, so a GraphQL::Schema::Mutation) that `Mutation.publishPost`
|
|
7
|
-
# resolves to by convention.
|
|
8
|
-
class Lighthouse::Generators::MutationGenerator < Lighthouse::Generators::ClassGenerator
|
|
9
|
-
desc 'Generate the class of a Mutation field (a Mutations::BaseMutation) in app/graphql/mutations.'
|
|
10
|
-
|
|
11
|
-
private
|
|
12
|
-
|
|
13
|
-
def kind = :mutations
|
|
14
|
-
|
|
15
|
-
def body
|
|
16
|
-
<<~RUBY
|
|
17
|
-
# Resolves Mutation.#{class_basename.camelize(:lower)}. Keyword arguments are the field's
|
|
18
|
-
# arguments in snake_case; `object` and `context` are available. Raise
|
|
19
|
-
# GraphQL::ExecutionError for failures the client should see.
|
|
20
|
-
class #{class_basename} < #{superclass_name}
|
|
21
|
-
def resolve(**args)
|
|
22
|
-
raise NotImplementedError
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
RUBY
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def usage
|
|
29
|
-
"Mutation.#{class_basename.camelize(:lower)} resolves to #{full_class_name} — add the field to your SDL without a resolving directive."
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'class_generator'
|
|
4
|
-
|
|
5
|
-
# `rails generate lighthouse:query LatestPosts` — the resolver (a
|
|
6
|
-
# Resolvers::BaseResolver, so a GraphQL::Schema::Resolver) that `Query.latestPosts`
|
|
7
|
-
# resolves to by convention.
|
|
8
|
-
class Lighthouse::Generators::QueryGenerator < Lighthouse::Generators::ClassGenerator
|
|
9
|
-
desc 'Generate the resolver of a Query field (a Resolvers::BaseResolver) in app/graphql/resolvers.'
|
|
10
|
-
|
|
11
|
-
private
|
|
12
|
-
|
|
13
|
-
def kind = :queries
|
|
14
|
-
|
|
15
|
-
def body
|
|
16
|
-
<<~RUBY
|
|
17
|
-
# Resolves Query.#{class_basename.camelize(:lower)}. Keyword arguments are the field's
|
|
18
|
-
# arguments in snake_case; `object` and `context` are available.
|
|
19
|
-
class #{class_basename} < #{superclass_name}
|
|
20
|
-
def resolve(**args)
|
|
21
|
-
raise NotImplementedError
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
RUBY
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def usage
|
|
28
|
-
"Query.#{class_basename.camelize(:lower)} resolves to #{full_class_name} — add the field to your SDL without a resolving directive."
|
|
29
|
-
end
|
|
30
|
-
end
|