graphiti 1.0.alpha.4 → 1.0.alpha.5
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/lib/graphiti/adapters/abstract.rb +28 -93
- data/lib/graphiti/adapters/active_record/base.rb +65 -51
- data/lib/graphiti/errors.rb +38 -2
- data/lib/graphiti/resource.rb +1 -0
- data/lib/graphiti/resource/configuration.rb +7 -3
- data/lib/graphiti/resource/dsl.rb +9 -1
- data/lib/graphiti/resource/polymorphism.rb +1 -1
- data/lib/graphiti/resource/sideloading.rb +2 -2
- data/lib/graphiti/schema.rb +7 -3
- data/lib/graphiti/schema_diff.rb +22 -0
- data/lib/graphiti/scoping/filter.rb +21 -0
- data/lib/graphiti/sideload.rb +1 -0
- data/lib/graphiti/util/link.rb +23 -4
- data/lib/graphiti/util/serializer_attributes.rb +4 -3
- data/lib/graphiti/util/serializer_relationships.rb +2 -6
- data/lib/graphiti/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a97ae8d131e23bc5acf8f8a4fcb97e7cdce25fdc
|
4
|
+
data.tar.gz: 04ae2c8cc7d56ffea5dc0f8a3fec1a7506a5baeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 254a66e4e0bd21eef9038bc8e756b2195cf0d4f0b817944e2f5c71ca139c40da3d0c3a99d7d540b92f77b4bdb5a6acade53efce5ffb839f61ca1a6dcfd01d46e
|
7
|
+
data.tar.gz: 7790a1eb890eeff8bdf7690df749b3841fbff88487d6ef39dc7f3e9679bdabb9854898ad833e9ed8c5427712f14af416ff6dee4d9ba92cecfe34f773997b1adc
|
@@ -1,82 +1,27 @@
|
|
1
1
|
module Graphiti
|
2
2
|
module Adapters
|
3
|
-
# Adapters DRY up common resource logic.
|
4
|
-
#
|
5
|
-
# For instance, there's no reason to write ActiveRecord logic like this in
|
6
|
-
# every Resource:
|
7
|
-
#
|
8
|
-
# allow_filter :title do |scope, value|
|
9
|
-
# scope.where(title: value)
|
10
|
-
# end
|
11
|
-
#
|
12
|
-
# sort do |scope, att, dir|
|
13
|
-
# scope.order(att => dir)
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# paginate do |scope, current_page, per_page|
|
17
|
-
# scope.page(current_page).per(per_page)
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# This logic can be re-used through an *Adapter*:
|
21
|
-
#
|
22
|
-
# use_adapter Graphiti::Adapters::ActiveRecord
|
23
|
-
# allow_filter :title
|
24
|
-
#
|
25
|
-
# Adapters are pretty simple to write. The corresponding code for the above
|
26
|
-
# ActiveRecord adapter, which should look pretty familiar:
|
27
|
-
#
|
28
|
-
# class Graphiti::Adapters::ActiveRecord
|
29
|
-
# def filter(scope, attribute, value)
|
30
|
-
# scope.where(attribute => value)
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# def order(scope, attribute, direction)
|
34
|
-
# scope.order(attribute => direction)
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
# def paginate(scope, current_page, per_page)
|
38
|
-
# scope.page(current_page).per(per_page)
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# An adapter can have a corresponding +sideloading_module+. This module
|
43
|
-
# gets mixed in to a Sideload. In other words, *Resource* is to
|
44
|
-
# *Adapter* as *Sideload* is to *Adapter#sideloading_module*. Use this
|
45
|
-
# module to define DSL methods that wrap #allow_sideload:
|
46
|
-
#
|
47
|
-
# class MyAdapter < Graphiti::Adapters::Abstract
|
48
|
-
# # ... code ...
|
49
|
-
# def sideloading_module
|
50
|
-
# MySideloadingAdapter
|
51
|
-
# end
|
52
|
-
# end
|
53
|
-
#
|
54
|
-
# module MySideloadingAdapter
|
55
|
-
# def belongs_to(association_name)
|
56
|
-
# allow_sideload association_name do
|
57
|
-
# # ... code ...
|
58
|
-
# end
|
59
|
-
# end
|
60
|
-
# end
|
61
|
-
#
|
62
|
-
# # And now in your Resource:
|
63
|
-
# class MyResource < ApplicationResource
|
64
|
-
# # ... code ...
|
65
|
-
# use_adapter MyAdapter
|
66
|
-
#
|
67
|
-
# belongs_to :my_association
|
68
|
-
# end
|
69
|
-
#
|
70
|
-
# If you need the adapter to do *nothing*, because perhaps the API you
|
71
|
-
# are hitting does not support sorting,
|
72
|
-
# use +Graphiti::Adapters::Null+.
|
73
|
-
#
|
74
|
-
# @see Resource.use_adapter
|
75
|
-
# @see Adapters::ActiveRecord
|
76
|
-
# @see Adapters::ActiveRecordSideloading
|
77
|
-
# @see Adapters::Null
|
78
3
|
class Abstract
|
79
|
-
|
4
|
+
attr_reader :resource
|
5
|
+
|
6
|
+
def initialize(resource)
|
7
|
+
@resource = resource
|
8
|
+
end
|
9
|
+
|
10
|
+
# You want to override this!
|
11
|
+
# Map of association_type => sideload_class
|
12
|
+
# e.g.
|
13
|
+
# { has_many: Adapters::ActiveRecord::HasManySideload }
|
14
|
+
def self.sideloading_classes
|
15
|
+
{
|
16
|
+
has_many: ::Graphiti::Sideload::HasMany,
|
17
|
+
belongs_to: ::Graphiti::Sideload::BelongsTo,
|
18
|
+
has_one: ::Graphiti::Sideload::HasOne,
|
19
|
+
many_to_many: ::Graphiti::Sideload::ManyToMany,
|
20
|
+
polymorphic_belongs_to: ::Graphiti::Sideload::PolymorphicBelongsTo
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.default_operators
|
80
25
|
{
|
81
26
|
string: [
|
82
27
|
:eq,
|
@@ -423,7 +368,11 @@ module Graphiti
|
|
423
368
|
parent, child, association_name, association_type
|
424
369
|
else
|
425
370
|
if [:has_many, :many_to_many].include?(association_type)
|
426
|
-
parent.send(:"#{association_name}")
|
371
|
+
if parent.send(:"#{association_name}").nil?
|
372
|
+
parent.send(:"#{association_name}=", [child])
|
373
|
+
else
|
374
|
+
parent.send(:"#{association_name}") << child
|
375
|
+
end
|
427
376
|
else
|
428
377
|
parent.send(:"#{association_name}=", child)
|
429
378
|
end
|
@@ -470,20 +419,6 @@ module Graphiti
|
|
470
419
|
raise 'you must override #disassociate in an adapter subclass'
|
471
420
|
end
|
472
421
|
|
473
|
-
# You want to override this!
|
474
|
-
# Map of association_type => sideload_class
|
475
|
-
# e.g.
|
476
|
-
# { has_many: Adapters::ActiveRecord::HasManySideload }
|
477
|
-
def sideloading_classes
|
478
|
-
{
|
479
|
-
has_many: ::Graphiti::Sideload::HasMany,
|
480
|
-
belongs_to: ::Graphiti::Sideload::BelongsTo,
|
481
|
-
has_one: ::Graphiti::Sideload::HasOne,
|
482
|
-
many_to_many: ::Graphiti::Sideload::ManyToMany,
|
483
|
-
polymorphic_belongs_to: ::Graphiti::Sideload::PolymorphicBelongsTo
|
484
|
-
}
|
485
|
-
end
|
486
|
-
|
487
422
|
# @param [Class] model_class The configured model class (see Resource.model)
|
488
423
|
# @param [Hash] create_params Attributes + id
|
489
424
|
# @return the model instance just created
|
@@ -528,13 +463,13 @@ module Graphiti
|
|
528
463
|
|
529
464
|
private
|
530
465
|
|
531
|
-
def numerical_operators
|
466
|
+
def self.numerical_operators
|
532
467
|
[:eq, :not_eq, :gt, :gte, :lt, :lte]
|
533
468
|
end
|
534
469
|
|
535
470
|
def activerecord_adapter
|
536
471
|
@activerecord_adapter ||=
|
537
|
-
::Graphiti::Adapters::ActiveRecord::Base.new
|
472
|
+
::Graphiti::Adapters::ActiveRecord::Base.new(resource)
|
538
473
|
end
|
539
474
|
|
540
475
|
def activerecord_associate?(parent, child, association_name)
|
@@ -2,6 +2,24 @@ module Graphiti
|
|
2
2
|
module Adapters
|
3
3
|
module ActiveRecord
|
4
4
|
class Base < ::Graphiti::Adapters::Abstract
|
5
|
+
def filter_eq(scope, attribute, value)
|
6
|
+
scope.where(attribute => value)
|
7
|
+
end
|
8
|
+
alias :filter_integer_eq :filter_eq
|
9
|
+
alias :filter_float_eq :filter_eq
|
10
|
+
alias :filter_big_decimal_eq :filter_eq
|
11
|
+
alias :filter_date_eq :filter_eq
|
12
|
+
alias :filter_boolean_eq :filter_eq
|
13
|
+
|
14
|
+
def filter_not_eq(scope, attribute, value)
|
15
|
+
scope.where.not(attribute => value)
|
16
|
+
end
|
17
|
+
alias :filter_integer_not_eq :filter_not_eq
|
18
|
+
alias :filter_float_not_eq :filter_not_eq
|
19
|
+
alias :filter_big_decimal_not_eq :filter_not_eq
|
20
|
+
alias :filter_date_not_eq :filter_not_eq
|
21
|
+
alias :filter_boolean_not_eq :filter_not_eq
|
22
|
+
|
5
23
|
def filter_string_eq(scope, attribute, value, is_not: false)
|
6
24
|
column = scope.klass.arel_table[attribute]
|
7
25
|
clause = column.lower.eq_any(value.map(&:downcase))
|
@@ -54,56 +72,44 @@ module Graphiti
|
|
54
72
|
filter_string_match(scope, attribute, value, is_not: true)
|
55
73
|
end
|
56
74
|
|
57
|
-
def
|
58
|
-
clause = { attribute => value }
|
59
|
-
is_not ? scope.where.not(clause) : scope.where(clause)
|
60
|
-
end
|
61
|
-
alias :filter_float_eq :filter_integer_eq
|
62
|
-
alias :filter_big_decimal_eq :filter_integer_eq
|
63
|
-
alias :filter_date_eq :filter_integer_eq
|
64
|
-
alias :filter_boolean_eq :filter_integer_eq
|
65
|
-
|
66
|
-
def filter_integer_not_eq(scope, attribute, value)
|
67
|
-
filter_integer_eq(scope, attribute, value, is_not: true)
|
68
|
-
end
|
69
|
-
alias :filter_float_not_eq :filter_integer_not_eq
|
70
|
-
alias :filter_big_decimal_not_eq :filter_integer_not_eq
|
71
|
-
alias :filter_date_not_eq :filter_integer_not_eq
|
72
|
-
|
73
|
-
def filter_integer_gt(scope, attribute, value)
|
75
|
+
def filter_gt(scope, attribute, value)
|
74
76
|
column = scope.klass.arel_table[attribute]
|
75
77
|
scope.where(column.gt_any(value))
|
76
78
|
end
|
77
|
-
alias :
|
78
|
-
alias :
|
79
|
-
alias :
|
80
|
-
alias :
|
79
|
+
alias :filter_integer_gt :filter_gt
|
80
|
+
alias :filter_float_gt :filter_gt
|
81
|
+
alias :filter_big_decimal_gt :filter_gt
|
82
|
+
alias :filter_datetime_gt :filter_gt
|
83
|
+
alias :filter_date_gt :filter_gt
|
81
84
|
|
82
|
-
def
|
85
|
+
def filter_gte(scope, attribute, value)
|
83
86
|
column = scope.klass.arel_table[attribute]
|
84
87
|
scope.where(column.gteq_any(value))
|
85
88
|
end
|
86
|
-
alias :
|
87
|
-
alias :
|
88
|
-
alias :
|
89
|
-
alias :
|
89
|
+
alias :filter_integer_gte :filter_gte
|
90
|
+
alias :filter_float_gte :filter_gte
|
91
|
+
alias :filter_big_decimal_gte :filter_gte
|
92
|
+
alias :filter_datetime_gte :filter_gte
|
93
|
+
alias :filter_date_gte :filter_gte
|
90
94
|
|
91
|
-
def
|
95
|
+
def filter_lt(scope, attribute, value)
|
92
96
|
column = scope.klass.arel_table[attribute]
|
93
97
|
scope.where(column.lt_any(value))
|
94
98
|
end
|
95
|
-
alias :
|
96
|
-
alias :
|
97
|
-
alias :
|
98
|
-
alias :
|
99
|
+
alias :filter_integer_lt :filter_lt
|
100
|
+
alias :filter_float_lt :filter_lt
|
101
|
+
alias :filter_big_decimal_lt :filter_lt
|
102
|
+
alias :filter_datetime_lt :filter_lt
|
103
|
+
alias :filter_date_lt :filter_lt
|
99
104
|
|
100
|
-
def
|
105
|
+
def filter_lte(scope, attribute, value)
|
101
106
|
column = scope.klass.arel_table[attribute]
|
102
107
|
scope.where(column.lteq_any(value))
|
103
108
|
end
|
104
|
-
alias :
|
105
|
-
alias :
|
106
|
-
alias :
|
109
|
+
alias :filter_integer_lte :filter_lte
|
110
|
+
alias :filter_float_lte :filter_lte
|
111
|
+
alias :filter_big_decimal_lte :filter_lte
|
112
|
+
alias :filter_date_lte :filter_lte
|
107
113
|
|
108
114
|
# Ensure fractional seconds don't matter
|
109
115
|
def filter_datetime_eq(scope, attribute, value, is_not: false)
|
@@ -180,7 +186,7 @@ module Graphiti
|
|
180
186
|
end
|
181
187
|
end
|
182
188
|
|
183
|
-
def sideloading_classes
|
189
|
+
def self.sideloading_classes
|
184
190
|
{
|
185
191
|
has_many: HasManySideload,
|
186
192
|
has_one: HasOneSideload,
|
@@ -198,26 +204,34 @@ module Graphiti
|
|
198
204
|
end
|
199
205
|
|
200
206
|
def associate_all(parent, children, association_name, association_type)
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
207
|
+
if activerecord_associate?(parent, children[0], association_name)
|
208
|
+
association = parent.association(association_name)
|
209
|
+
association.loaded!
|
210
|
+
|
211
|
+
children.each do |child|
|
212
|
+
if association_type == :many_to_many &&
|
213
|
+
!parent.send(association_name).exists?(child.id) &&
|
214
|
+
[:create, :update].include?(Graphiti.context[:namespace])
|
215
|
+
parent.send(association_name) << child
|
216
|
+
else
|
217
|
+
target = association.instance_variable_get(:@target)
|
218
|
+
target |= [child]
|
219
|
+
association.instance_variable_set(:@target, target)
|
220
|
+
end
|
213
221
|
end
|
222
|
+
else
|
223
|
+
super
|
214
224
|
end
|
215
225
|
end
|
216
226
|
|
217
227
|
def associate(parent, child, association_name, association_type)
|
218
|
-
|
219
|
-
|
220
|
-
|
228
|
+
if activerecord_associate?(parent, child, association_name)
|
229
|
+
association = parent.association(association_name)
|
230
|
+
association.loaded!
|
231
|
+
association.instance_variable_set(:@target, child)
|
232
|
+
else
|
233
|
+
super
|
234
|
+
end
|
221
235
|
end
|
222
236
|
|
223
237
|
# When a has_and_belongs_to_many relationship, we don't have a foreign
|
data/lib/graphiti/errors.rb
CHANGED
@@ -16,23 +16,59 @@ The adapter #{@adapter.class} does not implement method '#{@method}', which was
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
class InvalidFilterValue < Base
|
20
|
+
def initialize(resource, filter, value)
|
21
|
+
@resource = resource
|
22
|
+
@filter = filter
|
23
|
+
@value = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def message
|
27
|
+
allow = @filter.values[0][:allow]
|
28
|
+
reject = @filter.values[0][:reject]
|
29
|
+
msg = <<-MSG
|
30
|
+
#{@resource.class.name}: tried to filter on #{@filter.keys[0].inspect}, but passed invalid value #{@value.inspect}.
|
31
|
+
MSG
|
32
|
+
msg << "\nWhitelist: #{allow.inspect}" if allow
|
33
|
+
msg << "\nBlacklist: #{reject.inspect}" if reject
|
34
|
+
msg
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
19
38
|
class InvalidLink < Base
|
20
|
-
def initialize(resource_class, sideload)
|
39
|
+
def initialize(resource_class, sideload, action)
|
21
40
|
@resource_class = resource_class
|
22
41
|
@sideload = sideload
|
42
|
+
@action = action
|
23
43
|
end
|
24
44
|
|
25
45
|
def message
|
26
46
|
<<-MSG
|
27
47
|
#{@resource_class.name}: Cannot link to sideload #{@sideload.name.inspect}!
|
28
48
|
|
29
|
-
Make sure the endpoint "#{@sideload.resource.endpoint[:full_path]}" exists, or customize the endpoint for #{@sideload.resource.class.name}.
|
49
|
+
Make sure the endpoint "#{@sideload.resource.endpoint[:full_path]}" exists with action #{@action.inspect}, or customize the endpoint for #{@sideload.resource.class.name}.
|
30
50
|
|
31
51
|
If you do not wish to generate a link, pass link: false or set self.relationship_links_by_default = false.
|
32
52
|
MSG
|
33
53
|
end
|
34
54
|
end
|
35
55
|
|
56
|
+
class SingularFilter < Base
|
57
|
+
def initialize(resource, filter, value)
|
58
|
+
@resource = resource
|
59
|
+
@filter = filter
|
60
|
+
@value = value
|
61
|
+
end
|
62
|
+
|
63
|
+
def message
|
64
|
+
<<-MSG
|
65
|
+
#{@resource.class.name}: passed multiple values to filter #{@filter.keys[0].inspect}, which was marked single: true.
|
66
|
+
|
67
|
+
Value was: #{@value.inspect}
|
68
|
+
MSG
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
36
72
|
class Unlinkable < Base
|
37
73
|
def initialize(resource_class, sideload)
|
38
74
|
@resource_class = resource_class
|
data/lib/graphiti/resource.rb
CHANGED
@@ -44,8 +44,8 @@ module Graphiti
|
|
44
44
|
attr_writer :config
|
45
45
|
end
|
46
46
|
|
47
|
-
class_attribute :adapter,
|
48
|
-
|
47
|
+
class_attribute :adapter, instance_reader: false
|
48
|
+
class_attribute :model,
|
49
49
|
:type,
|
50
50
|
:polymorphic,
|
51
51
|
:polymorphic_child,
|
@@ -66,7 +66,7 @@ module Graphiti
|
|
66
66
|
def self.inherited(klass)
|
67
67
|
super
|
68
68
|
klass.config = Util::Hash.deep_dup(config)
|
69
|
-
klass.adapter ||= Adapters::Abstract
|
69
|
+
klass.adapter ||= Adapters::Abstract
|
70
70
|
klass.default_sort ||= []
|
71
71
|
klass.default_page_size ||= 20
|
72
72
|
# re-assigning causes a new Class.new
|
@@ -200,6 +200,10 @@ module Graphiti
|
|
200
200
|
Util::AttributeCheck.run(self, name, flag, request, raise_error)
|
201
201
|
end
|
202
202
|
|
203
|
+
def adapter
|
204
|
+
@adapter ||= self.class.adapter.new(self)
|
205
|
+
end
|
206
|
+
|
203
207
|
def filters
|
204
208
|
self.class.filters
|
205
209
|
end
|
@@ -10,9 +10,17 @@ module Graphiti
|
|
10
10
|
if att = get_attr(name, :filterable, raise_error: :only_unsupported)
|
11
11
|
aliases = [name, opts[:aliases]].flatten.compact
|
12
12
|
operators = FilterOperators.build(self, att[:type], opts, &blk)
|
13
|
+
|
14
|
+
if Graphiti::Types[att[:type]][:canonical_name] == :boolean
|
15
|
+
opts[:single] = true
|
16
|
+
end
|
17
|
+
|
13
18
|
config[:filters][name.to_sym] = {
|
14
19
|
aliases: aliases,
|
15
20
|
type: att[:type],
|
21
|
+
allow: opts[:allow],
|
22
|
+
reject: opts[:reject],
|
23
|
+
single: !!opts[:single],
|
16
24
|
operators: operators.to_hash
|
17
25
|
}
|
18
26
|
else
|
@@ -53,7 +61,7 @@ module Graphiti
|
|
53
61
|
end
|
54
62
|
|
55
63
|
def stat(symbol_or_hash, &blk)
|
56
|
-
dsl = Stats::DSL.new(adapter, symbol_or_hash)
|
64
|
+
dsl = Stats::DSL.new(new.adapter, symbol_or_hash)
|
57
65
|
dsl.instance_eval(&blk) if blk
|
58
66
|
config[:stats][dsl.name] = dsl
|
59
67
|
end
|
@@ -25,7 +25,7 @@ module Graphiti
|
|
25
25
|
def associate(parent, child, association_name, type)
|
26
26
|
child_resource = self.class.resource_for_model(parent)
|
27
27
|
if child_resource.sideloads[association_name]
|
28
|
-
child_resource.adapter
|
28
|
+
child_resource.new.adapter
|
29
29
|
.associate(parent, child, association_name, type)
|
30
30
|
end
|
31
31
|
end
|
@@ -61,10 +61,10 @@ module Graphiti
|
|
61
61
|
resource = resource ||= Util::Class.infer_resource_class(self, name)
|
62
62
|
sideload = resource.sideload(as)
|
63
63
|
|
64
|
-
|
64
|
+
_resource = resource
|
65
65
|
filter sideload.true_foreign_key, resource.attributes[:id][:type] do
|
66
66
|
eq do |scope, value|
|
67
|
-
|
67
|
+
_resource.new.adapter.belongs_to_many_filter(sideload, scope, value)
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
data/lib/graphiti/schema.rb
CHANGED
@@ -132,11 +132,15 @@ module Graphiti
|
|
132
132
|
|
133
133
|
def filters(resource)
|
134
134
|
{}.tap do |f|
|
135
|
-
resource.filters.each_pair do |name,
|
135
|
+
resource.filters.each_pair do |name, filter|
|
136
136
|
config = {
|
137
|
-
type:
|
138
|
-
operators:
|
137
|
+
type: filter[:type].to_s,
|
138
|
+
operators: filter[:operators].keys.map(&:to_s)
|
139
139
|
}
|
140
|
+
config[:single] = true if filter[:single]
|
141
|
+
config[:allow] = filter[:allow] if filter[:allow]
|
142
|
+
config[:reject] = filter[:reject] if filter[:reject]
|
143
|
+
|
140
144
|
attr = resource.attributes[name]
|
141
145
|
if attr[:filterable].is_a?(Symbol)
|
142
146
|
if attr[:filterable] == :required
|
data/lib/graphiti/schema_diff.rb
CHANGED
@@ -94,6 +94,28 @@ module Graphiti
|
|
94
94
|
next
|
95
95
|
end
|
96
96
|
|
97
|
+
if new_filter[:single] && !old_filter[:single]
|
98
|
+
@errors << "#{old_resource[:name]}: filter #{name.inspect} became singular."
|
99
|
+
end
|
100
|
+
|
101
|
+
if new_filter[:allow] != old_filter[:allow]
|
102
|
+
new = new_filter[:allow] || []
|
103
|
+
old = old_filter[:allow] || []
|
104
|
+
diff = new - old
|
105
|
+
if diff.length > 0
|
106
|
+
@errors << "#{old_resource[:name]}: filter #{name.inspect} whitelist went from #{old.inspect} to #{new.inspect}."
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
if new_filter[:reject] != old_filter[:reject]
|
111
|
+
new = new_filter[:reject] || []
|
112
|
+
old = old_filter[:reject] || []
|
113
|
+
diff = new - old
|
114
|
+
if diff.length > 0
|
115
|
+
@errors << "#{old_resource[:name]}: filter #{name.inspect} blacklist went from #{old.inspect} to #{new.inspect}."
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
97
119
|
if (diff = old_filter[:operators] - new_filter[:operators]).length > 0
|
98
120
|
diff.each do |op|
|
99
121
|
@errors << "#{old_resource[:name]}: filter #{name.inspect} removed operator #{op.inspect}."
|
@@ -72,7 +72,28 @@ module Graphiti
|
|
72
72
|
operator = param_value.keys.first
|
73
73
|
value = param_value.values.first unless filter.values[0][:type] == :hash
|
74
74
|
value = value.split(',') if value.is_a?(String) && value.include?(',')
|
75
|
+
|
76
|
+
if filter.values[0][:single] && value.is_a?(Array)
|
77
|
+
raise Errors::SingularFilter.new(resource, filter, value)
|
78
|
+
end
|
79
|
+
|
75
80
|
value = coerce_types(param_name.to_sym, value)
|
81
|
+
|
82
|
+
value.each do |v|
|
83
|
+
if allow = filter.values[0][:allow]
|
84
|
+
unless allow.include?(v)
|
85
|
+
raise Errors::InvalidFilterValue.new(resource, filter, value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if reject = filter.values[0][:reject]
|
90
|
+
if reject.include?(v)
|
91
|
+
raise Errors::InvalidFilterValue.new(resource, filter, value)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
value = value[0] if filter.values[0][:single]
|
76
97
|
yield filter, operator, value
|
77
98
|
end
|
78
99
|
end
|
data/lib/graphiti/sideload.rb
CHANGED
data/lib/graphiti/util/link.rb
CHANGED
@@ -14,14 +14,33 @@ module Graphiti
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def generate
|
17
|
-
|
18
|
-
|
17
|
+
on_demand_links(raw_url)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def raw_url
|
23
|
+
if @sideload.link_proc
|
24
|
+
@sideload.link_proc.call(@model)
|
19
25
|
else
|
20
|
-
|
26
|
+
if params.empty?
|
27
|
+
path
|
28
|
+
else
|
29
|
+
"#{path}?#{URI.unescape(params.to_query)}"
|
30
|
+
end
|
21
31
|
end
|
22
32
|
end
|
23
33
|
|
24
|
-
|
34
|
+
def on_demand_links(url)
|
35
|
+
return url unless Graphiti.config.links_on_demand
|
36
|
+
|
37
|
+
if url.include?('?')
|
38
|
+
url << '&links=true'
|
39
|
+
else
|
40
|
+
url << '?links=true'
|
41
|
+
end
|
42
|
+
url
|
43
|
+
end
|
25
44
|
|
26
45
|
def params
|
27
46
|
@params ||= {}.tap do |params|
|
@@ -66,10 +66,11 @@ module Graphiti
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def wrap_proc(inner)
|
69
|
-
|
69
|
+
_resource = @resource.new
|
70
|
+
_name = @name
|
70
71
|
->(serializer_instance = nil) {
|
71
|
-
|
72
|
-
|
72
|
+
value = serializer_instance.instance_eval(&inner)
|
73
|
+
_resource.typecast(_name, value, :readable)
|
73
74
|
}
|
74
75
|
end
|
75
76
|
|
@@ -44,11 +44,7 @@ module Graphiti
|
|
44
44
|
proc do
|
45
45
|
if @proxy.query.links?
|
46
46
|
link(:related) do
|
47
|
-
|
48
|
-
prc.call(@object)
|
49
|
-
else
|
50
|
-
::Graphiti::Util::Link.new(sl, @object).generate
|
51
|
-
end
|
47
|
+
::Graphiti::Util::Link.new(sl, @object).generate
|
52
48
|
end
|
53
49
|
end
|
54
50
|
end
|
@@ -75,7 +71,7 @@ module Graphiti
|
|
75
71
|
action = sideload.type == :belongs_to ? :show : :index
|
76
72
|
prc = Graphiti.config.context_for_endpoint
|
77
73
|
unless prc.call(sideload.resource.endpoint[:full_path], action)
|
78
|
-
raise Errors::InvalidLink.new(@resource_class, sideload)
|
74
|
+
raise Errors::InvalidLink.new(@resource_class, sideload, action)
|
79
75
|
end
|
80
76
|
end
|
81
77
|
|
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.alpha.
|
4
|
+
version: 1.0.alpha.5
|
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-08-
|
11
|
+
date: 2018-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsonapi-serializable
|