graphiti 1.2.16 → 1.2.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +43 -14
- data/Appraisals +37 -5
- data/Guardfile +4 -4
- data/deprecated_generators/graphiti/resource_generator.rb +1 -1
- data/gemfiles/rails_5_0.gemfile +18 -0
- data/gemfiles/rails_5_0_graphiti_rails.gemfile +20 -0
- data/gemfiles/rails_5_1.gemfile +18 -0
- data/gemfiles/rails_5_1_graphiti_rails.gemfile +20 -0
- data/gemfiles/{rails_5.gemfile → rails_5_2.gemfile} +0 -0
- data/gemfiles/{rails_5_graphiti_rails.gemfile → rails_5_2_graphiti_rails.gemfile} +0 -0
- data/gemfiles/rails_6.gemfile +1 -1
- data/gemfiles/rails_6_graphiti_rails.gemfile +1 -1
- data/graphiti.gemspec +10 -10
- data/lib/graphiti.rb +3 -3
- data/lib/graphiti/adapters/abstract.rb +3 -3
- data/lib/graphiti/adapters/active_record.rb +64 -35
- data/lib/graphiti/configuration.rb +1 -1
- data/lib/graphiti/debugger.rb +4 -4
- data/lib/graphiti/delegates/pagination.rb +3 -3
- data/lib/graphiti/deserializer.rb +3 -3
- data/lib/graphiti/errors.rb +24 -4
- data/lib/graphiti/query.rb +4 -4
- data/lib/graphiti/railtie.rb +1 -1
- data/lib/graphiti/request_validator.rb +4 -4
- data/lib/graphiti/request_validators/update_validator.rb +1 -2
- data/lib/graphiti/request_validators/validator.rb +2 -2
- data/lib/graphiti/resource/configuration.rb +6 -4
- data/lib/graphiti/resource/dsl.rb +5 -4
- data/lib/graphiti/resource/links.rb +3 -3
- data/lib/graphiti/resource/persistence.rb +2 -1
- data/lib/graphiti/resource/polymorphism.rb +2 -1
- data/lib/graphiti/resource/remote.rb +1 -1
- data/lib/graphiti/runner.rb +1 -1
- data/lib/graphiti/schema.rb +6 -6
- data/lib/graphiti/scope.rb +5 -5
- data/lib/graphiti/scoping/base.rb +3 -3
- data/lib/graphiti/scoping/filter.rb +17 -7
- data/lib/graphiti/scoping/sort.rb +1 -1
- data/lib/graphiti/sideload.rb +26 -22
- data/lib/graphiti/sideload/belongs_to.rb +1 -1
- data/lib/graphiti/stats/payload.rb +4 -4
- data/lib/graphiti/types.rb +15 -15
- data/lib/graphiti/util/link.rb +5 -1
- data/lib/graphiti/util/persistence.rb +16 -10
- data/lib/graphiti/util/relationship_payload.rb +4 -4
- data/lib/graphiti/util/simple_errors.rb +1 -1
- data/lib/graphiti/util/transaction_hooks_recorder.rb +1 -1
- data/lib/graphiti/version.rb +1 -1
- metadata +8 -4
data/lib/graphiti/debugger.rb
CHANGED
@@ -59,10 +59,10 @@ module Graphiti
|
|
59
59
|
add_chunk(payload[:resource], payload[:parent]) do |logs, json|
|
60
60
|
logs << [" \\_ #{sideload.name}", :yellow, true]
|
61
61
|
json[:name] = sideload.name
|
62
|
-
if sideload.class.scope_proc
|
63
|
-
|
62
|
+
query = if sideload.class.scope_proc
|
63
|
+
"#{payload[:resource].class.name}: Manual sideload via .scope"
|
64
64
|
else
|
65
|
-
|
65
|
+
"#{payload[:resource].class.name}.all(#{params.inspect})"
|
66
66
|
end
|
67
67
|
logs << [" #{query}", :cyan, true]
|
68
68
|
json[:query] = query
|
@@ -148,7 +148,7 @@ module Graphiti
|
|
148
148
|
parent: parent,
|
149
149
|
logs: logs,
|
150
150
|
json: json,
|
151
|
-
children: []
|
151
|
+
children: []
|
152
152
|
}
|
153
153
|
end
|
154
154
|
|
@@ -15,7 +15,7 @@ module Graphiti
|
|
15
15
|
links[:last] = pagination_link(last_page)
|
16
16
|
links[:prev] = pagination_link(current_page - 1) unless current_page == 1
|
17
17
|
links[:next] = pagination_link(current_page + 1) unless current_page == last_page
|
18
|
-
end.select {|k, v| !v.nil? }
|
18
|
+
end.select { |k, v| !v.nil? }
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
@@ -29,8 +29,8 @@ module Graphiti
|
|
29
29
|
uri.query = @proxy.query.hash.merge({
|
30
30
|
page: {
|
31
31
|
number: page,
|
32
|
-
size: page_size
|
33
|
-
}
|
32
|
+
size: page_size
|
33
|
+
}
|
34
34
|
}).to_query
|
35
35
|
uri.to_s
|
36
36
|
end
|
@@ -87,7 +87,7 @@ class Graphiti::Deserializer
|
|
87
87
|
type: data[:type],
|
88
88
|
temp_id: data[:'temp-id'],
|
89
89
|
method: action,
|
90
|
-
payload_path: ["data"]
|
90
|
+
payload_path: ["data"]
|
91
91
|
}
|
92
92
|
end
|
93
93
|
|
@@ -185,10 +185,10 @@ class Graphiti::Deserializer
|
|
185
185
|
jsonapi_type: datum[:type],
|
186
186
|
temp_id: temp_id,
|
187
187
|
method: method,
|
188
|
-
payload_path: ["included", included_idx]
|
188
|
+
payload_path: ["included", included_idx]
|
189
189
|
},
|
190
190
|
attributes: attributes,
|
191
|
-
relationships: relationships
|
191
|
+
relationships: relationships
|
192
192
|
}
|
193
193
|
end
|
194
194
|
|
data/lib/graphiti/errors.rb
CHANGED
@@ -143,8 +143,13 @@ module Graphiti
|
|
143
143
|
def message
|
144
144
|
allow = @filter.values[0][:allow]
|
145
145
|
deny = @filter.values[0][:deny]
|
146
|
+
value_string = if @value == "(empty)"
|
147
|
+
"empty value"
|
148
|
+
else
|
149
|
+
"value #{@value.inspect}"
|
150
|
+
end
|
146
151
|
msg = <<-MSG
|
147
|
-
#{@resource.class.name}: tried to filter on #{@filter.keys[0].inspect}, but passed invalid
|
152
|
+
#{@resource.class.name}: tried to filter on #{@filter.keys[0].inspect}, but passed invalid #{value_string}.
|
148
153
|
MSG
|
149
154
|
msg << "\nAllowlist: #{allow.inspect}" if allow
|
150
155
|
msg << "\nDenylist: #{deny.inspect}" if deny
|
@@ -189,7 +194,7 @@ module Graphiti
|
|
189
194
|
|
190
195
|
Make sure the endpoint "#{@sideload.resource.endpoint[:full_path]}" exists with action #{@action.inspect}, or customize the endpoint for #{@sideload.resource.class.name}.
|
191
196
|
|
192
|
-
If you do not wish to generate a link, pass link: false or set self.
|
197
|
+
If you do not wish to generate a link, pass link: false or set self.autolink = false.
|
193
198
|
MSG
|
194
199
|
end
|
195
200
|
end
|
@@ -316,14 +321,14 @@ module Graphiti
|
|
316
321
|
sortable: "sort on",
|
317
322
|
filterable: "filter on",
|
318
323
|
readable: "read",
|
319
|
-
writable: "write"
|
324
|
+
writable: "write"
|
320
325
|
}[@flag]
|
321
326
|
else
|
322
327
|
{
|
323
328
|
sortable: "add sort",
|
324
329
|
filterable: "add filter",
|
325
330
|
readable: "read",
|
326
|
-
writable: "write"
|
331
|
+
writable: "write"
|
327
332
|
}[@flag]
|
328
333
|
end
|
329
334
|
end
|
@@ -722,6 +727,21 @@ module Graphiti
|
|
722
727
|
end
|
723
728
|
|
724
729
|
class RecordNotFound < Base
|
730
|
+
def initialize(resource = nil, id = nil, path = nil)
|
731
|
+
@resource = resource
|
732
|
+
@id = id
|
733
|
+
@path = path
|
734
|
+
end
|
735
|
+
|
736
|
+
def message
|
737
|
+
if !@resource.nil? && !@id.nil?
|
738
|
+
"The referenced resource '#{@resource}' with id '#{@id}' could not be found.".tap do |msg|
|
739
|
+
msg << " Referenced at '#{@path}'" unless @path.nil?
|
740
|
+
end
|
741
|
+
else
|
742
|
+
"Specified Record Not Found"
|
743
|
+
end
|
744
|
+
end
|
725
745
|
end
|
726
746
|
|
727
747
|
class RequiredFilter < Base
|
data/lib/graphiti/query.rb
CHANGED
@@ -232,7 +232,7 @@ module Graphiti
|
|
232
232
|
return true if @resource.remote?
|
233
233
|
|
234
234
|
if (att = @resource.get_attr(name, flag, request: true))
|
235
|
-
|
235
|
+
att
|
236
236
|
else
|
237
237
|
not_associated_name = !@resource.class.association_names.include?(name)
|
238
238
|
not_associated_type = !@resource.class.association_types.include?(name)
|
@@ -262,8 +262,8 @@ module Graphiti
|
|
262
262
|
def parse_fieldset(fieldset)
|
263
263
|
{}.tap do |hash|
|
264
264
|
fieldset.each_pair do |type, fields|
|
265
|
-
type
|
266
|
-
fields
|
265
|
+
type = type.to_sym
|
266
|
+
fields = fields.split(",") unless fields.is_a?(Array)
|
267
267
|
hash[type] = fields.map(&:to_sym)
|
268
268
|
end
|
269
269
|
end
|
@@ -282,7 +282,7 @@ module Graphiti
|
|
282
282
|
|
283
283
|
def sort_hash(attr)
|
284
284
|
value = attr[0] == "-" ? :desc : :asc
|
285
|
-
key
|
285
|
+
key = attr.sub("-", "").to_sym
|
286
286
|
|
287
287
|
{key => value}
|
288
288
|
end
|
data/lib/graphiti/railtie.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Graphiti
|
2
2
|
class RequestValidator
|
3
3
|
delegate :validate,
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
:validate!,
|
5
|
+
:errors,
|
6
|
+
:deserialized_payload,
|
7
|
+
to: :@validator
|
8
8
|
|
9
9
|
def initialize(root_resource, raw_params)
|
10
10
|
@validator = ValidatorFactory.create(root_resource, raw_params)
|
@@ -5,7 +5,7 @@ module Graphiti
|
|
5
5
|
if required_payload? && payload_matches_endpoint?
|
6
6
|
super
|
7
7
|
else
|
8
|
-
|
8
|
+
false
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -36,7 +36,6 @@ module Graphiti
|
|
36
36
|
attribute_mismatch([:data, :id])
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
39
|
meta_type = @raw_params.dig(:data, :type)
|
41
40
|
|
42
41
|
# NOTE: calling #to_s and comparing 2 strings is slower than
|
@@ -25,7 +25,7 @@ module Graphiti
|
|
25
25
|
|
26
26
|
def validate!
|
27
27
|
unless validate
|
28
|
-
raise @error_class || Graphiti::Errors::InvalidRequest,
|
28
|
+
raise @error_class || Graphiti::Errors::InvalidRequest, errors
|
29
29
|
end
|
30
30
|
|
31
31
|
true
|
@@ -47,7 +47,7 @@ module Graphiti
|
|
47
47
|
def process_relationships(resource, relationships, payload_path)
|
48
48
|
opts = {
|
49
49
|
resource: resource,
|
50
|
-
relationships: relationships
|
50
|
+
relationships: relationships
|
51
51
|
}
|
52
52
|
|
53
53
|
Graphiti::Util::RelationshipPayload.iterate(opts) do |x|
|
@@ -22,7 +22,7 @@ module Graphiti
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def type=(val)
|
25
|
-
val = val
|
25
|
+
val = val&.to_sym
|
26
26
|
if (val = super)
|
27
27
|
serializer.type(val)
|
28
28
|
end
|
@@ -42,7 +42,7 @@ module Graphiti
|
|
42
42
|
path: val,
|
43
43
|
full_path: val,
|
44
44
|
url: val,
|
45
|
-
actions: [:index, :show]
|
45
|
+
actions: [:index, :show]
|
46
46
|
}
|
47
47
|
end
|
48
48
|
|
@@ -82,7 +82,8 @@ module Graphiti
|
|
82
82
|
:attributes_schema_by_default,
|
83
83
|
:relationships_readable_by_default,
|
84
84
|
:relationships_writable_by_default,
|
85
|
-
:filters_accept_nil_by_default
|
85
|
+
:filters_accept_nil_by_default,
|
86
|
+
:filters_deny_empty_by_default
|
86
87
|
|
87
88
|
class << self
|
88
89
|
prepend Overrides
|
@@ -104,6 +105,7 @@ module Graphiti
|
|
104
105
|
default(klass, :relationships_readable_by_default, true)
|
105
106
|
default(klass, :relationships_writable_by_default, true)
|
106
107
|
default(klass, :filters_accept_nil_by_default, false)
|
108
|
+
default(klass, :filters_deny_empty_by_default, false)
|
107
109
|
|
108
110
|
unless klass.config[:attributes][:id]
|
109
111
|
klass.attribute :id, :integer_id
|
@@ -197,7 +199,7 @@ module Graphiti
|
|
197
199
|
attributes: {},
|
198
200
|
extra_attributes: {},
|
199
201
|
sideloads: {},
|
200
|
-
callbacks: {}
|
202
|
+
callbacks: {}
|
201
203
|
}
|
202
204
|
end
|
203
205
|
|
@@ -32,7 +32,8 @@ module Graphiti
|
|
32
32
|
dependencies: opts[:dependent],
|
33
33
|
required: required,
|
34
34
|
operators: operators.to_hash,
|
35
|
-
allow_nil: opts.fetch(:allow_nil, filters_accept_nil_by_default)
|
35
|
+
allow_nil: opts.fetch(:allow_nil, filters_accept_nil_by_default),
|
36
|
+
deny_empty: opts.fetch(:deny_empty, filters_deny_empty_by_default)
|
36
37
|
}
|
37
38
|
elsif (type = args[0])
|
38
39
|
attribute name, type, only: [:filterable], allow: opts[:allow]
|
@@ -55,7 +56,7 @@ module Graphiti
|
|
55
56
|
|
56
57
|
if get_attr(name, :sortable, raise_error: :only_unsupported)
|
57
58
|
config[:sorts][name] = {
|
58
|
-
proc: blk
|
59
|
+
proc: blk
|
59
60
|
}.merge(opts.slice(:only))
|
60
61
|
elsif (type = args[0])
|
61
62
|
attribute name, type, only: [:sortable]
|
@@ -78,7 +79,7 @@ module Graphiti
|
|
78
79
|
def default_filter(name = nil, &blk)
|
79
80
|
name ||= :__default
|
80
81
|
config[:default_filters][name.to_sym] = {
|
81
|
-
filter: blk
|
82
|
+
filter: blk
|
82
83
|
}
|
83
84
|
end
|
84
85
|
|
@@ -131,7 +132,7 @@ module Graphiti
|
|
131
132
|
readable: true,
|
132
133
|
writable: false,
|
133
134
|
sortable: false,
|
134
|
-
filterable: false
|
135
|
+
filterable: false
|
135
136
|
}
|
136
137
|
options = defaults.merge(options)
|
137
138
|
config[:extra_attributes][name] = options
|
@@ -39,7 +39,7 @@ module Graphiti
|
|
39
39
|
path: path,
|
40
40
|
full_path: full_path_for(path),
|
41
41
|
url: url_for(path),
|
42
|
-
actions: DEFAULT_ACTIONS.dup
|
42
|
+
actions: DEFAULT_ACTIONS.dup
|
43
43
|
}
|
44
44
|
end
|
45
45
|
|
@@ -49,7 +49,7 @@ module Graphiti
|
|
49
49
|
path: path,
|
50
50
|
full_path: full_path_for(path),
|
51
51
|
url: url_for(path),
|
52
|
-
actions: actions
|
52
|
+
actions: actions
|
53
53
|
}
|
54
54
|
end
|
55
55
|
|
@@ -60,7 +60,7 @@ module Graphiti
|
|
60
60
|
path: path,
|
61
61
|
full_path: full_path_for(path),
|
62
62
|
url: url_for(path),
|
63
|
-
actions: actions
|
63
|
+
actions: actions
|
64
64
|
}]
|
65
65
|
end
|
66
66
|
|
@@ -89,7 +89,8 @@ module Graphiti
|
|
89
89
|
|
90
90
|
def update(update_params, meta = nil)
|
91
91
|
model_instance = nil
|
92
|
-
id = update_params
|
92
|
+
id = update_params[:id]
|
93
|
+
update_params = update_params.except(:id)
|
93
94
|
|
94
95
|
run_callbacks :persistence, :update, update_params, meta do
|
95
96
|
run_callbacks :attributes, :update, update_params, meta do |params|
|
@@ -67,7 +67,8 @@ module Graphiti
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def resource_for_model(model)
|
70
|
-
resource = children.find { |c| model.
|
70
|
+
resource = children.find { |c| model.class == c.model } ||
|
71
|
+
children.find { |c| model.is_a?(c.model) }
|
71
72
|
if resource.nil?
|
72
73
|
raise Errors::PolymorphicResourceChildNotFound.new(self, model: model)
|
73
74
|
else
|
data/lib/graphiti/runner.rb
CHANGED
data/lib/graphiti/schema.rb
CHANGED
@@ -33,7 +33,7 @@ module Graphiti
|
|
33
33
|
{
|
34
34
|
resources: generate_resources,
|
35
35
|
endpoints: generate_endpoints,
|
36
|
-
types: generate_types
|
36
|
+
types: generate_types
|
37
37
|
}
|
38
38
|
end
|
39
39
|
|
@@ -94,7 +94,7 @@ module Graphiti
|
|
94
94
|
extra_attributes: extra_attributes(r),
|
95
95
|
sorts: sorts(r),
|
96
96
|
filters: filters(r),
|
97
|
-
relationships: relationships(r)
|
97
|
+
relationships: relationships(r)
|
98
98
|
}
|
99
99
|
|
100
100
|
if r.default_sort
|
@@ -121,7 +121,7 @@ module Graphiti
|
|
121
121
|
name: r.name,
|
122
122
|
description: r.description,
|
123
123
|
remote: r.remote_url,
|
124
|
-
relationships: relationships(r)
|
124
|
+
relationships: relationships(r)
|
125
125
|
}
|
126
126
|
}
|
127
127
|
|
@@ -136,7 +136,7 @@ module Graphiti
|
|
136
136
|
type: config[:type].to_s,
|
137
137
|
readable: flag(config[:readable]),
|
138
138
|
writable: flag(config[:writable]),
|
139
|
-
description: resource.attribute_description(name)
|
139
|
+
description: resource.attribute_description(name)
|
140
140
|
}
|
141
141
|
end
|
142
142
|
end
|
@@ -149,7 +149,7 @@ module Graphiti
|
|
149
149
|
attrs[name] = {
|
150
150
|
type: config[:type].to_s,
|
151
151
|
readable: flag(config[:readable]),
|
152
|
-
description: resource.attribute_description(name)
|
152
|
+
description: resource.attribute_description(name)
|
153
153
|
}
|
154
154
|
end
|
155
155
|
end
|
@@ -186,7 +186,7 @@ module Graphiti
|
|
186
186
|
|
187
187
|
config = {
|
188
188
|
type: filter[:type].to_s,
|
189
|
-
operators: filter[:operators].keys.map(&:to_s)
|
189
|
+
operators: filter[:operators].keys.map(&:to_s)
|
190
190
|
}
|
191
191
|
|
192
192
|
config[:single] = true if filter[:single]
|
data/lib/graphiti/scope.rb
CHANGED
@@ -3,10 +3,10 @@ module Graphiti
|
|
3
3
|
attr_accessor :object, :unpaginated_object
|
4
4
|
attr_reader :pagination
|
5
5
|
def initialize(object, resource, query, opts = {})
|
6
|
-
@object
|
7
|
-
@resource
|
8
|
-
@query
|
9
|
-
@opts
|
6
|
+
@object = object
|
7
|
+
@resource = resource
|
8
|
+
@query = query
|
9
|
+
@opts = opts
|
10
10
|
|
11
11
|
@object = @resource.around_scoping(@object, @query.hash) { |scope|
|
12
12
|
apply_scoping(scope, opts)
|
@@ -75,7 +75,7 @@ module Graphiti
|
|
75
75
|
resource: @resource,
|
76
76
|
params: @opts[:params],
|
77
77
|
sideload: @opts[:sideload],
|
78
|
-
parent: @opts[:parent]
|
78
|
+
parent: @opts[:parent]
|
79
79
|
# Set once data is resolved within block
|
80
80
|
# results: ...
|
81
81
|
}
|