graphiti_gql 0.2.27 → 0.2.30
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/Gemfile.lock +1 -1
- data/lib/graphiti_gql/active_resource.rb +10 -8
- data/lib/graphiti_gql/engine.rb +11 -2
- data/lib/graphiti_gql/graphiti_hax.rb +87 -2
- data/lib/graphiti_gql/loaders/belongs_to.rb +15 -8
- data/lib/graphiti_gql/loaders/many.rb +9 -4
- data/lib/graphiti_gql/log_subscriber.rb +177 -0
- data/lib/graphiti_gql/schema/fields/attribute.rb +10 -2
- data/lib/graphiti_gql/schema/fields/show.rb +1 -1
- data/lib/graphiti_gql/schema/fields/stats.rb +9 -6
- data/lib/graphiti_gql/schema/list_arguments.rb +0 -1
- data/lib/graphiti_gql/schema/registry.rb +1 -2
- data/lib/graphiti_gql/schema/resource_type.rb +11 -0
- data/lib/graphiti_gql/schema.rb +4 -0
- data/lib/graphiti_gql/spec_helper.rb +3 -0
- data/lib/graphiti_gql/version.rb +1 -1
- data/lib/graphiti_gql.rb +18 -3
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a02dc0813b461497979a165c77f9116922559c46a54ba26a5130e31832c4bcef
|
|
4
|
+
data.tar.gz: 60a27d5d31c31a39d062d1a65a2c0278ce3ea6f3cba3c4900ed1332525a54460
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e2a9a66b5dd624962bfffdc733115f8cf0363846db434bf9dfbe76374cd94dd0b9411ccf2aa93ce543a144ad0505e9f8d1e39bcb34b5593a7f870e17d7f13440
|
|
7
|
+
data.tar.gz: eb5004a261e6b7b815eb16ab548591c27beda1231e0875103c75d059c885ed2311b7d8c2b4d1a651d08905facdb8478b328eb5f78ec0fb812dab8cfc5f175583
|
data/Gemfile.lock
CHANGED
|
@@ -37,18 +37,20 @@ module GraphitiGql
|
|
|
37
37
|
def edge(name, node_id)
|
|
38
38
|
found = @edges[name].empty? ? nil : @edges[name]
|
|
39
39
|
if found && node_id
|
|
40
|
-
found.find
|
|
40
|
+
found.find do |f|
|
|
41
|
+
gql_node_id = f.instance_variable_get(:@node_id)
|
|
42
|
+
gql_node_id == node_id ||
|
|
43
|
+
gql_node_id == node_id.to_s ||
|
|
44
|
+
gql_node_id == @resource.gid(node_id)
|
|
45
|
+
end
|
|
41
46
|
else
|
|
42
47
|
found
|
|
43
48
|
end
|
|
44
49
|
end
|
|
45
50
|
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def int_id
|
|
51
|
-
decoded_id.to_i
|
|
51
|
+
def _id
|
|
52
|
+
deserialized = @resource.deserialize_gid(id)
|
|
53
|
+
deserialized.to_i.to_s == deserialized ? deserialized.to_i : deserialized
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
|
|
@@ -77,7 +79,7 @@ module GraphitiGql
|
|
|
77
79
|
data # fire query
|
|
78
80
|
Node.new(underscore(data[data.keys.first]), @resource)
|
|
79
81
|
else
|
|
80
|
-
nodes.find { |n| n.id == id.to_s }
|
|
82
|
+
nodes.find { |n| n.id == id.to_s || n.id == @resource.gid(id) }
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
85
|
|
data/lib/graphiti_gql/engine.rb
CHANGED
|
@@ -2,11 +2,20 @@ module GraphitiGql
|
|
|
2
2
|
class Engine < ::Rails::Engine
|
|
3
3
|
isolate_namespace GraphitiGql
|
|
4
4
|
|
|
5
|
-
# TODO improvable?
|
|
6
5
|
config.to_prepare do
|
|
7
|
-
# initializer "graphiti_gql.generate_schema" do
|
|
8
6
|
Dir.glob("#{Rails.root}/app/resources/**/*").each { |f| require(f) }
|
|
9
7
|
GraphitiGql.schema!
|
|
8
|
+
|
|
9
|
+
log_level = ENV.fetch('GRAPHITI_LOG_LEVEL', '1').to_i
|
|
10
|
+
log_activerecord = false
|
|
11
|
+
if log_level == -1 && defined?(ActiveRecord)
|
|
12
|
+
log_level = 0
|
|
13
|
+
log_activerecord = true
|
|
14
|
+
end
|
|
15
|
+
Graphiti.logger.level = log_level
|
|
16
|
+
if GraphitiGql.config.log
|
|
17
|
+
GraphitiGql::LogSubscriber.subscribe!(activerecord: log_activerecord)
|
|
18
|
+
end
|
|
10
19
|
end
|
|
11
20
|
end
|
|
12
21
|
end
|
|
@@ -71,13 +71,46 @@ module GraphitiGql
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
class_methods do
|
|
74
|
+
def gid(*ids)
|
|
75
|
+
ids = ids[0] if ids[0].is_a?(Array) && ids.length == 1
|
|
76
|
+
gids = ids.map { |id| serialize_gid(id) }
|
|
77
|
+
ids.length == 1 ? gids.first : gids
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def serialize_gid(id)
|
|
81
|
+
Base64.encode64("#{graphql_name}/#{id}")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def deserialize_gid(gid, integer = true)
|
|
85
|
+
id = Base64.decode64(gid).split("/").last
|
|
86
|
+
id.to_i.to_s == id ? id.to_i : id
|
|
87
|
+
end
|
|
88
|
+
|
|
74
89
|
def config
|
|
75
90
|
return @config if @config
|
|
76
91
|
super
|
|
77
92
|
@config = @config.merge(value_objects: {}, is_value_object: false)
|
|
78
93
|
end
|
|
79
94
|
|
|
95
|
+
def graphql_name
|
|
96
|
+
@graphql_name ||= begin
|
|
97
|
+
if name.nil?
|
|
98
|
+
'Unknown'
|
|
99
|
+
else
|
|
100
|
+
name.gsub('Resource', '').gsub('::', '')
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
80
105
|
def attribute(*args)
|
|
106
|
+
# new default is :gid, not integer_id
|
|
107
|
+
if args[0] == :id && args[1] == :integer_id
|
|
108
|
+
args[1] = :gid
|
|
109
|
+
opts = args.extract_options!
|
|
110
|
+
opts[:null] = false
|
|
111
|
+
args << opts
|
|
112
|
+
end
|
|
113
|
+
|
|
81
114
|
super(*args).tap do
|
|
82
115
|
opts = args.extract_options!
|
|
83
116
|
att = config[:attributes][args[0]]
|
|
@@ -143,6 +176,12 @@ module GraphitiGql
|
|
|
143
176
|
deprecation_reason: opts[:deprecation_reason]
|
|
144
177
|
)
|
|
145
178
|
end
|
|
179
|
+
|
|
180
|
+
def all(*args)
|
|
181
|
+
params = args[0]
|
|
182
|
+
Graphiti.broadcast("resource.all", { params: params, resource: self })
|
|
183
|
+
super
|
|
184
|
+
end
|
|
146
185
|
end
|
|
147
186
|
end
|
|
148
187
|
Graphiti::Resource.send(:prepend, ResourceExtras)
|
|
@@ -314,6 +353,7 @@ module GraphitiGql
|
|
|
314
353
|
description: 'Datetime with milliseconds'
|
|
315
354
|
}
|
|
316
355
|
|
|
356
|
+
# Support ranges
|
|
317
357
|
[:string, :integer, :float, :datetime, :precise_datetime].each do |kind|
|
|
318
358
|
duped_hash = Graphiti::Util::Hash.deep_dup(Graphiti::Types[:hash])
|
|
319
359
|
type = Graphiti::Types[:"#{kind}_range"] = duped_hash
|
|
@@ -328,6 +368,29 @@ module GraphitiGql
|
|
|
328
368
|
}
|
|
329
369
|
end
|
|
330
370
|
|
|
371
|
+
# Support gid
|
|
372
|
+
gid_going_out = ->(id) {
|
|
373
|
+
resource.gid(id)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
gid_coming_in = definition.constructor do |input|
|
|
377
|
+
if defined?(ApplicationResource)
|
|
378
|
+
ApplicationResource.deserialize_gid(input)
|
|
379
|
+
else
|
|
380
|
+
Graphiti::Resource.deserialize_gid(input)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Register it with Graphiti
|
|
385
|
+
Graphiti::Types[:gid] = {
|
|
386
|
+
params: gid_coming_in,
|
|
387
|
+
read: gid_going_out,
|
|
388
|
+
write: gid_coming_in,
|
|
389
|
+
kind: 'scalar',
|
|
390
|
+
canonical_name: :gid,
|
|
391
|
+
description: 'Globally-unique identifier',
|
|
392
|
+
}
|
|
393
|
+
|
|
331
394
|
module ActiveRecordAdapterExtras
|
|
332
395
|
extend ActiveSupport::Concern
|
|
333
396
|
|
|
@@ -338,6 +401,8 @@ module GraphitiGql
|
|
|
338
401
|
alias_method :filter_precise_datetime_gte, :filter_gte
|
|
339
402
|
alias_method :filter_precise_datetime_eq, :filter_eq
|
|
340
403
|
alias_method :filter_precise_datetime_not_eq, :filter_not_eq
|
|
404
|
+
alias_method :filter_gid_eq, :filter_eq
|
|
405
|
+
alias_method :filter_gid_not_eq, :filter_not_eq
|
|
341
406
|
end
|
|
342
407
|
|
|
343
408
|
# TODO: integration specs mysql vs postgres for case sensitivity
|
|
@@ -387,11 +452,14 @@ module GraphitiGql
|
|
|
387
452
|
class << self
|
|
388
453
|
alias :old_default_operators :default_operators
|
|
389
454
|
def default_operators
|
|
390
|
-
old_default_operators.merge({
|
|
391
|
-
precise_datetime: numerical_operators,
|
|
455
|
+
ops = old_default_operators.merge({
|
|
456
|
+
precise_datetime: numerical_operators - [:not_eq],
|
|
392
457
|
string_enum: [:eq, :not_eq],
|
|
393
458
|
integer_enum: [:eq, :not_eq],
|
|
459
|
+
gid: [:eq, :not_eq]
|
|
394
460
|
})
|
|
461
|
+
ops[:datetime] = ops[:datetime] - [:not_eq]
|
|
462
|
+
ops
|
|
395
463
|
end
|
|
396
464
|
end
|
|
397
465
|
end
|
|
@@ -519,4 +587,21 @@ class Graphiti::ValueObjectAssociation
|
|
|
519
587
|
instance.parent = parent
|
|
520
588
|
instance
|
|
521
589
|
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
module Graphiti
|
|
593
|
+
def self.debug(msg, color = :white, bold = false)
|
|
594
|
+
log(msg, color, bold, level: :debug)
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def self.info(msg, color = :white, bold = false)
|
|
598
|
+
log(msg, color, bold, level: :info)
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def self.log(msg, color = :white, bold = false, level: nil)
|
|
602
|
+
return unless ::GraphitiGql.config.log
|
|
603
|
+
colored = ActiveSupport::LogSubscriber.new.send(:color, msg, color, bold)
|
|
604
|
+
level ||= :debug
|
|
605
|
+
logger.send(level, colored)
|
|
606
|
+
end
|
|
522
607
|
end
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
module GraphitiGql
|
|
2
2
|
module Loaders
|
|
3
|
-
class FakeRecord < Struct.new(:id, :type)
|
|
3
|
+
class FakeRecord < Struct.new(:id, :type, :graphiti_resource)
|
|
4
|
+
def initialize(*args)
|
|
5
|
+
super
|
|
6
|
+
@__graphiti_resource = graphiti_resource
|
|
7
|
+
end
|
|
4
8
|
end
|
|
5
9
|
|
|
6
10
|
class BelongsTo < GraphQL::Batch::Loader
|
|
@@ -10,6 +14,7 @@ module GraphitiGql
|
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
def perform(ids)
|
|
17
|
+
Graphiti.broadcast("association", { sideload: @sideload })
|
|
13
18
|
# process nils
|
|
14
19
|
ids.each { |id| fulfill(id, nil) if id.nil? }
|
|
15
20
|
ids.compact!
|
|
@@ -20,11 +25,11 @@ module GraphitiGql
|
|
|
20
25
|
ids.each do |id|
|
|
21
26
|
child = @sideload.children.values.find { |c| c.group_name == id[:type].to_sym }
|
|
22
27
|
type = Schema::Registry.instance.get(child.resource.class, interface: false)[:type]
|
|
23
|
-
fulfill(id, FakeRecord.new(id[:id], type))
|
|
28
|
+
fulfill(id, FakeRecord.new(id[:id], type, child.resource))
|
|
24
29
|
end
|
|
25
30
|
else
|
|
26
31
|
type = Schema::Registry.instance.get(@sideload.resource.class)[:type]
|
|
27
|
-
ids.each { |id| fulfill(id, FakeRecord.new(id, type)) }
|
|
32
|
+
ids.each { |id| fulfill(id, FakeRecord.new(id, type, @sideload.resource)) }
|
|
28
33
|
end
|
|
29
34
|
return
|
|
30
35
|
end
|
|
@@ -39,9 +44,10 @@ module GraphitiGql
|
|
|
39
44
|
payload.each_pair do |key, value|
|
|
40
45
|
params = { filter: {} }
|
|
41
46
|
klass = @sideload.children.values.find { |c| c.group_name == key.to_sym }
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
filter_ids = value.map { |id| @sideload.resource.class.gid(id.to_s) }
|
|
48
|
+
params = @params.merge({
|
|
49
|
+
filter: { id: { eq: filter_ids } }
|
|
50
|
+
})
|
|
45
51
|
|
|
46
52
|
futures << Concurrent::Future.execute do
|
|
47
53
|
{ type: key, data: klass.resource.class.all(params).data }
|
|
@@ -54,9 +60,10 @@ module GraphitiGql
|
|
|
54
60
|
end
|
|
55
61
|
else
|
|
56
62
|
resource = Schema.registry.get(@sideload.resource.class)[:resource]
|
|
57
|
-
params =
|
|
63
|
+
params = @params.deep_dup
|
|
58
64
|
unless resource.singular
|
|
59
|
-
|
|
65
|
+
filter_ids = ids.map { |id| @sideload.resource.class.gid(id.to_s) }
|
|
66
|
+
params[:filter] = {id: { eq: filter_ids } }
|
|
60
67
|
end
|
|
61
68
|
records = resource.all(params).data
|
|
62
69
|
if resource.singular
|
|
@@ -15,10 +15,11 @@ module GraphitiGql
|
|
|
15
15
|
|
|
16
16
|
def initialize(sideload, params)
|
|
17
17
|
@sideload = sideload
|
|
18
|
-
@params = params
|
|
18
|
+
@params = params.merge(typecast_filters: false)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def perform(parent_records)
|
|
22
|
+
Graphiti.broadcast("association", { sideload: @sideload })
|
|
22
23
|
raise ::Graphiti::Errors::UnsupportedPagination if paginating? && parent_records.length > 1
|
|
23
24
|
raise Errors::UnsupportedStats if requesting_stats? && parent_records.length > 1 && !can_group?
|
|
24
25
|
|
|
@@ -33,6 +34,7 @@ module GraphitiGql
|
|
|
33
34
|
pk
|
|
34
35
|
end
|
|
35
36
|
end
|
|
37
|
+
ids.compact!
|
|
36
38
|
|
|
37
39
|
build_params(ids, parent_records)
|
|
38
40
|
resource = Schema.registry.get(@sideload.resource.class)[:resource]
|
|
@@ -52,13 +54,16 @@ module GraphitiGql
|
|
|
52
54
|
if @sideload.polymorphic_as
|
|
53
55
|
type = ids[0][:"#{@sideload.polymorphic_as}_type"]
|
|
54
56
|
foreign_keys = ids.map { |id| id[@sideload.foreign_key] }
|
|
57
|
+
foreign_keys.map! { |id| @sideload.parent_resource.class.gid(id) }
|
|
55
58
|
@params[:filter][:"#{@sideload.polymorphic_as}_type"] = type
|
|
56
|
-
@params[:filter][@sideload.foreign_key] = foreign_keys
|
|
59
|
+
@params[:filter][@sideload.foreign_key] = foreign_keys
|
|
57
60
|
elsif @sideload.type == :many_to_many
|
|
61
|
+
filter_ids = ids.map { |id| @sideload.parent_resource.class.gid(id) }
|
|
58
62
|
fk = @sideload.foreign_key.values.first
|
|
59
|
-
@params[:filter].merge!(fk => { eq:
|
|
63
|
+
@params[:filter].merge!(fk => { eq: filter_ids })
|
|
60
64
|
elsif !@sideload.parent_resource.class.singular
|
|
61
|
-
|
|
65
|
+
filter_ids = ids.map { |id| @sideload.parent_resource.class.gid(id) }
|
|
66
|
+
@params[:filter].merge!(@sideload.foreign_key => { eq: filter_ids })
|
|
62
67
|
end
|
|
63
68
|
|
|
64
69
|
if @params[:stats]
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# TODO: remove OG graphiti debugger
|
|
2
|
+
module GraphitiGql
|
|
3
|
+
class LogSubscriber
|
|
4
|
+
def self.subscribe!(activerecord: false)
|
|
5
|
+
instance = LogSubscriber.new
|
|
6
|
+
instance.subscribe!('resolve', :on_data)
|
|
7
|
+
instance.subscribe!('schema.before_execute', :on_schema_before_execute)
|
|
8
|
+
instance.subscribe!('schema.execute', :on_schema_execute)
|
|
9
|
+
instance.subscribe!('resource.all', :on_resource_all)
|
|
10
|
+
instance.subscribe!('association', :on_association)
|
|
11
|
+
instance.subscribe!('before_stats', :on_before_stats)
|
|
12
|
+
instance.subscribe!('after_stats', :on_after_stats)
|
|
13
|
+
if activerecord
|
|
14
|
+
ActiveSupport::Notifications
|
|
15
|
+
.subscribe("sql.active_record", instance.method(:on_activerecord))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
@chunks = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def subscribe!(name, method_name)
|
|
24
|
+
ActiveSupport::Notifications
|
|
25
|
+
.subscribe("#{name}.graphiti", method(method_name))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def on_data(name, start, stop, id, payload)
|
|
29
|
+
@resolving = false
|
|
30
|
+
if payload[:exception]
|
|
31
|
+
@error_on_resolve = true
|
|
32
|
+
return
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
num_results = payload[:results].length
|
|
36
|
+
klasses = payload[:results].map(&:class).map(&:name).uniq
|
|
37
|
+
color = num_results == 0 ? :yellow : :green
|
|
38
|
+
stmt = "#{indent} #{num_results} #{"result".pluralize(num_results)}"
|
|
39
|
+
stmt << " of #{"type".pluralize(klasses.length)} #{klasses.to_sentence}" if num_results > 0
|
|
40
|
+
add_chunk(stmt, color, true)
|
|
41
|
+
|
|
42
|
+
took = ((stop - start) * 1000.0).round(2)
|
|
43
|
+
add_chunk("#{indent} Took: #{took}ms", :magenta, true)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def on_schema_before_execute(name, start, stop, id, payload)
|
|
47
|
+
Graphiti.debug(payload[:query].strip_heredoc, :white, true)
|
|
48
|
+
unless payload[:variables].empty?
|
|
49
|
+
Graphiti.debug("✨ Variables: #{payload[:variables].inspect}", :yellow, true)
|
|
50
|
+
end
|
|
51
|
+
unless payload[:context].empty?
|
|
52
|
+
Graphiti.debug("✨ Context: #{payload[:context].inspect}", :blue, true)
|
|
53
|
+
end
|
|
54
|
+
Graphiti.debug(%|💡 Debug tip! Override Resource#resolve:
|
|
55
|
+
|
|
56
|
+
class YourResource < ApplicationResource
|
|
57
|
+
# ... code ...
|
|
58
|
+
def resolve(scope)
|
|
59
|
+
debugger
|
|
60
|
+
# if activerecord, call scope.to_sql/scope.to_a
|
|
61
|
+
super
|
|
62
|
+
end
|
|
63
|
+
end|, :white, true)
|
|
64
|
+
Graphiti.debug("🤠🚀🤠🚀🤠🚀🤠🚀🤠🚀🤠🚀🤠🚀🤠 Executing! 🤠🚀🤠🚀🤠🚀🤠🚀🤠🚀🤠🚀🤠🚀🤠", :white, true)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def on_schema_execute(name, start, stop, id, payload)
|
|
68
|
+
if payload[:exception] || (response_errors = payload[:result]["errors"])
|
|
69
|
+
indent = indent(path: @last_association_path)
|
|
70
|
+
add_chunk("#{indent}❌🚨❌🚨❌🚨❌ ERROR! ❌🚨❌🚨❌🚨❌", :red, true, path: @last_association_path)
|
|
71
|
+
if @error_on_resolve
|
|
72
|
+
add_chunk("#{indent}This error occurred while executing the above query, so it's likely not caused by Graphiti itself. Maybe bad SQL? Try running again and putting a debugger in this Resource's #resolve, or try to run the query independent of Graphiti/GraphQL.",
|
|
73
|
+
:red, true, path: @last_association_path)
|
|
74
|
+
end
|
|
75
|
+
flush_chunks(@chunks)
|
|
76
|
+
if response_errors
|
|
77
|
+
Graphiti.info("❌🚨 Response contained errors!", :red, true)
|
|
78
|
+
response_errors.each do |err|
|
|
79
|
+
Graphiti.info("#{err['extensions']['code']} - #{err['message']}", :red, true)
|
|
80
|
+
Graphiti.info("#{err['path'].join(".")}", :red, false) if err['path']
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
else
|
|
84
|
+
flush_chunks(@chunks)
|
|
85
|
+
took = ((stop - start) * 1000.0).round(2)
|
|
86
|
+
Graphiti.info("✅ Completed successfully in #{took}ms", :magenta, true)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def on_resource_all(name, start, stop, id, payload)
|
|
91
|
+
@resolving = true
|
|
92
|
+
params = payload[:params].inspect
|
|
93
|
+
resource = payload[:resource].name
|
|
94
|
+
if thin_path.length == 1
|
|
95
|
+
add_chunk("Query.#{thin_path.first}", :yellow, true)
|
|
96
|
+
end
|
|
97
|
+
add_chunk("#{indent}\\_ #{resource}.all(#{params})", :cyan, true)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def on_association(name, start, stop, id, payload)
|
|
101
|
+
@last_association_path = thin_path
|
|
102
|
+
sideload = payload[:sideload]
|
|
103
|
+
add_chunk("#{indent}🔗 #{sideload.type} :#{sideload.name}", :white, true)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def on_before_stats(name, start, stop, id, payload)
|
|
107
|
+
@stats = true
|
|
108
|
+
add_chunk("#{indent}🔢 Calculating Statistics...", :yellow, true)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def on_after_stats(name, start, stop, id, payload)
|
|
112
|
+
@stats = false
|
|
113
|
+
took = ((stop - start) * 1000.0).round(2)
|
|
114
|
+
add_chunk("#{indent}🔢 Done! Took #{took}ms", :yellow, true)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def on_activerecord(name, start, stop, id, payload)
|
|
118
|
+
if @resolving || @stats
|
|
119
|
+
sql = payload[:sql]
|
|
120
|
+
unless sql.starts_with?('SHOW ')
|
|
121
|
+
add_chunk("#{indent}#{sql}", :blue, true)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def flush_chunks(chunks)
|
|
129
|
+
chunks.each_pair do |_, value|
|
|
130
|
+
value[:lines].each do |line|
|
|
131
|
+
Graphiti.info(line[:text], line[:color], line[:bold])
|
|
132
|
+
end
|
|
133
|
+
flush_chunks(value.except(:lines))
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def add_chunk(text, color, bold, path: nil)
|
|
138
|
+
path ||= thin_path
|
|
139
|
+
current_chunks = @chunks
|
|
140
|
+
path.each_with_index do |subpath, index|
|
|
141
|
+
last = index == path.length - 1
|
|
142
|
+
line = { text: text, color: color, bold: bold }
|
|
143
|
+
if current_chunks.key?(subpath)
|
|
144
|
+
if last
|
|
145
|
+
current_chunks[subpath][:lines] << line
|
|
146
|
+
else
|
|
147
|
+
current_chunks = current_chunks[subpath]
|
|
148
|
+
end
|
|
149
|
+
else
|
|
150
|
+
current_chunks[subpath] ||= { lines: [] }
|
|
151
|
+
current_chunks[subpath][:lines] << line
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def thin_path
|
|
157
|
+
path = Graphiti.context[:object][:current_path]
|
|
158
|
+
return [] unless path
|
|
159
|
+
path.reject do |p|
|
|
160
|
+
p.is_a?(Integer) ||
|
|
161
|
+
p == "nodes" ||
|
|
162
|
+
p == "node" ||
|
|
163
|
+
p == "edges"
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def indent(path: nil)
|
|
168
|
+
path ||= thin_path
|
|
169
|
+
" " * [path.length - 1, 0].max
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def current_path
|
|
173
|
+
path = Graphiti.context[:object][:current_path].join(".")
|
|
174
|
+
"Query.#{path}"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -37,7 +37,16 @@ module GraphitiGql
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
return if value.nil?
|
|
40
|
-
|
|
40
|
+
|
|
41
|
+
caster = Graphiti::Types[_config[:type]][:read]
|
|
42
|
+
# Dry::Types can't be instance_exec'd
|
|
43
|
+
# This dependency has probably served it's purpose and can be
|
|
44
|
+
# refactored away
|
|
45
|
+
if caster.is_a?(Proc)
|
|
46
|
+
instance_exec(value, &caster)
|
|
47
|
+
else
|
|
48
|
+
caster.call(value)
|
|
49
|
+
end
|
|
41
50
|
end
|
|
42
51
|
end
|
|
43
52
|
|
|
@@ -51,7 +60,6 @@ module GraphitiGql
|
|
|
51
60
|
if !field_type
|
|
52
61
|
canonical_graphiti_type = Graphiti::Types.name_for(@config[:type])
|
|
53
62
|
field_type = GQL_TYPE_MAP[canonical_graphiti_type.to_sym]
|
|
54
|
-
field_type = String if @name == :id
|
|
55
63
|
end
|
|
56
64
|
field_type = [field_type] if @config[:type].to_s.starts_with?("array_of")
|
|
57
65
|
field_type
|
|
@@ -12,7 +12,7 @@ module GraphitiGql
|
|
|
12
12
|
null: true,
|
|
13
13
|
extras: [:lookahead]
|
|
14
14
|
unless @registered[:resource].singular
|
|
15
|
-
field.argument(:id,
|
|
15
|
+
field.argument(:id, GraphQL::Types::ID, required: true)
|
|
16
16
|
end
|
|
17
17
|
_registered = @registered
|
|
18
18
|
query.define_method name do |**arguments|
|
|
@@ -9,16 +9,19 @@ module GraphitiGql
|
|
|
9
9
|
def apply(type)
|
|
10
10
|
type.field :stats, build_stat_class, null: false
|
|
11
11
|
type.define_method :stats do
|
|
12
|
+
Graphiti.broadcast('before_stats', {})
|
|
12
13
|
# Process grouped (to-many relationship) stats
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
Graphiti.broadcast('after_stats', {}) do
|
|
15
|
+
stats = object.proxy.stats.deep_dup
|
|
16
|
+
stats.each_pair do |attr, calc|
|
|
17
|
+
calc.each_pair do |calc_name, value|
|
|
18
|
+
if value.is_a?(Hash)
|
|
19
|
+
stats[attr][calc_name] = value[parent.id]
|
|
20
|
+
end
|
|
18
21
|
end
|
|
19
22
|
end
|
|
23
|
+
stats
|
|
20
24
|
end
|
|
21
|
-
stats
|
|
22
25
|
end
|
|
23
26
|
type
|
|
24
27
|
end
|
|
@@ -141,6 +141,17 @@ module GraphitiGql
|
|
|
141
141
|
klass = Class.new(Schema.base_object)
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
+
klass.define_method :resource do
|
|
145
|
+
return @resource if @resource
|
|
146
|
+
resource = object.instance_variable_get(:@__graphiti_resource)
|
|
147
|
+
resource_class = resource.class
|
|
148
|
+
if resource_class.polymorphic? && !resource_class.polymorphic_child?
|
|
149
|
+
resource_class = resource_class.resource_for_model(object)
|
|
150
|
+
end
|
|
151
|
+
@resource = resource_class
|
|
152
|
+
resource_class
|
|
153
|
+
end
|
|
154
|
+
|
|
144
155
|
klass
|
|
145
156
|
end
|
|
146
157
|
|
data/lib/graphiti_gql/schema.rb
CHANGED
|
@@ -33,6 +33,7 @@ module GraphitiGql
|
|
|
33
33
|
integer_id: String,
|
|
34
34
|
string: String,
|
|
35
35
|
uuid: String,
|
|
36
|
+
gid: GraphQL::Types::ID,
|
|
36
37
|
integer: Integer,
|
|
37
38
|
big_integer: GraphQL::Types::BigInt,
|
|
38
39
|
float: Float,
|
|
@@ -68,7 +69,9 @@ module GraphitiGql
|
|
|
68
69
|
end
|
|
69
70
|
|
|
70
71
|
def self.base_object
|
|
72
|
+
return @base_object if @base_object
|
|
71
73
|
klass = Class.new(GraphQL::Schema::Object)
|
|
74
|
+
|
|
72
75
|
# TODO make this config maybe
|
|
73
76
|
if defined?(ActionView)
|
|
74
77
|
klass.send(:include, ActionView::Helpers::TranslationHelper)
|
|
@@ -79,6 +82,7 @@ module GraphitiGql
|
|
|
79
82
|
end
|
|
80
83
|
end
|
|
81
84
|
end
|
|
85
|
+
@base_object = klass
|
|
82
86
|
klass
|
|
83
87
|
end
|
|
84
88
|
|
data/lib/graphiti_gql/version.rb
CHANGED
data/lib/graphiti_gql.rb
CHANGED
|
@@ -28,13 +28,14 @@ require "graphiti_gql/schema/fields/attribute"
|
|
|
28
28
|
require "graphiti_gql/schema/fields/stats"
|
|
29
29
|
require "graphiti_gql/active_resource"
|
|
30
30
|
require "graphiti_gql/exception_handler"
|
|
31
|
+
require "graphiti_gql/log_subscriber"
|
|
31
32
|
require "graphiti_gql/engine" if defined?(Rails)
|
|
32
33
|
|
|
33
34
|
module GraphitiGql
|
|
34
35
|
class Error < StandardError; end
|
|
35
36
|
|
|
36
37
|
class Configuration
|
|
37
|
-
attr_accessor :exception_handler, :error_handling
|
|
38
|
+
attr_accessor :exception_handler, :error_handling, :logging
|
|
38
39
|
|
|
39
40
|
def exception_handler
|
|
40
41
|
@exception_handler ||= ExceptionHandler
|
|
@@ -43,6 +44,10 @@ module GraphitiGql
|
|
|
43
44
|
def error_handling
|
|
44
45
|
@error_handling != false
|
|
45
46
|
end
|
|
47
|
+
|
|
48
|
+
def log
|
|
49
|
+
@log ||= !ENV['GRAPHITI_LOG_LEVEL'].nil?
|
|
50
|
+
end
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
def self.schema!
|
|
@@ -72,10 +77,20 @@ module GraphitiGql
|
|
|
72
77
|
context = Graphiti.context[:object]
|
|
73
78
|
end
|
|
74
79
|
Graphiti.with_context(context) do
|
|
75
|
-
|
|
80
|
+
payload = {
|
|
81
|
+
query: query_string,
|
|
76
82
|
variables: variables,
|
|
77
83
|
context: context
|
|
78
|
-
|
|
84
|
+
}
|
|
85
|
+
Graphiti.broadcast("schema.before_execute", payload)
|
|
86
|
+
Graphiti.broadcast("schema.execute", payload) do
|
|
87
|
+
result = schema.execute query_string,
|
|
88
|
+
variables: variables,
|
|
89
|
+
context: context
|
|
90
|
+
result_hash = result.to_h
|
|
91
|
+
payload[:result] = result_hash
|
|
92
|
+
result_hash
|
|
93
|
+
end
|
|
79
94
|
end
|
|
80
95
|
end
|
|
81
96
|
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.2.
|
|
4
|
+
version: 0.2.30
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lee Richmond
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-08-
|
|
11
|
+
date: 2022-08-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: graphql
|
|
@@ -154,6 +154,7 @@ files:
|
|
|
154
154
|
- lib/graphiti_gql/loaders/many.rb
|
|
155
155
|
- lib/graphiti_gql/loaders/many_to_many.rb
|
|
156
156
|
- lib/graphiti_gql/loaders/polymorphic_has_many.rb
|
|
157
|
+
- lib/graphiti_gql/log_subscriber.rb
|
|
157
158
|
- lib/graphiti_gql/response_shim.rb
|
|
158
159
|
- lib/graphiti_gql/schema.rb
|
|
159
160
|
- lib/graphiti_gql/schema/connection.rb
|