graphiti_gql 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85aecb8badace989fe7f60f3187c189a2d73ce0e99fd4725a8388ed01f2a9cd0
4
- data.tar.gz: 488756eb837219bde85b23a015bd6b6081fa2857c753c3e1837ab0fb7c758dc8
3
+ metadata.gz: 2a19fd68dc698b8a7284fe80686da9f50ee137365508241cd374e32aa6c505aa
4
+ data.tar.gz: d509f137452b90e94986c46267f8728160021de305e599ba47512af9366fe109
5
5
  SHA512:
6
- metadata.gz: ef8b222b5b932bbcb6f21cb8a9480341499086b3daffbb85657af89e208c7b639c4b75fc1ec7f432edb818673ae1e971689fc3f940d11176721a651220f60325
7
- data.tar.gz: 867db73b474375ab9189aa421c953f1ebb008f94ea906d163a4e70f6059ec7df3a699199322cd276f0227d6eebb80d15958587530a8e9e2282ad010139e8e926
6
+ metadata.gz: 22f8643e779d272d806d53179e5f83e6ca6157fc8c261e03ea3232fdf685faa35ad50c70bd3f5f0f445b8bd4578e616b9a538c9205e330a301a30f370b2c612a
7
+ data.tar.gz: ec6e08b56162dc2bb077bfd3cfc5fb5dcf8edcdc3680d940347eb33949f8530967e6d828e0b914f0920a406bad4807d83c643981f9ab0136dd7a3e9e422e068a
data/README.md CHANGED
@@ -1,3 +1,90 @@
1
1
  # GraphitiGql
2
2
 
3
- asdf
3
+ GraphQL bindings for [Graphiti](www.graphiti.dev).
4
+
5
+ Write code like this:
6
+
7
+ ```ruby
8
+ class EmployeeResource < ApplicationResource
9
+ attribute :first_name, :string
10
+ attribute :age, :integer
11
+
12
+ has_many :positions
13
+ end
14
+ ```
15
+
16
+ Get an API like this:
17
+
18
+ ```gql
19
+ query {
20
+ employees(
21
+ filter: { firstName: { match: "arha" } },
22
+ sort: [{ att: age, dir: desc }],
23
+ first: 10,
24
+ after: "abc123"
25
+ ) {
26
+ edges {
27
+ node {
28
+ id
29
+ firstName
30
+ age
31
+ positions {
32
+ nodes {
33
+ title
34
+ }
35
+ }
36
+ }
37
+ cursor
38
+ }
39
+ stats {
40
+ total {
41
+ count
42
+ }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Getting Started
49
+
50
+ ```ruby
51
+ # Gemfile
52
+ gem 'graphiti'
53
+ gem "graphiti-rails"
54
+ gem 'graphiti_gql'
55
+ ```
56
+
57
+ ```ruby
58
+ # config/routes.rb
59
+
60
+ Rails.application.routes.draw do
61
+ scope path: ApplicationResource.endpoint_namespace do
62
+ mount GraphitiGql::Engine, at: "/gql"
63
+ end
64
+ end
65
+ ```
66
+
67
+ Write your Graphiti code as normal, omit controllers.
68
+
69
+ ### How does it work?
70
+
71
+ This autogenerates `graphql-ruby` code by introspecting Graphiti Resources. Something like this happens under-the-hood:
72
+
73
+ ```ruby
74
+ field :employees, [EmployeeType], null: false do
75
+ argument :filter, EmployeeFilter, required: false
76
+ # ... etc ...
77
+ end
78
+
79
+ def employees(**arguments)
80
+ EmployeeResource.all(**arguments).to_a
81
+ end
82
+ ```
83
+
84
+ In practice it's more complicated, but this is the basic premise - use Graphiti resources to handle query and persistence operations; autogenerate `graphql-ruby` code to expose those Resources as an API. This means we play nicely with e.g. telemetry and error-handling libraries because it's all `graphql-ruby` under-the-hood...except for actually **performing** the operations, which is really more a Ruby thing than a GraphQL thing.
85
+
86
+ ### Caveats
87
+
88
+ This rethinks the responsibilities of Graphiti, coupling the execution cycle to `graphql-ruby`. We do this so we can play nicely with other gems in the GQL ecosystem, and saves on development time by offloading responsibilities. The downside is we can no longer run a `JSON:API` with the same codebase, and certain documentation may be out of date.
89
+
90
+ Longer-term, we should rip out only the parts of Graphiti we really need and redocument.
@@ -2,6 +2,7 @@ module GraphitiGql
2
2
  class Engine < ::Rails::Engine
3
3
  isolate_namespace GraphitiGql
4
4
 
5
+ # TODO improvable?
5
6
  config.after_initialize do
6
7
  # initializer "graphiti_gql.generate_schema" do
7
8
  Dir.glob("#{Rails.root}/app/resources/**/*").each { |f| require(f) }
@@ -10,11 +11,10 @@ module GraphitiGql
10
11
 
11
12
  initializer "graphiti_gql.define_controller" do
12
13
  require "#{Rails.root}/app/controllers/application_controller"
13
- app_controller = ::ApplicationController # todo
14
+ app_controller = GraphitiGql.config.application_controller || ::ApplicationController
14
15
 
15
16
  # rubocop:disable Lint/ConstantDefinitionInBlock(Standard)
16
17
  class GraphitiGql::ExecutionController < app_controller
17
- # register_exception Graphiti::Errors::UnreadableAttribute, message: true
18
18
  def execute
19
19
  params = request.params # avoid strong_parameters
20
20
  variables = params[:variables] || {}
@@ -17,5 +17,21 @@ module GraphitiGql
17
17
  "You are not authorized to read field #{@field}"
18
18
  end
19
19
  end
20
+
21
+ class NullFilter < Base
22
+ def initialize(name)
23
+ @name = name
24
+ end
25
+
26
+ def message
27
+ "Filter '#{@name}' does not support null"
28
+ end
29
+ end
30
+
31
+ class UnsupportedLast < Base
32
+ def message
33
+ "We do not currently support combining 'last' with 'before' or 'after'"
34
+ end
35
+ end
20
36
  end
21
37
  end
@@ -1,71 +1,218 @@
1
1
  # These should be in Graphiti itself, but can't do it quite yet b/c GQL coupling.
2
2
  # Ideally we eventually rip out the parts of Graphiti we need and roll this into
3
3
  # that effort.
4
- module ResourceExtras
5
- extend ActiveSupport::Concern
4
+ module GraphitiGql
5
+ module ResourceExtras
6
+ extend ActiveSupport::Concern
6
7
 
7
- included do
8
- class << self
9
- attr_accessor :graphql_name
8
+ included do
9
+ class << self
10
+ attr_accessor :graphql_name
11
+ end
12
+ end
13
+
14
+ class_methods do
15
+ def attribute(*args)
16
+ super(*args).tap do
17
+ opts = args.extract_options!
18
+ att = config[:attributes][args[0]]
19
+ att[:deprecation_reason] = opts[:deprecation_reason]
20
+ att[:null] = opts.key?(:null) ? opts[:null] : args[0] != :id
21
+ att[:name] = args.first # for easier lookup
22
+ end
23
+ end
10
24
  end
11
25
  end
26
+ Graphiti::Resource.send(:include, ResourceExtras)
27
+
28
+ module FilterExtras
29
+ def filter_param
30
+ default_filter = resource.default_filter if resource.respond_to?(:default_filter)
31
+ default_filter ||= {}
32
+ default_filter.merge(super)
33
+ end
34
+
35
+ def each_filter
36
+ super do |filter, operator, value|
37
+ unless filter.values[0][:allow_nil]
38
+ has_nil = value.nil? || value.is_a?(Array) && value.any?(&:nil?)
39
+ raise Errors::NullFilter.new(filter.keys.first) if has_nil
40
+ end
41
+ yield filter, operator, value
42
+ end
43
+ end
44
+
45
+ # Only for alias, tiny diff
46
+ def filter_via_adapter(filter, operator, value)
47
+ type_name = ::Graphiti::Types.name_for(filter.values.first[:type])
48
+ method = :"filter_#{type_name}_#{operator}"
49
+ name = filter.keys.first
50
+ name = resource.all_attributes[name][:alias] || name
51
+
52
+ if resource.adapter.respond_to?(method)
53
+ resource.adapter.send(method, @scope, name, value)
54
+ else
55
+ raise ::Graphiti::Errors::AdapterNotImplemented.new \
56
+ resource.adapter, name, method
57
+ end
58
+ end
59
+ end
60
+ Graphiti::Scoping::Filter.send(:prepend, FilterExtras)
61
+
62
+ module SortAliasExtras
63
+ def each_sort
64
+ sort_param.each do |sort_hash|
65
+ name = sort_hash.keys.first
66
+ name = resource.all_attributes[name][:alias] || name
67
+ direction = sort_hash.values.first
68
+ yield name, direction
69
+ end
70
+ end
71
+ end
72
+ Graphiti::Scoping::Sort.send(:prepend, SortAliasExtras)
73
+
74
+ module PaginateExtras
75
+ def apply
76
+ if query_hash[:reverse] && (before_cursor || after_cursor)
77
+ raise ::GraphitiGql::Errors::UnsupportedLast
78
+ end
79
+ super
80
+ end
81
+
82
+ def offset
83
+ offset = 0
84
+
85
+ if (value = page_param[:offset])
86
+ offset = value.to_i
87
+ end
12
88
 
13
- class_methods do
14
- def attribute(*args)
15
- super(*args).tap do
16
- opts = args.extract_options!
17
- att = config[:attributes][args[0]]
18
- att[:deprecation_reason] = opts[:deprecation_reason]
19
- att[:null] = opts.key?(:null) ? opts[:null] : args[0] != :id
89
+ if before_cursor&.key?(:offset)
90
+ if page_param.key?(:number)
91
+ raise Errors::UnsupportedBeforeCursor
92
+ end
93
+
94
+ offset = before_cursor[:offset] - (size * number) - 1
95
+ offset = 0 if offset.negative?
96
+ end
97
+
98
+ if after_cursor&.key?(:offset)
99
+ offset = after_cursor[:offset]
100
+ end
101
+
102
+ offset
103
+ end
104
+
105
+ # TODO memoize
106
+ def size
107
+ size = super
108
+ if before_cursor && after_cursor
109
+ diff = before_cursor[:offset] - after_cursor[:offset] - 1
110
+ size = [size, diff].min
111
+ elsif before_cursor
112
+ comparator = query_hash[:reverse] ? :>= : :<=
113
+ if before_cursor[:offset].send(comparator, size)
114
+ diff = before_cursor[:offset] - size
115
+ size = [size, diff].min
116
+ size = 1 if size.zero?
117
+ end
20
118
  end
119
+ size
21
120
  end
22
121
  end
23
- end
122
+ Graphiti::Scoping::Paginate.send(:prepend, PaginateExtras)
24
123
 
25
- Graphiti::Resource.send(:include, ResourceExtras)
124
+ module StatsExtras
125
+ def calculate_stat(name, function)
126
+ config = @resource.all_attributes[name] || {}
127
+ name = config[:alias] || name
128
+ super(name, function)
129
+ end
130
+ end
131
+ Graphiti::Stats::Payload.send(:prepend, StatsExtras)
132
+
133
+ Graphiti::Types[:big_integer] = Graphiti::Types[:integer].dup
134
+ Graphiti::Types[:big_integer][:graphql_type] = ::GraphQL::Types::BigInt
135
+
136
+ ######## support precise_datetime ###########
137
+ #############################################
138
+ definition = Dry::Types::Nominal.new(String)
139
+ _out = definition.constructor do |input|
140
+ input.utc.round(10).iso8601(6)
141
+ end
26
142
 
27
- module FilterExtras
28
- def filter_param
29
- default_filter = resource.default_filter if resource.respond_to?(:default_filter)
30
- default_filter ||= {}
31
- default_filter.merge(super)
143
+ _in = definition.constructor do |input|
144
+ Time.zone.parse(input)
32
145
  end
33
- end
34
- Graphiti::Scoping::Filter.send(:prepend, FilterExtras)
35
-
36
- # ==================================================
37
- # Below is all to support pagination argument 'last'
38
- # ==================================================
39
- module SortExtras
40
- def sort_param
41
- param = super
42
- if query_hash[:reverse]
43
- param = [{ id: :asc }] if param == []
44
- param = param.map do |p|
45
- {}.tap do |hash|
46
- dir = p[p.keys.first]
47
- dir = dir == :asc ? :desc : :asc
48
- hash[p.keys.first] = dir
146
+
147
+ # Register it with Graphiti
148
+ Graphiti::Types[:precise_datetime] = {
149
+ params: _in,
150
+ read: _out,
151
+ write: _in,
152
+ kind: 'scalar',
153
+ canonical_name: :precise_datetime,
154
+ description: 'Datetime with milliseconds'
155
+ }
156
+
157
+ module ActiveRecordAdapterExtras
158
+ extend ActiveSupport::Concern
159
+
160
+ included do
161
+ alias_method :filter_precise_datetime_lt, :filter_lt
162
+ alias_method :filter_precise_datetime_lte, :filter_lte
163
+ alias_method :filter_precise_datetime_gt, :filter_gt
164
+ alias_method :filter_precise_datetime_gte, :filter_gte
165
+ alias_method :filter_precise_datetime_eq, :filter_eq
166
+ alias_method :filter_precise_datetime_not_eq, :filter_not_eq
167
+ end
168
+ end
169
+ Graphiti::Adapters::ActiveRecord.send(:include, ActiveRecordAdapterExtras)
170
+
171
+ Graphiti::Adapters::Abstract.class_eval do
172
+ class << self
173
+ alias :old_default_operators :default_operators
174
+ def default_operators
175
+ old_default_operators.merge(precise_datetime: numerical_operators)
176
+ end
177
+ end
178
+ end
179
+ ########## end support precise_datetime ############
180
+
181
+ # ==================================================
182
+ # Below is all to support pagination argument 'last'
183
+ # ==================================================
184
+ module SortExtras
185
+ def sort_param
186
+ param = super
187
+ if query_hash[:reverse]
188
+ param = [{ id: :asc }] if param == []
189
+ param = param.map do |p|
190
+ {}.tap do |hash|
191
+ dir = p[p.keys.first]
192
+ dir = dir == :asc ? :desc : :asc
193
+ hash[p.keys.first] = dir
194
+ end
49
195
  end
50
196
  end
197
+ param
51
198
  end
52
- param
53
199
  end
54
- end
55
- Graphiti::Scoping::Sort.send(:prepend, SortExtras)
56
- module QueryExtras
57
- def hash
58
- hash = super
59
- hash[:reverse] = true if @params[:reverse]
60
- hash
200
+ Graphiti::Scoping::Sort.send(:prepend, SortExtras)
201
+ module QueryExtras
202
+ def hash
203
+ hash = super
204
+ hash[:reverse] = true if @params[:reverse]
205
+ hash
206
+ end
61
207
  end
62
- end
63
- Graphiti::Query.send(:prepend, QueryExtras)
64
- module ScopeExtras
65
- def resolve(*args)
66
- results = super
67
- results.reverse! if @query.hash[:reverse]
68
- results
208
+
209
+ Graphiti::Query.send(:prepend, QueryExtras)
210
+ module ScopeExtras
211
+ def resolve(*args)
212
+ results = super
213
+ results.reverse! if @query.hash[:reverse]
214
+ results
215
+ end
69
216
  end
70
- end
71
- Graphiti::Scope.send(:prepend, ScopeExtras)
217
+ Graphiti::Scope.send(:prepend, ScopeExtras)
218
+ end
@@ -3,17 +3,19 @@ module GraphitiGql
3
3
  module Fields
4
4
  class Attribute
5
5
  def initialize(name, config)
6
- @name = name
7
6
  @config = config
7
+ @name = name
8
+ @alias = config[:alias]
8
9
  end
9
10
 
10
11
  def apply(type)
11
12
  is_nullable = !!@config[:null]
12
13
  _config = @config
13
14
  _name = @name
15
+ _alias = @alias
14
16
  opts = @config.slice(:null, :deprecation_reason)
15
- type.field(@name, field_type, **opts)
16
- type.define_method @name do
17
+ type.field(_name, field_type, **opts)
18
+ type.define_method _name do
17
19
  if (readable = _config[:readable]).is_a?(Symbol)
18
20
  resource = object.instance_variable_get(:@__graphiti_resource)
19
21
  unless resource.send(readable)
@@ -24,7 +26,7 @@ module GraphitiGql
24
26
  value = if _config[:proc]
25
27
  instance_eval(&_config[:proc])
26
28
  else
27
- object.send(_name)
29
+ object.send(_alias || _name)
28
30
  end
29
31
  return if value.nil?
30
32
  Graphiti::Types[_config[:type]][:read].call(value)
@@ -58,18 +58,21 @@ module GraphitiGql
58
58
  filter_graphql_name = "#{type_name}Filter#{filter_name.to_s.camelize(:lower)}"
59
59
  klass.graphql_name(filter_graphql_name)
60
60
  filter_config[:operators].keys.each do |operator|
61
- canonical_graphiti_type = Graphiti::Types
62
- .name_for(filter_config[:type])
63
- type = GQL_TYPE_MAP[canonical_graphiti_type]
64
- type = String if filter_name == :id
65
- required = !!filter_config[:required] && operator == "eq"
66
-
61
+ graphiti_type = Graphiti::Types[filter_config[:type]]
62
+ type = graphiti_type[:graphql_type]
63
+ if !type
64
+ canonical_graphiti_type = Graphiti::Types
65
+ .name_for(filter_config[:type])
66
+ type = GQL_TYPE_MAP[canonical_graphiti_type]
67
+ type = String if filter_name == :id
68
+ end
69
+
67
70
  if (allowlist = filter_config[:allow])
68
71
  type = define_allowlist_type(filter_graphql_name, allowlist)
69
72
  end
70
73
 
71
74
  type = [type] unless !!filter_config[:single]
72
- klass.argument operator, type, required: required
75
+ klass.argument operator, type, required: false
73
76
  end
74
77
  klass
75
78
  end
@@ -1,21 +1,28 @@
1
1
  module GraphitiGql
2
2
  class Schema
3
+ class PreciseDatetime < GraphQL::Types::ISO8601DateTime
4
+ self.time_precision = 6
5
+ end
6
+
3
7
  GQL_TYPE_MAP = {
4
8
  integer_id: String,
5
9
  string: String,
6
10
  uuid: String,
7
11
  integer: Integer,
12
+ big_integer: GraphQL::Types::BigInt,
8
13
  float: Float,
9
14
  boolean: GraphQL::Schema::Member::GraphQLTypeNames::Boolean,
10
15
  date: GraphQL::Types::ISO8601Date,
11
16
  datetime: GraphQL::Types::ISO8601DateTime,
17
+ precise_datetime: PreciseDatetime,
12
18
  hash: GraphQL::Types::JSON,
13
19
  array: [GraphQL::Types::JSON],
14
20
  array_of_strings: [String],
15
21
  array_of_integers: [Integer],
16
22
  array_of_floats: [Float],
17
23
  array_of_dates: [GraphQL::Types::ISO8601Date],
18
- array_of_datetimes: [GraphQL::Types::ISO8601DateTime]
24
+ array_of_datetimes: [GraphQL::Types::ISO8601DateTime],
25
+ array_of_precise_datetimes: [PreciseDatetime]
19
26
  }
20
27
 
21
28
  class RelayConnectionExtension < GraphQL::Schema::Field::ConnectionExtension
@@ -25,6 +32,14 @@ module GraphitiGql
25
32
  end
26
33
  end
27
34
 
35
+ def self.registry
36
+ Registry.instance
37
+ end
38
+
39
+ def self.print
40
+ GraphQL::Schema::Printer.print_schema(GraphitiGql.schema)
41
+ end
42
+
28
43
  def initialize(resources)
29
44
  @resources = resources
30
45
  end
@@ -35,6 +50,7 @@ module GraphitiGql
35
50
  klass.use(GraphQL::Batch)
36
51
  klass.connections.add(ResponseShim, Connection)
37
52
  klass.connections.add(Array, ToManyConnection)
53
+ klass.orphan_types [GraphQL::Types::JSON]
38
54
  klass
39
55
  end
40
56
  end
@@ -0,0 +1,150 @@
1
+ module GraphitiGql
2
+ module SpecHelper
3
+ extend ActiveSupport::Concern
4
+
5
+ class Node < OpenStruct
6
+ def decoded_id
7
+ Base64.decode64(self.id)
8
+ end
9
+
10
+ def int_id
11
+ decoded_id.to_i
12
+ end
13
+ end
14
+
15
+ class Util
16
+ def self.underscore(hash)
17
+ hash.deep_transform_keys { |k| k.to_s.underscore.to_sym }
18
+ end
19
+ end
20
+
21
+ def query
22
+ name = Schema.registry.key_for(resource)
23
+ q = %|
24
+ query #{name} (
25
+ $filter: #{name}Filter,
26
+ $sort: [#{name}Sort!],
27
+ $first: Int,
28
+ $last: Int,
29
+ $before: String,
30
+ $after: String,
31
+ ) {
32
+ #{resource.graphql_entrypoint} (
33
+ filter: $filter,
34
+ sort: $sort,
35
+ first: $first,
36
+ last: $last,
37
+ before: $before,
38
+ after: $after,
39
+ ) {
40
+ edges {
41
+ node {|
42
+
43
+ fields.each do |name|
44
+ q << %|
45
+ #{name.to_s.camelize(:lower)}|
46
+ end
47
+
48
+ q << %|
49
+ }
50
+ }
51
+ pageInfo {
52
+ startCursor
53
+ endCursor
54
+ hasNextPage
55
+ hasPreviousPage
56
+ }|
57
+
58
+ if params[:stats]
59
+ q << %|
60
+ stats {|
61
+ params[:stats].each_pair do |name, calculations|
62
+ q << %|
63
+ #{name.to_s.camelize(:lower)} {|
64
+ Array(calculations).each do |calc|
65
+ q << %|
66
+ #{calc.to_s.camelize(:lower)}|
67
+ end
68
+
69
+ q << %|
70
+ }|
71
+ end
72
+ q << %|
73
+ }|
74
+ end
75
+
76
+ q << %|
77
+ }
78
+ }
79
+ |
80
+
81
+ q
82
+ end
83
+
84
+ def run
85
+ lambda do
86
+ gql_params = params.deep_transform_keys { |key| key.to_s.camelize(:lower).to_sym }
87
+ (gql_params[:sort] || []).each do |sort|
88
+ sort[:att] = sort[:att].to_s.camelize(:lower)
89
+ sort[:dir] = sort[:dir].to_s
90
+ end
91
+ GraphitiGql.run(query, gql_params, ctx).deep_symbolize_keys
92
+ end
93
+ end
94
+
95
+ def run!
96
+ @response = run.call
97
+ end
98
+
99
+ def response
100
+ @response ||= run!
101
+ end
102
+
103
+ def errors
104
+ response[:errors]
105
+ end
106
+
107
+ def error_messages
108
+ response[:errors].map { |e| e[:message] }
109
+ end
110
+
111
+ def nodes
112
+ return [] unless data
113
+ nodes = edges.map { |e| Util.underscore(e[:node]) }
114
+ nodes.map { |n| ::GraphitiGql::SpecHelper::Node.new(n) }
115
+ end
116
+
117
+ def data
118
+ if response.key?(:data)
119
+ response[:data]
120
+ else
121
+ raise "Tried to access 'data', but these errors were returned instead: #{error_messages.join(". ")}."
122
+ end
123
+ end
124
+
125
+ def edges
126
+ data[data.keys.first][:edges]
127
+ end
128
+
129
+ def stats
130
+ Util.underscore(data[data.keys.first][:stats])
131
+ end
132
+
133
+ def page_info
134
+ Util.underscore(data[data.keys.first][:pageInfo])
135
+ end
136
+
137
+ included do
138
+ let(:params) { {} }
139
+ let(:resource) { described_class }
140
+ let(:ctx) { {} }
141
+ let(:fields) do
142
+ fields = []
143
+ resource.attributes.each_pair do |name, config|
144
+ (fields << name) if config[:readable]
145
+ end
146
+ fields
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,3 +1,3 @@
1
1
  module GraphitiGql
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/graphiti_gql.rb CHANGED
@@ -29,6 +29,10 @@ require "graphiti_gql/engine" if defined?(Rails)
29
29
  module GraphitiGql
30
30
  class Error < StandardError; end
31
31
 
32
+ class Configuration
33
+ attr_accessor :application_controller
34
+ end
35
+
32
36
  def self.schema!
33
37
  Schema::Registry.instance.clear
34
38
  resources ||= Graphiti.resources.reject(&:abstract_class?)
@@ -39,6 +43,14 @@ module GraphitiGql
39
43
  @schema
40
44
  end
41
45
 
46
+ def self.config
47
+ @config ||= Configuration.new
48
+ end
49
+
50
+ def self.configure
51
+ yield config
52
+ end
53
+
42
54
  def self.entrypoints=(val)
43
55
  @entrypoints = val
44
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti_gql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-06-03 00:00:00.000000000 Z
11
+ date: 2022-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -108,7 +108,7 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '7.0'
111
- description:
111
+ description:
112
112
  email:
113
113
  - richmolj@gmail.com
114
114
  executables: []
@@ -160,13 +160,14 @@ files:
160
160
  - lib/graphiti_gql/schema/registry.rb
161
161
  - lib/graphiti_gql/schema/resource_type.rb
162
162
  - lib/graphiti_gql/schema/util.rb
163
+ - lib/graphiti_gql/spec_helper.rb
163
164
  - lib/graphiti_gql/version.rb
164
165
  homepage: https://www.graphiti.dev
165
166
  licenses:
166
167
  - MIT
167
168
  metadata:
168
169
  homepage_uri: https://www.graphiti.dev
169
- post_install_message:
170
+ post_install_message:
170
171
  rdoc_options: []
171
172
  require_paths:
172
173
  - lib
@@ -181,8 +182,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
182
  - !ruby/object:Gem::Version
182
183
  version: '0'
183
184
  requirements: []
184
- rubygems_version: 3.3.7
185
- signing_key:
185
+ rubygems_version: 3.0.3.1
186
+ signing_key:
186
187
  specification_version: 4
187
188
  summary: GraphQL support for Graphiti
188
189
  test_files: []