Module: Quail::SchemaBuilder

Defined in:
lib/quail/schema_builder.rb,
lib/quail/schema_builder/discovery.rb,
lib/quail/schema_builder/type_definitions.rb

Overview

Assembles the full GraphQL schema from registered resources, custom queries, and mutations.

Defined Under Namespace

Modules: Discovery, TypeDefinitions

Class Method Summary collapse

Class Method Details

.attach_root_types(schema_class) ⇒ Object



49
50
51
52
53
54
55
56
57
# File 'lib/quail/schema_builder.rb', line 49

def self.attach_root_types(schema_class)
  query_type = build_query_type
  mutation_type = build_mutation_type
  subscription_type = build_subscription_type

  schema_class.query(query_type) if query_type
  schema_class.mutation(mutation_type) if mutation_type
  schema_class.subscription(subscription_type) if subscription_type
end

.build_mutation_typeObject



89
90
91
92
93
94
95
96
97
98
# File 'lib/quail/schema_builder.rb', line 89

def self.build_mutation_type
  mutations = collect_all_mutations
  return nil if mutations.empty?

  base = Quail.base_object_class || GraphQL::Schema::Object
  Class.new(base) do
    graphql_name "Mutation"
    mutations.each { |name, klass| field name, mutation: klass }
  end
end

.build_query_typeObject



64
65
66
67
68
69
70
71
# File 'lib/quail/schema_builder.rb', line 64

def self.build_query_type
  fields = collect_resource_query_fields
  custom = Discovery.custom_queries
  extra = Quail.extra_queries
  return nil if fields.empty? && extra.empty? && custom.empty?

  create_query_class(fields, custom, extra)
end

.build_subscription_typeObject



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/quail/schema_builder.rb', line 115

def self.build_subscription_type
  fields = {}
  Quail.registry.each_value { |r| fields.merge!(r.subscription_fields) }
  return nil if fields.empty?

  base = Quail.base_object_class || GraphQL::Schema::Object
  Class.new(base) do
    graphql_name "Subscription"
    TypeDefinitions.define_subscription_fields(self, fields)
  end
end

.call(schema_class, &block) ⇒ Object



11
12
13
14
15
# File 'lib/quail/schema_builder.rb', line 11

def self.call(schema_class, &block)
  schema_class.instance_variable_set(:@quail_configured, false)
  install_lazy_hooks(schema_class)
  block&.call(schema_class)
end

.collect_all_mutationsObject



100
101
102
103
104
105
# File 'lib/quail/schema_builder.rb', line 100

def self.collect_all_mutations
  mutations = collect_resource_mutations
  Discovery.custom_mutations.each { |name, klass| mutations[name] = klass }
  Quail.extra_mutations.each { |name, klass| mutations[name.to_sym] = klass }
  mutations
end

.collect_resource_mutationsObject



107
108
109
110
111
112
113
# File 'lib/quail/schema_builder.rb', line 107

def self.collect_resource_mutations
  mutations = {}
  Quail.registry.each_value do |r|
    r.mutations.each { |action, klass| mutations[:"#{r.model_class.name.underscore}_#{action}"] = klass }
  end
  mutations
end

.collect_resource_query_fieldsObject



83
84
85
86
87
# File 'lib/quail/schema_builder.rb', line 83

def self.collect_resource_query_fields
  fields = {}
  Quail.registry.each_value { |r| fields.merge!(r.query_fields) }
  fields
end

.configure!(schema_class) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/quail/schema_builder.rb', line 26

def self.configure!(schema_class)
  @configure_mutex.synchronize do
    return if schema_class.instance_variable_get(:@quail_configured)

    eager_load_resources if defined?(Rails)
    Resource::TypeBuilder.build_all
    eager_load_resolvers if defined?(Rails)
    attach_root_types(schema_class)
    install_defaults(schema_class)
    schema_class.instance_variable_set(:@quail_configured, true)
  end
end

.create_query_class(fields, custom, extra) ⇒ Object



73
74
75
76
77
78
79
80
81
# File 'lib/quail/schema_builder.rb', line 73

def self.create_query_class(fields, custom, extra)
  base = Quail.base_object_class || GraphQL::Schema::Object
  Class.new(base) do
    graphql_name "Query"
    TypeDefinitions.define_query_fields(self, fields)
    custom.each { |name, klass| field name, resolver: klass }
    TypeDefinitions.define_extra_query_fields(self, extra)
  end
end

.eager_load_resolversObject



43
44
45
46
47
# File 'lib/quail/schema_builder.rb', line 43

def self.eager_load_resolvers
  %w[mutations queries].each do |dir|
    Dir[Rails.root.join("app/graphql/#{dir}/**/*.rb")].each { |f| require f }
  end
end

.eager_load_resourcesObject



39
40
41
# File 'lib/quail/schema_builder.rb', line 39

def self.eager_load_resources
  Dir[Rails.root.join("app/graphql/resources/**/*.rb")].each { |f| require f }
end

.install_defaults(schema_class) ⇒ Object



59
60
61
62
# File 'lib/quail/schema_builder.rb', line 59

def self.install_defaults(schema_class)
  schema_class.use GraphQL::Dataloader unless schema_class.dataloader_class
  schema_class.use GraphQL::Subscriptions::ActionCableSubscriptions unless schema_class.subscriptions
end

.install_lazy_hooks(schema_class) ⇒ Object



17
18
19
20
21
22
23
24
# File 'lib/quail/schema_builder.rb', line 17

def self.install_lazy_hooks(schema_class)
  %i[multiplex execute to_definition].each do |method_name|
    schema_class.define_singleton_method(method_name) do |*args, **kwargs|
      Quail::SchemaBuilder.configure!(self) unless @quail_configured
      super(*args, **kwargs)
    end
  end
end