graphiti 1.0.beta.5 → 1.0.beta.6
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/graphiti.gemspec +2 -2
- data/lib/graphiti.rb +29 -0
- data/lib/graphiti/base.rb +5 -1
- data/lib/graphiti/configuration.rb +16 -0
- data/lib/graphiti/debugger.rb +194 -0
- data/lib/graphiti/query.rb +5 -0
- data/lib/graphiti/rails.rb +7 -0
- data/lib/graphiti/railtie.rb +5 -0
- data/lib/graphiti/renderer.rb +10 -14
- data/lib/graphiti/resource/interface.rb +1 -0
- data/lib/graphiti/resource_proxy.rb +14 -7
- data/lib/graphiti/scope.rb +19 -2
- data/lib/graphiti/sideload.rb +18 -11
- data/lib/graphiti/sideload/belongs_to.rb +1 -1
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +2 -2
- data/lib/graphiti/tasks.rb +54 -0
- data/lib/graphiti/util/serializer_relationships.rb +14 -3
- data/lib/graphiti/version.rb +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebbb1dbc7d5e33d3fe8411ea7f184cb1c1a42b72
|
4
|
+
data.tar.gz: 5d94a4b97e8aa526abcc342adfe12ae124df549c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60cd65ff706feace9b10d691b13f55cf716c0fdef0563b75ce9ad8a34579d553824a3417eba67bfd6ac84ff02e70356537a5f63001e71bf1a54bcf16cba6d216
|
7
|
+
data.tar.gz: 15055313e91f05705ef372721e720c34556c7a4b82e11e122b167d16fca063feac398513a5b89141e3a8c8618c8f9e056037d5101a49de29cbb5355a13ae7bf5
|
data/graphiti.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
# Pinning this version until backwards-incompatibility is addressed
|
22
22
|
spec.add_dependency 'jsonapi-serializable', '~> 0.3.0'
|
23
23
|
spec.add_dependency 'dry-types', '~> 0.13'
|
24
|
-
spec.add_dependency 'graphiti_errors', '~> 1.0.
|
24
|
+
spec.add_dependency 'graphiti_errors', '~> 1.0.beta.1'
|
25
25
|
spec.add_dependency 'concurrent-ruby', '~> 1.0'
|
26
26
|
spec.add_dependency 'activesupport', ['>= 4.1', '< 6']
|
27
27
|
|
@@ -32,5 +32,5 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency "sqlite3"
|
33
33
|
spec.add_development_dependency "database_cleaner"
|
34
34
|
spec.add_development_dependency "activemodel", ['>= 4.1', '< 6']
|
35
|
-
spec.add_development_dependency "graphiti_spec_helpers", '>= 1.0.
|
35
|
+
spec.add_development_dependency "graphiti_spec_helpers", '>= 1.0.beta.3'
|
36
36
|
end
|
data/lib/graphiti.rb
CHANGED
@@ -68,6 +68,7 @@ require "graphiti/extensions/extra_attribute"
|
|
68
68
|
require "graphiti/extensions/boolean_attribute"
|
69
69
|
require "graphiti/extensions/temp_id"
|
70
70
|
require "graphiti/serializer"
|
71
|
+
require "graphiti/debugger"
|
71
72
|
|
72
73
|
if defined?(ActiveRecord)
|
73
74
|
require 'graphiti/adapters/active_record'
|
@@ -127,6 +128,34 @@ module Graphiti
|
|
127
128
|
@resources ||= []
|
128
129
|
end
|
129
130
|
|
131
|
+
def self.broadcast(name, payload)
|
132
|
+
name = "graphiti.#{name}"
|
133
|
+
ActiveSupport::Notifications.instrument(name, payload) do
|
134
|
+
yield payload if block_given?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.logger
|
139
|
+
@logger ||= stdout_logger
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.stdout_logger
|
143
|
+
logger = Logger.new($stdout)
|
144
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
145
|
+
"#{msg}\n"
|
146
|
+
end
|
147
|
+
logger
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.logger=(val)
|
151
|
+
@logger = val
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.log(msg, color = :white, bold = false)
|
155
|
+
colored = ActiveSupport::LogSubscriber.new.send(:color, msg, color, bold)
|
156
|
+
logger.debug(colored)
|
157
|
+
end
|
158
|
+
|
130
159
|
# When we add a sideload, we need to do configuration, such as
|
131
160
|
# adding the relationship to the Resource's serializer.
|
132
161
|
# However, the sideload's Resource class may not be loaded yet.
|
data/lib/graphiti/base.rb
CHANGED
@@ -57,7 +57,11 @@ module Graphiti
|
|
57
57
|
def proxy(base = nil, opts = {})
|
58
58
|
base ||= jsonapi_resource.base_scope
|
59
59
|
scope_opts = opts.slice :sideload_parent_length,
|
60
|
-
:default_paginate,
|
60
|
+
:default_paginate,
|
61
|
+
:after_resolve,
|
62
|
+
:sideload,
|
63
|
+
:parent,
|
64
|
+
:params
|
61
65
|
scope = jsonapi_scope(base, scope_opts)
|
62
66
|
ResourceProxy.new jsonapi_resource,
|
63
67
|
scope,
|
@@ -13,6 +13,8 @@ module Graphiti
|
|
13
13
|
attr_accessor :schema_path
|
14
14
|
attr_accessor :links_on_demand
|
15
15
|
attr_accessor :typecast_reads
|
16
|
+
attr_accessor :debug
|
17
|
+
attr_accessor :debug_models
|
16
18
|
|
17
19
|
# Set defaults
|
18
20
|
# @api private
|
@@ -22,14 +24,28 @@ module Graphiti
|
|
22
24
|
@respond_to = [:json, :jsonapi, :xml]
|
23
25
|
@links_on_demand = false
|
24
26
|
@typecast_reads = true
|
27
|
+
self.debug = ENV.fetch('GRAPHITI_DEBUG', true)
|
28
|
+
self.debug_models = ENV.fetch('GRAPHITI_DEBUG_MODELS', false)
|
25
29
|
|
26
30
|
if defined?(::Rails)
|
27
31
|
@schema_path = "#{::Rails.root}/public/schema.json"
|
32
|
+
self.debug = ::Rails.logger.level.zero?
|
33
|
+
Graphiti.logger = ::Rails.logger
|
28
34
|
end
|
29
35
|
end
|
30
36
|
|
31
37
|
def schema_path
|
32
38
|
@schema_path ||= raise('No schema_path defined! Set Graphiti.config.schema_path to save your schema.')
|
33
39
|
end
|
40
|
+
|
41
|
+
def debug=(val)
|
42
|
+
@debug = val
|
43
|
+
Debugger.enabled = val
|
44
|
+
end
|
45
|
+
|
46
|
+
def debug_models=(val)
|
47
|
+
@debug_models = val
|
48
|
+
Debugger.debug_models = val
|
49
|
+
end
|
34
50
|
end
|
35
51
|
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
# This could definitely use some refactoring love, but I have no time ATM
|
2
|
+
# The code is pretty self-contained; we're just listening to notifications
|
3
|
+
# and taking action.
|
4
|
+
module Graphiti
|
5
|
+
class Debugger
|
6
|
+
class << self
|
7
|
+
attr_accessor :enabled, :chunks, :debug_models, :preserve, :pry
|
8
|
+
end
|
9
|
+
self.chunks = []
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def on_data(name, start, stop, id, payload)
|
13
|
+
took = ((stop-start)*1000.0).round(2)
|
14
|
+
params = scrub_params(payload[:params])
|
15
|
+
|
16
|
+
if payload[:exception]
|
17
|
+
on_data_exception(payload, params)
|
18
|
+
else
|
19
|
+
if payload[:sideload]
|
20
|
+
on_sideload_data(payload, params, took)
|
21
|
+
else
|
22
|
+
on_primary_data(payload, params, took)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_data_exception(payload, params)
|
28
|
+
unless payload[:exception_object].instance_variable_get(:@__graphiti_debug)
|
29
|
+
add_chunk do |logs, json|
|
30
|
+
logs << ["\n=== Graphiti Debug ERROR", :red, true]
|
31
|
+
if sideload = payload[:sideload]
|
32
|
+
logs << ["#{sideload.parent_resource.class}: Sideload \"#{sideload.name}\"", :red, true]
|
33
|
+
json[:parent_resource] = sideload.parent_resource.class.name
|
34
|
+
json[:sideload] = sideload.name
|
35
|
+
end
|
36
|
+
if params
|
37
|
+
query = "#{payload[:resource].class.name}.all(#{JSON.pretty_generate(params)}).data"
|
38
|
+
logs << [query, :cyan, true]
|
39
|
+
logs << ["The error occurred when running the above query. Copy/paste it into a rake task or Rails console session to reproduce. Keep in mind you may have to set context.", :yellow, true]
|
40
|
+
json[:query] = query
|
41
|
+
else
|
42
|
+
query = "This sideload is done manually via .scope - no debug information available."
|
43
|
+
logs << [query, :cyan, true]
|
44
|
+
json[:query] = query
|
45
|
+
end
|
46
|
+
logs << "\n\n"
|
47
|
+
payload[:exception_object].instance_variable_set(:@__graphiti_debug, json)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def results(raw_results)
|
53
|
+
raw_results.map { |r| "[#{r.class.name}, #{r.id.inspect}]" }.join(', ')
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_sideload_data(payload, params, took)
|
57
|
+
sideload = payload[:sideload]
|
58
|
+
results = results(payload[:results])
|
59
|
+
add_chunk(payload[:resource], payload[:parent]) do |logs, json|
|
60
|
+
#logs << [" \\_ #{sideload.parent_resource_class} > \"#{sideload.name}\":", :yellow, true]
|
61
|
+
logs << [" \\_ #{sideload.name}", :yellow, true]
|
62
|
+
json[:name] = sideload.name
|
63
|
+
query = "#{payload[:resource].class.name}.all(#{params.inspect})"
|
64
|
+
unless params
|
65
|
+
query = "#{payload[:resource].class.name}: Manual sideload via .scope"
|
66
|
+
end
|
67
|
+
logs << [" #{query}", :cyan, true]
|
68
|
+
json[:query] = query
|
69
|
+
logs << [" Returned Models: #{results}"] if debug_models
|
70
|
+
logs << [" Took: #{took}ms", :magenta, true]
|
71
|
+
json[:took] = took
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_primary_data(payload, params, took)
|
76
|
+
add_chunk(payload[:resource], payload[:parent]) do |logs, json|
|
77
|
+
logs << [""]
|
78
|
+
logs << ["=== Graphiti Debug", :green, true]
|
79
|
+
title = "Top Level Data Retrieval (+ sideloads):"
|
80
|
+
logs << [title, :green, true]
|
81
|
+
json[:title] = title
|
82
|
+
query = "#{payload[:resource].class.name}.all(#{params.inspect})"
|
83
|
+
logs << [query, :cyan, true]
|
84
|
+
json[:query] = query
|
85
|
+
logs << ["Returned Models: #{results}"] if debug_models
|
86
|
+
logs << ["Took: #{took}ms", :magenta, true]
|
87
|
+
json[:took] = took
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def on_render(name, start, stop, id, payload)
|
92
|
+
add_chunk do |logs|
|
93
|
+
took = ((stop-start)*1000.0).round(2)
|
94
|
+
logs << [""]
|
95
|
+
logs << ["=== Graphiti Debug", :green, true]
|
96
|
+
logs << ["Rendering:", :green, true]
|
97
|
+
logs << ["Took: #{took}ms", :magenta, true]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def debug
|
102
|
+
if enabled
|
103
|
+
begin
|
104
|
+
self.chunks = []
|
105
|
+
yield
|
106
|
+
ensure
|
107
|
+
flush
|
108
|
+
self.chunks = [] unless self.preserve
|
109
|
+
end
|
110
|
+
else
|
111
|
+
yield
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_a
|
116
|
+
debugs = []
|
117
|
+
graph_statements.each do |chunk|
|
118
|
+
debugs << chunk_to_hash(chunk)
|
119
|
+
end
|
120
|
+
debugs
|
121
|
+
end
|
122
|
+
|
123
|
+
def flush
|
124
|
+
Graphiti.broadcast('debug.flush', {}) do |payload|
|
125
|
+
payload[:chunks] = chunks
|
126
|
+
graph_statements.each do |chunk|
|
127
|
+
flush_chunk(chunk)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def scrub_params(params)
|
135
|
+
params ||= {}
|
136
|
+
params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h)
|
137
|
+
params.reject! { |k,v| [:controller, :action, :format, :debug].include?(k.to_sym) }
|
138
|
+
params.reject! { |k,v| k.to_sym == :include }
|
139
|
+
params.deep_symbolize_keys
|
140
|
+
end
|
141
|
+
|
142
|
+
def add_chunk(resource = nil, parent = nil)
|
143
|
+
logs, json = [], {}
|
144
|
+
yield(logs, json)
|
145
|
+
self.chunks << {
|
146
|
+
resource: resource,
|
147
|
+
parent: parent,
|
148
|
+
logs: logs,
|
149
|
+
json: json,
|
150
|
+
children: []
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
def graph_statements
|
155
|
+
@chunks.each do |chunk|
|
156
|
+
if parent = chunk[:parent]
|
157
|
+
relevant = chunks.find { |c| c[:resource] == parent }
|
158
|
+
relevant[:children].unshift(chunk)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
@chunks.reject! { |c| !!c[:parent] }
|
162
|
+
@chunks
|
163
|
+
end
|
164
|
+
|
165
|
+
def chunk_to_hash(chunk)
|
166
|
+
hash = {}
|
167
|
+
hash.merge!(chunk[:json])
|
168
|
+
sideloads = []
|
169
|
+
chunk[:children].each do |child_chunk|
|
170
|
+
sideloads << chunk_to_hash(child_chunk)
|
171
|
+
end
|
172
|
+
hash[:sideloads] = sideloads
|
173
|
+
hash
|
174
|
+
end
|
175
|
+
|
176
|
+
def flush_chunk(chunk, depth = 0)
|
177
|
+
chunk[:logs].each do |args|
|
178
|
+
indent = ' ' * depth
|
179
|
+
args[0] = "#{indent}#{args[0]}"
|
180
|
+
Graphiti.log(*args)
|
181
|
+
end
|
182
|
+
|
183
|
+
chunk[:children].each do |child_chunk|
|
184
|
+
flush_chunk(child_chunk, depth + 1)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
ActiveSupport::Notifications.subscribe \
|
190
|
+
'graphiti.data', method(:on_data)
|
191
|
+
ActiveSupport::Notifications.subscribe \
|
192
|
+
'graphiti.render', method(:on_render)
|
193
|
+
end
|
194
|
+
end
|
data/lib/graphiti/query.rb
CHANGED
@@ -22,6 +22,7 @@ module Graphiti
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def links?
|
25
|
+
return false if [:json, :xml, 'json', 'xml'].include?(params[:format])
|
25
26
|
if Graphiti.config.links_on_demand
|
26
27
|
[true, 'true'].include?(@params[:links])
|
27
28
|
else
|
@@ -29,6 +30,10 @@ module Graphiti
|
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
33
|
+
def debug_requested?
|
34
|
+
!!@params[:debug]
|
35
|
+
end
|
36
|
+
|
32
37
|
def hash
|
33
38
|
@hash ||= {}.tap do |h|
|
34
39
|
h[:filter] = filters unless filters.empty?
|
data/lib/graphiti/rails.rb
CHANGED
@@ -12,6 +12,7 @@ module Graphiti
|
|
12
12
|
include Graphiti::Context
|
13
13
|
include GraphitiErrors
|
14
14
|
around_action :wrap_context
|
15
|
+
around_action :debug
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
@@ -21,6 +22,12 @@ module Graphiti
|
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
25
|
+
def debug
|
26
|
+
Debugger.debug do
|
27
|
+
yield
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
24
31
|
def jsonapi_context
|
25
32
|
self
|
26
33
|
end
|
data/lib/graphiti/railtie.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
module Graphiti
|
2
2
|
class Railtie < ::Rails::Railtie
|
3
|
+
rake_tasks do
|
4
|
+
path = File.expand_path(__dir__)
|
5
|
+
load "#{path}/tasks.rb"
|
6
|
+
end
|
7
|
+
|
3
8
|
initializer "graphiti.require_activerecord_adapter" do
|
4
9
|
config.after_initialize do |app|
|
5
10
|
ActiveSupport.on_load(:active_record) do
|
data/lib/graphiti/renderer.rb
CHANGED
@@ -38,7 +38,7 @@ module Graphiti
|
|
38
38
|
private
|
39
39
|
|
40
40
|
def render(renderer)
|
41
|
-
|
41
|
+
Graphiti.broadcast(:render, records: records, options: options) do
|
42
42
|
options[:fields] = proxy.fields
|
43
43
|
options[:expose] ||= {}
|
44
44
|
options[:expose][:extra_fields] = proxy.extra_fields
|
@@ -46,25 +46,21 @@ module Graphiti
|
|
46
46
|
options[:include] = proxy.include_hash
|
47
47
|
options[:meta] ||= {}
|
48
48
|
options[:meta].merge!(stats: proxy.stats) unless proxy.stats.empty?
|
49
|
+
options[:meta][:debug] = Debugger.to_a if debug_json?
|
50
|
+
|
49
51
|
renderer.render(records, options)
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
records: records,
|
60
|
-
options: options
|
61
|
-
]
|
62
|
-
ActiveSupport::Notifications.instrument(*opts) do
|
63
|
-
yield
|
55
|
+
def debug_json?
|
56
|
+
debug = false
|
57
|
+
if Debugger.enabled && proxy.debug_requested?
|
58
|
+
context = proxy.resource.context
|
59
|
+
if context.respond_to?(:allow_graphiti_debug_json?)
|
60
|
+
debug = context.allow_graphiti_debug_json?
|
64
61
|
end
|
65
|
-
else
|
66
|
-
yield
|
67
62
|
end
|
63
|
+
debug
|
68
64
|
end
|
69
65
|
end
|
70
66
|
end
|
@@ -4,13 +4,16 @@ module Graphiti
|
|
4
4
|
|
5
5
|
attr_reader :resource, :query, :scope, :payload
|
6
6
|
|
7
|
-
def initialize(resource, scope, query,
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
def initialize(resource, scope, query,
|
8
|
+
payload: nil,
|
9
|
+
single: false,
|
10
|
+
raise_on_missing: false)
|
11
|
+
@resource = resource
|
12
|
+
@scope = scope
|
13
|
+
@query = query
|
14
|
+
@payload = payload
|
15
|
+
@single = single
|
16
|
+
@raise_on_missing = raise_on_missing
|
14
17
|
end
|
15
18
|
|
16
19
|
def single?
|
@@ -122,6 +125,10 @@ module Graphiti
|
|
122
125
|
query.extra_fields
|
123
126
|
end
|
124
127
|
|
128
|
+
def debug_requested?
|
129
|
+
query.debug_requested?
|
130
|
+
end
|
131
|
+
|
125
132
|
private
|
126
133
|
|
127
134
|
def persist
|
data/lib/graphiti/scope.rb
CHANGED
@@ -17,7 +17,11 @@ module Graphiti
|
|
17
17
|
if @query.zero_results?
|
18
18
|
[]
|
19
19
|
else
|
20
|
-
resolved =
|
20
|
+
resolved = broadcast_data do |payload|
|
21
|
+
payload[:results] = @resource.resolve(@object)
|
22
|
+
payload[:results]
|
23
|
+
end
|
24
|
+
resolved.compact!
|
21
25
|
assign_serializer(resolved)
|
22
26
|
yield resolved if block_given?
|
23
27
|
if @opts[:after_resolve]
|
@@ -30,6 +34,18 @@ module Graphiti
|
|
30
34
|
|
31
35
|
private
|
32
36
|
|
37
|
+
def broadcast_data
|
38
|
+
opts = {
|
39
|
+
resource: @resource,
|
40
|
+
params: @opts[:params],
|
41
|
+
sideload: @opts[:sideload],
|
42
|
+
parent: @opts[:parent]
|
43
|
+
}
|
44
|
+
Graphiti.broadcast("data", opts) do |payload|
|
45
|
+
yield payload
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
33
49
|
# Used to ensure the resource's serializer is used
|
34
50
|
# Not one derived through the usual jsonapi-rb logic
|
35
51
|
def assign_serializer(records)
|
@@ -46,8 +62,9 @@ module Graphiti
|
|
46
62
|
|
47
63
|
@query.sideloads.each_pair do |name, q|
|
48
64
|
sideload = @resource.class.sideload(name)
|
65
|
+
_parent = @resource
|
49
66
|
resolve_sideload = -> {
|
50
|
-
sideload.resolve(results, q)
|
67
|
+
sideload.resolve(results, q, _parent)
|
51
68
|
if concurrent && defined?(ActiveRecord)
|
52
69
|
ActiveRecord::Base.clear_active_connections!
|
53
70
|
end
|
data/lib/graphiti/sideload.rb
CHANGED
@@ -141,10 +141,12 @@ module Graphiti
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
-
def load(parents, query)
|
144
|
+
def load(parents, query, graph_parent)
|
145
145
|
params = load_params(parents, query)
|
146
146
|
params_proc.call(params, parents) if params_proc
|
147
147
|
opts = load_options(parents, query)
|
148
|
+
opts[:sideload] = self
|
149
|
+
opts[:parent] = graph_parent
|
148
150
|
proxy = resource.class._all(params, opts, base_scope)
|
149
151
|
pre_load_proc.call(proxy, parents) if pre_load_proc
|
150
152
|
proxy.to_a
|
@@ -191,23 +193,28 @@ module Graphiti
|
|
191
193
|
children.replace(associated) if track_associated
|
192
194
|
end
|
193
195
|
|
194
|
-
def resolve(parents, query)
|
196
|
+
def resolve(parents, query, graph_parent)
|
195
197
|
if single? && parents.length > 1
|
196
198
|
raise Errors::SingularSideload.new(self, parents.length)
|
197
199
|
end
|
198
200
|
|
199
201
|
if self.class.scope_proc
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
202
|
+
Graphiti.broadcast("data", resource_class: resource.class, sideload: self) do |payload|
|
203
|
+
sideload_scope = fire_scope(parents)
|
204
|
+
sideload_scope = Scope.new sideload_scope,
|
205
|
+
resource,
|
206
|
+
query,
|
207
|
+
parent: graph_parent,
|
208
|
+
sideload: self,
|
209
|
+
sideload_parent_length: parents.length,
|
210
|
+
default_paginate: false
|
211
|
+
sideload_scope.resolve do |sideload_results|
|
212
|
+
payload[:results] = sideload_results
|
213
|
+
fire_assign(parents, sideload_results)
|
214
|
+
end
|
208
215
|
end
|
209
216
|
else
|
210
|
-
load(parents, query)
|
217
|
+
load(parents, query, graph_parent)
|
211
218
|
end
|
212
219
|
end
|
213
220
|
|
@@ -78,14 +78,14 @@ class Graphiti::Sideload::PolymorphicBelongsTo < Graphiti::Sideload::BelongsTo
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
-
def resolve(parents, query)
|
81
|
+
def resolve(parents, query, graph_parent)
|
82
82
|
parents.group_by(&grouper.field_name).each_pair do |group_name, group|
|
83
83
|
next if group_name.nil?
|
84
84
|
|
85
85
|
match = ->(c) { c.group_name == group_name.to_sym }
|
86
86
|
if sideload = children.values.find(&match)
|
87
87
|
query = remove_invalid_sideloads(sideload.resource, query)
|
88
|
-
sideload.resolve(group, query)
|
88
|
+
sideload.resolve(group, query, graph_parent)
|
89
89
|
else
|
90
90
|
err = ::Graphiti::Errors::PolymorphicSideloadChildNotFound
|
91
91
|
raise err.new(self, group_name)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
namespace :graphiti do
|
2
|
+
include GraphitiSpecHelpers::Helpers
|
3
|
+
|
4
|
+
def response
|
5
|
+
session.response
|
6
|
+
end
|
7
|
+
|
8
|
+
def session
|
9
|
+
@session ||= ActionDispatch::Integration::Session.new(Rails.application)
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup_rails!
|
13
|
+
Rails.application.eager_load!
|
14
|
+
Rails.application.config.cache_classes = true
|
15
|
+
Rails.application.config.action_controller.perform_caching = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def make_request(path, debug = false)
|
19
|
+
if path.split('/').length == 2
|
20
|
+
path = "#{ApplicationResource.endpoint_namespace}#{path}"
|
21
|
+
end
|
22
|
+
if path.include?('?')
|
23
|
+
path << '&cache=bust'
|
24
|
+
else
|
25
|
+
path << '?cache=bust'
|
26
|
+
end
|
27
|
+
path = "#{path}&debug=true" if debug
|
28
|
+
session.get("#{path}")
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Execute request without web server."
|
32
|
+
task :request, [:path,:debug] => [:environment] do |_, args|
|
33
|
+
setup_rails!
|
34
|
+
Graphiti.logger = Graphiti.stdout_logger
|
35
|
+
Graphiti::Debugger.preserve = true
|
36
|
+
require 'pp'
|
37
|
+
path, debug = args[:path], args[:debug]
|
38
|
+
puts "Graphiti Request: #{path}"
|
39
|
+
make_request(path, debug)
|
40
|
+
pp json
|
41
|
+
Graphiti::Debugger.flush if debug
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Execute benchmark without web server."
|
45
|
+
task :benchmark, [:path,:requests] => [:environment] do |_, args|
|
46
|
+
setup_rails!
|
47
|
+
took = Benchmark.ms do
|
48
|
+
args[:requests].to_i.times do
|
49
|
+
make_request(args[:path])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
puts "Took: #{(took / args[:requests].to_f).round(2)}ms"
|
53
|
+
end
|
54
|
+
end
|
@@ -35,6 +35,12 @@ module Graphiti
|
|
35
35
|
@serializer.relationship(@sideload.name, &block)
|
36
36
|
end
|
37
37
|
|
38
|
+
# If we can't eagerly validate links on app boot, we do it at runtime
|
39
|
+
# To avoid any performance confusion, this caches that lookup
|
40
|
+
def self.validated_link_cache
|
41
|
+
@validated_link_cache ||= []
|
42
|
+
end
|
43
|
+
|
38
44
|
private
|
39
45
|
|
40
46
|
def block
|
@@ -49,11 +55,13 @@ module Graphiti
|
|
49
55
|
data { instance_eval(&_data_proc) }
|
50
56
|
|
51
57
|
if _link
|
52
|
-
|
58
|
+
if _links = @proxy.query.links?
|
59
|
+
_self.send(:validate_link!) unless _self.send(:eagerly_validate_links?)
|
53
60
|
|
54
|
-
if @proxy.query.links?
|
55
61
|
link(:related) do
|
56
|
-
|
62
|
+
if _links
|
63
|
+
::Graphiti::Util::Link.new(_sl, @object).generate
|
64
|
+
end
|
57
65
|
end
|
58
66
|
end
|
59
67
|
end
|
@@ -103,10 +111,13 @@ module Graphiti
|
|
103
111
|
|
104
112
|
def _validate_link!(sideload)
|
105
113
|
action = sideload.type == :belongs_to ? :show : :index
|
114
|
+
cache_key = :"#{@sideload.object_id}-#{action}"
|
115
|
+
return if self.class.validated_link_cache.include?(cache_key)
|
106
116
|
prc = Graphiti.config.context_for_endpoint
|
107
117
|
unless prc.call(sideload.resource.endpoint[:full_path], action)
|
108
118
|
raise Errors::InvalidLink.new(@resource_class, sideload, action)
|
109
119
|
end
|
120
|
+
self.class.validated_link_cache << cache_key
|
110
121
|
end
|
111
122
|
|
112
123
|
def link?
|
data/lib/graphiti/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphiti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.beta.
|
4
|
+
version: 1.0.beta.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Richmond
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-09-
|
11
|
+
date: 2018-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsonapi-serializable
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 1.0.
|
47
|
+
version: 1.0.beta.1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 1.0.
|
54
|
+
version: 1.0.beta.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: concurrent-ruby
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -202,14 +202,14 @@ dependencies:
|
|
202
202
|
requirements:
|
203
203
|
- - ">="
|
204
204
|
- !ruby/object:Gem::Version
|
205
|
-
version: 1.0.
|
205
|
+
version: 1.0.beta.3
|
206
206
|
type: :development
|
207
207
|
prerelease: false
|
208
208
|
version_requirements: !ruby/object:Gem::Requirement
|
209
209
|
requirements:
|
210
210
|
- - ">="
|
211
211
|
- !ruby/object:Gem::Version
|
212
|
-
version: 1.0.
|
212
|
+
version: 1.0.beta.3
|
213
213
|
description:
|
214
214
|
email:
|
215
215
|
- richmolj@gmail.com
|
@@ -265,6 +265,7 @@ files:
|
|
265
265
|
- lib/graphiti/cli.rb
|
266
266
|
- lib/graphiti/configuration.rb
|
267
267
|
- lib/graphiti/context.rb
|
268
|
+
- lib/graphiti/debugger.rb
|
268
269
|
- lib/graphiti/deserializer.rb
|
269
270
|
- lib/graphiti/errors.rb
|
270
271
|
- lib/graphiti/extensions/boolean_attribute.rb
|
@@ -306,6 +307,7 @@ files:
|
|
306
307
|
- lib/graphiti/sideload/polymorphic_belongs_to.rb
|
307
308
|
- lib/graphiti/stats/dsl.rb
|
308
309
|
- lib/graphiti/stats/payload.rb
|
310
|
+
- lib/graphiti/tasks.rb
|
309
311
|
- lib/graphiti/types.rb
|
310
312
|
- lib/graphiti/util/attribute_check.rb
|
311
313
|
- lib/graphiti/util/class.rb
|