graphiti 1.2.32 → 1.2.37
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/CHANGELOG.md +1 -0
- data/lib/graphiti.rb +2 -0
- data/lib/graphiti/errors.rb +30 -2
- data/lib/graphiti/extensions/extra_attribute.rb +3 -3
- data/lib/graphiti/hash_renderer.rb +51 -19
- data/lib/graphiti/request_validators/update_validator.rb +3 -3
- data/lib/graphiti/request_validators/validator.rb +20 -17
- data/lib/graphiti/resource/configuration.rb +17 -0
- data/lib/graphiti/resource/dsl.rb +11 -0
- data/lib/graphiti/schema.rb +18 -1
- data/lib/graphiti/schema_diff.rb +44 -4
- data/lib/graphiti/scoping/filter.rb +7 -0
- data/lib/graphiti/scoping/filter_group_validator.rb +78 -0
- data/lib/graphiti/sideload.rb +8 -0
- data/lib/graphiti/util/link.rb +4 -0
- data/lib/graphiti/version.rb +1 -1
- 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: 1ae9a52df4d54256b453573dc02f8eb2d8b1ad062eec7b38be89e3fbc7c6a9f3
|
|
4
|
+
data.tar.gz: 99ce6f9e0067eae81661338a933561b2c573b8a78bd92551d6ef5b21a16e6cdf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d77c8c268ef6c44b0557cf50fd3ecdfc71013fd25545cac6a935afd48f7e40caf47045af60649e03bf4f5ee01d8269d05f6be5991bc26a3c6303b7eb2d537afb
|
|
7
|
+
data.tar.gz: e83bf82263f8b59b2e10a243238c85204631f0eca55ed3306d07129f30d099421d4b518c841bf460187329760d0f9920ff90f5136797cfa1ed13d5cf18d90b3d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
3
|
Features:
|
|
4
|
+
- [329](https://github.com/graphiti-api/graphiti/pull/329) Propagate `extra_fields` to related resource links.
|
|
4
5
|
- [242](https://github.com/graphiti-api/graphiti/pull/242) Bump `jsonapi-renderer` to `~0.2.2` now that (https://github.com/jsonapi-rb/jsonapi-renderer/pull/36) is fixed.
|
|
5
6
|
- [158](https://github.com/graphiti-api/graphiti/pull/158) Filters options `allow_nil: true`
|
|
6
7
|
Option can be set at the resource level `Resource.filters_accept_nil_by_default = true`.
|
data/lib/graphiti.rb
CHANGED
|
@@ -6,6 +6,7 @@ require "active_support/core_ext/class/attribute"
|
|
|
6
6
|
require "active_support/core_ext/hash/conversions" # to_xml
|
|
7
7
|
require "active_support/concern"
|
|
8
8
|
require "active_support/time"
|
|
9
|
+
require "active_support/deprecation"
|
|
9
10
|
|
|
10
11
|
require "dry-types"
|
|
11
12
|
require "graphiti_errors"
|
|
@@ -141,6 +142,7 @@ require "graphiti/scoping/sort"
|
|
|
141
142
|
require "graphiti/scoping/paginate"
|
|
142
143
|
require "graphiti/scoping/extra_attributes"
|
|
143
144
|
require "graphiti/scoping/filterable"
|
|
145
|
+
require "graphiti/scoping/filter_group_validator"
|
|
144
146
|
require "graphiti/scoping/default_filter"
|
|
145
147
|
require "graphiti/scoping/filter"
|
|
146
148
|
require "graphiti/stats/dsl"
|
data/lib/graphiti/errors.rb
CHANGED
|
@@ -14,13 +14,12 @@ module Graphiti
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
class NullRelation
|
|
17
|
-
extend ActiveModel::Naming
|
|
18
17
|
attr_accessor :id, :errors, :pointer
|
|
19
18
|
|
|
20
19
|
def initialize(id, pointer)
|
|
21
20
|
@id = id
|
|
22
21
|
@pointer = pointer
|
|
23
|
-
@errors =
|
|
22
|
+
@errors = Graphiti::Util::SimpleErrors.new(self)
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def self.human_attribute_name(attr, options = {})
|
|
@@ -817,5 +816,34 @@ module Graphiti
|
|
|
817
816
|
|
|
818
817
|
class ConflictRequest < InvalidRequest
|
|
819
818
|
end
|
|
819
|
+
|
|
820
|
+
class FilterGroupInvalidRequirement < Base
|
|
821
|
+
def initialize(resource, valid_required_values)
|
|
822
|
+
@resource = resource
|
|
823
|
+
@valid_required_values = valid_required_values
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
def message
|
|
827
|
+
<<-MSG.gsub(/\s+/, " ").strip
|
|
828
|
+
The filter group required: value on resource #{@resource.class} must be one of the following:
|
|
829
|
+
#{@valid_required_values.join(", ")}
|
|
830
|
+
MSG
|
|
831
|
+
end
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
class FilterGroupMissingRequiredFilters < Base
|
|
835
|
+
def initialize(resource, filter_names, required)
|
|
836
|
+
@resource = resource
|
|
837
|
+
@filter_names = filter_names
|
|
838
|
+
@required_label = required == :all ? "All" : "One"
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
def message
|
|
842
|
+
<<-MSG.gsub(/\s+/, " ").strip
|
|
843
|
+
#{@required_label} of the following filters must be provided on resource #{@resource.type}:
|
|
844
|
+
#{@filter_names.join(", ")}
|
|
845
|
+
MSG
|
|
846
|
+
end
|
|
847
|
+
end
|
|
820
848
|
end
|
|
821
849
|
end
|
|
@@ -46,9 +46,9 @@ module Graphiti
|
|
|
46
46
|
next false unless instance_exec(&options[:if])
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
@extra_fields
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
next false unless @extra_fields
|
|
50
|
+
|
|
51
|
+
@extra_fields[@_type]&.include?(name) || @extra_fields[@resource&.type]&.include?(name)
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
attribute name, if: allow_field, &blk
|
|
@@ -74,19 +74,22 @@ module Graphiti
|
|
|
74
74
|
name_chain << k unless name_chain.last == k
|
|
75
75
|
|
|
76
76
|
unless remote_resource? && serializers.nil?
|
|
77
|
-
|
|
78
|
-
serializers.map
|
|
77
|
+
payload = if serializers.is_a?(Array)
|
|
78
|
+
data = serializers.map { |rr|
|
|
79
79
|
rr.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain: name_chain)
|
|
80
|
-
|
|
80
|
+
}
|
|
81
|
+
graphql ? {nodes: data} : data
|
|
81
82
|
elsif serializers.nil?
|
|
82
83
|
if @resource.class.respond_to?(:sideload)
|
|
83
84
|
if @resource.class.sideload(k).type.to_s.include?("_many")
|
|
84
|
-
[]
|
|
85
|
+
graphql ? {nodes: []} : []
|
|
85
86
|
end
|
|
86
87
|
end
|
|
87
88
|
else
|
|
88
89
|
serializers.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain: name_chain)
|
|
89
90
|
end
|
|
91
|
+
|
|
92
|
+
attrs[name.to_sym] = payload
|
|
90
93
|
end
|
|
91
94
|
end
|
|
92
95
|
|
|
@@ -133,29 +136,58 @@ module Graphiti
|
|
|
133
136
|
serializers = options[:data]
|
|
134
137
|
opts = options.slice(:fields, :include)
|
|
135
138
|
opts[:graphql] = @graphql
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
top_level_key = get_top_level_key(@resource, serializers.is_a?(Array))
|
|
140
|
+
|
|
141
|
+
hash = {top_level_key => {}}
|
|
142
|
+
nodes = get_nodes(serializers, opts)
|
|
143
|
+
add_nodes(hash, top_level_key, options, nodes, @graphql)
|
|
144
|
+
add_stats(hash, top_level_key, options, @graphql)
|
|
145
|
+
hash
|
|
139
146
|
end
|
|
140
147
|
|
|
141
148
|
private
|
|
142
149
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
def get_top_level_key(resource, is_many)
|
|
151
|
+
key = :data
|
|
152
|
+
|
|
153
|
+
if @graphql
|
|
154
|
+
key = @resource.graphql_entrypoint
|
|
155
|
+
key = key.to_s.singularize.to_sym unless is_many
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
key
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def get_nodes(serializers, opts)
|
|
162
|
+
if serializers.is_a?(Array)
|
|
163
|
+
serializers.map do |s|
|
|
164
|
+
s.to_hash(**opts)
|
|
151
165
|
end
|
|
166
|
+
else
|
|
167
|
+
serializers.to_hash(**opts)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def add_nodes(hash, top_level_key, opts, nodes, graphql)
|
|
172
|
+
payload = nodes
|
|
173
|
+
if graphql && nodes.is_a?(Array)
|
|
174
|
+
payload = {nodes: nodes}
|
|
175
|
+
end
|
|
152
176
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
177
|
+
# Don't render nodes if we only requested stats
|
|
178
|
+
unless graphql && opts[:fields].values == [[:stats]]
|
|
179
|
+
hash[top_level_key] = payload
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def add_stats(hash, top_level_key, options, graphql)
|
|
184
|
+
if options[:meta] && !options[:meta].empty?
|
|
185
|
+
if @graphql
|
|
186
|
+
if (stats = options[:meta][:stats])
|
|
187
|
+
hash[top_level_key][:stats] = stats
|
|
156
188
|
end
|
|
157
189
|
else
|
|
158
|
-
|
|
190
|
+
hash.merge!(options.slice(:meta))
|
|
159
191
|
end
|
|
160
192
|
end
|
|
161
193
|
end
|
|
@@ -26,17 +26,17 @@ module Graphiti
|
|
|
26
26
|
[:data, :type],
|
|
27
27
|
[:data, :id]
|
|
28
28
|
].each do |required_attr|
|
|
29
|
-
attribute_mismatch(required_attr) unless @
|
|
29
|
+
attribute_mismatch(required_attr) unless @params.dig(*required_attr)
|
|
30
30
|
end
|
|
31
31
|
errors.blank?
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def payload_matches_endpoint?
|
|
35
|
-
unless @
|
|
35
|
+
unless @params.dig(:data, :id) == @params.dig(:filter, :id)
|
|
36
36
|
attribute_mismatch([:data, :id])
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
meta_type = @
|
|
39
|
+
meta_type = @params.dig(:data, :type)
|
|
40
40
|
|
|
41
41
|
# NOTE: calling #to_s and comparing 2 strings is slower than
|
|
42
42
|
# calling #to_sym and comparing 2 symbols. But pre ruby-2.2
|
|
@@ -5,21 +5,31 @@ module Graphiti
|
|
|
5
5
|
|
|
6
6
|
def initialize(root_resource, raw_params, action)
|
|
7
7
|
@root_resource = root_resource
|
|
8
|
-
@
|
|
8
|
+
@params = normalized_params(raw_params)
|
|
9
9
|
@errors = Graphiti::Util::SimpleErrors.new(raw_params)
|
|
10
10
|
@action = action
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def validate
|
|
14
|
+
# Right now, all requests - even reads - go through the validator
|
|
15
|
+
# In the future these should have their own validation logic, but
|
|
16
|
+
# for now we can just bypass
|
|
17
|
+
return true unless @params.has_key?(:data)
|
|
18
|
+
|
|
14
19
|
resource = @root_resource
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
|
|
21
|
+
if @params[:data].has_key?(:type)
|
|
22
|
+
if (meta_type = deserialized_payload.meta[:type].try(:to_sym))
|
|
23
|
+
if @root_resource.type != meta_type && @root_resource.polymorphic?
|
|
24
|
+
resource = @root_resource.class.resource_for_type(meta_type).new
|
|
25
|
+
end
|
|
18
26
|
end
|
|
19
|
-
end
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
typecast_attributes(resource, deserialized_payload.attributes, deserialized_payload.meta[:payload_path])
|
|
29
|
+
process_relationships(resource, deserialized_payload.relationships, deserialized_payload.meta[:payload_path])
|
|
30
|
+
else
|
|
31
|
+
errors.add(:"data.type", :missing)
|
|
32
|
+
end
|
|
23
33
|
|
|
24
34
|
errors.blank?
|
|
25
35
|
end
|
|
@@ -33,14 +43,7 @@ module Graphiti
|
|
|
33
43
|
end
|
|
34
44
|
|
|
35
45
|
def deserialized_payload
|
|
36
|
-
@deserialized_payload ||=
|
|
37
|
-
payload = normalized_params
|
|
38
|
-
if payload[:data] && payload[:data][:type]
|
|
39
|
-
Graphiti::Deserializer.new(payload)
|
|
40
|
-
else
|
|
41
|
-
Graphiti::Deserializer.new({})
|
|
42
|
-
end
|
|
43
|
-
end
|
|
46
|
+
@deserialized_payload ||= Graphiti::Deserializer.new(@params)
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
private
|
|
@@ -86,8 +89,8 @@ module Graphiti
|
|
|
86
89
|
end
|
|
87
90
|
end
|
|
88
91
|
|
|
89
|
-
def normalized_params
|
|
90
|
-
normalized =
|
|
92
|
+
def normalized_params(raw_params)
|
|
93
|
+
normalized = raw_params
|
|
91
94
|
if normalized.respond_to?(:to_unsafe_h)
|
|
92
95
|
normalized = normalized.to_unsafe_h.deep_symbolize_keys
|
|
93
96
|
end
|
|
@@ -28,6 +28,14 @@ module Graphiti
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
def graphql_entrypoint=(val)
|
|
32
|
+
if val
|
|
33
|
+
super(val.to_s.camelize(:lower).to_sym)
|
|
34
|
+
else
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
31
39
|
# The .stat call stores a proc based on adapter
|
|
32
40
|
# So if we assign a new adapter, reconfigure
|
|
33
41
|
def adapter=(val)
|
|
@@ -191,6 +199,7 @@ module Graphiti
|
|
|
191
199
|
@config ||=
|
|
192
200
|
{
|
|
193
201
|
filters: {},
|
|
202
|
+
grouped_filters: {},
|
|
194
203
|
default_filters: {},
|
|
195
204
|
stats: {},
|
|
196
205
|
sort_all: nil,
|
|
@@ -227,6 +236,10 @@ module Graphiti
|
|
|
227
236
|
config[:filters]
|
|
228
237
|
end
|
|
229
238
|
|
|
239
|
+
def grouped_filters
|
|
240
|
+
config[:grouped_filters]
|
|
241
|
+
end
|
|
242
|
+
|
|
230
243
|
def sorts
|
|
231
244
|
config[:sorts]
|
|
232
245
|
end
|
|
@@ -265,6 +278,10 @@ module Graphiti
|
|
|
265
278
|
self.class.filters
|
|
266
279
|
end
|
|
267
280
|
|
|
281
|
+
def grouped_filters
|
|
282
|
+
self.class.grouped_filters
|
|
283
|
+
end
|
|
284
|
+
|
|
268
285
|
def sort_all
|
|
269
286
|
self.class.sort_all
|
|
270
287
|
end
|
|
@@ -44,6 +44,17 @@ module Graphiti
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def filter_group(filter_names, *args)
|
|
48
|
+
opts = args.extract_options!
|
|
49
|
+
|
|
50
|
+
Scoping::FilterGroupValidator.raise_unless_filter_group_requirement_valid!(self, opts[:required])
|
|
51
|
+
|
|
52
|
+
config[:grouped_filters] = {
|
|
53
|
+
names: filter_names,
|
|
54
|
+
required: opts[:required]
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
47
58
|
def sort_all(&blk)
|
|
48
59
|
if block_given?
|
|
49
60
|
config[:_sort_all] = blk
|
data/lib/graphiti/schema.rb
CHANGED
|
@@ -96,9 +96,14 @@ module Graphiti
|
|
|
96
96
|
extra_attributes: extra_attributes(r),
|
|
97
97
|
sorts: sorts(r),
|
|
98
98
|
filters: filters(r),
|
|
99
|
-
relationships: relationships(r)
|
|
99
|
+
relationships: relationships(r),
|
|
100
|
+
stats: stats(r)
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
if r.grouped_filters.any?
|
|
104
|
+
config[:filter_group] = r.grouped_filters
|
|
105
|
+
end
|
|
106
|
+
|
|
102
107
|
if r.default_sort
|
|
103
108
|
default_sort = r.default_sort.map { |s|
|
|
104
109
|
{s.keys.first.to_s => s.values.first.to_s}
|
|
@@ -165,6 +170,14 @@ module Graphiti
|
|
|
165
170
|
end
|
|
166
171
|
end
|
|
167
172
|
|
|
173
|
+
def stats(resource)
|
|
174
|
+
{}.tap do |stats|
|
|
175
|
+
resource.stats.each_pair do |name, config|
|
|
176
|
+
stats[name] = config.calculations.keys
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
168
181
|
def sorts(resource)
|
|
169
182
|
{}.tap do |s|
|
|
170
183
|
resource.sorts.each_pair do |name, sort|
|
|
@@ -212,6 +225,10 @@ module Graphiti
|
|
|
212
225
|
end
|
|
213
226
|
end
|
|
214
227
|
|
|
228
|
+
def filter_group(resource)
|
|
229
|
+
resource.config[:grouped_filters]
|
|
230
|
+
end
|
|
231
|
+
|
|
215
232
|
def relationships(resource)
|
|
216
233
|
{}.tap do |r|
|
|
217
234
|
resource.sideloads.each_pair do |name, config|
|
data/lib/graphiti/schema_diff.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module Graphiti
|
|
2
2
|
class SchemaDiff
|
|
3
3
|
def initialize(old, new)
|
|
4
|
-
@old = old.deep_symbolize_keys
|
|
5
|
-
@new = new.deep_symbolize_keys
|
|
4
|
+
@old = JSON.parse(old.to_json).deep_symbolize_keys
|
|
5
|
+
@new = JSON.parse(new.to_json).deep_symbolize_keys
|
|
6
6
|
@errors = []
|
|
7
7
|
end
|
|
8
8
|
|
|
@@ -30,6 +30,8 @@ module Graphiti
|
|
|
30
30
|
compare_extra_attributes(r, new_resource)
|
|
31
31
|
compare_sorts(r, new_resource)
|
|
32
32
|
compare_filters(r, new_resource)
|
|
33
|
+
compare_filter_group(r, new_resource)
|
|
34
|
+
compare_stats(r, new_resource)
|
|
33
35
|
compare_relationships(r, new_resource)
|
|
34
36
|
end
|
|
35
37
|
end
|
|
@@ -133,12 +135,12 @@ module Graphiti
|
|
|
133
135
|
end
|
|
134
136
|
|
|
135
137
|
if new_sort[:only] && !old_sort[:only]
|
|
136
|
-
@errors << "#{old_resource[:name]}: sort #{name.inspect} now limited to only #{new_sort[:only].inspect}."
|
|
138
|
+
@errors << "#{old_resource[:name]}: sort #{name.inspect} now limited to only #{new_sort[:only].to_sym.inspect}."
|
|
137
139
|
end
|
|
138
140
|
|
|
139
141
|
if new_sort[:only] && old_sort[:only]
|
|
140
142
|
if new_sort[:only] != old_sort[:only]
|
|
141
|
-
@errors << "#{old_resource[:name]}: sort #{name.inspect} was limited to only #{old_sort[:only].inspect}, now limited to only #{new_sort[:only].inspect}."
|
|
143
|
+
@errors << "#{old_resource[:name]}: sort #{name.inspect} was limited to only #{old_sort[:only].to_sym.inspect}, now limited to only #{new_sort[:only].to_sym.inspect}."
|
|
142
144
|
end
|
|
143
145
|
end
|
|
144
146
|
end
|
|
@@ -204,6 +206,44 @@ module Graphiti
|
|
|
204
206
|
end
|
|
205
207
|
end
|
|
206
208
|
|
|
209
|
+
def compare_filter_group(old_resource, new_resource)
|
|
210
|
+
if new_resource[:filter_group]
|
|
211
|
+
if old_resource[:filter_group]
|
|
212
|
+
new_names = new_resource[:filter_group][:names]
|
|
213
|
+
old_names = old_resource[:filter_group][:names]
|
|
214
|
+
diff = new_names - old_names
|
|
215
|
+
if !diff.empty? && new_resource[:filter_group][:required] == "all"
|
|
216
|
+
@errors << "#{old_resource[:name]}: all required filter group #{old_names.map(&:to_sym).inspect} added #{"member".pluralize(diff.length)} #{diff.map(&:to_sym).inspect}."
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
old_required = old_resource[:filter_group][:required]
|
|
220
|
+
new_required = new_resource[:filter_group][:required]
|
|
221
|
+
if old_required == "any" && new_required == "all"
|
|
222
|
+
@errors << "#{old_resource[:name]}: filter group #{old_names.map(&:to_sym).inspect} moved from required: :any to required: :all"
|
|
223
|
+
end
|
|
224
|
+
else
|
|
225
|
+
@errors << "#{old_resource[:name]}: filter group #{new_resource[:filter_group][:names].map(&:to_sym).inspect} was added."
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def compare_stats(old_resource, new_resource)
|
|
231
|
+
return unless old_resource.key?(:stats)
|
|
232
|
+
|
|
233
|
+
old_resource[:stats].each_pair do |name, old_calculations|
|
|
234
|
+
new_calculations = new_resource[:stats][name]
|
|
235
|
+
if new_calculations
|
|
236
|
+
old_calculations.each do |calc|
|
|
237
|
+
unless new_calculations.include?(calc)
|
|
238
|
+
@errors << "#{old_resource[:name]}: calculation #{calc.to_sym.inspect} was removed from stat #{name.inspect}."
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
else
|
|
242
|
+
@errors << "#{old_resource[:name]}: stat #{name.inspect} was removed."
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
207
247
|
def compare_endpoints
|
|
208
248
|
@old[:endpoints].each_pair do |path, old_endpoint|
|
|
209
249
|
unless (new_endpoint = @new[:endpoints][path])
|
|
@@ -3,6 +3,13 @@ module Graphiti
|
|
|
3
3
|
include Scoping::Filterable
|
|
4
4
|
|
|
5
5
|
def apply
|
|
6
|
+
unless @opts[:bypass_required_filters]
|
|
7
|
+
Graphiti::Scoping::FilterGroupValidator.new(
|
|
8
|
+
resource,
|
|
9
|
+
query_hash
|
|
10
|
+
).raise_unless_filter_group_requirements_met!
|
|
11
|
+
end
|
|
12
|
+
|
|
6
13
|
if missing_required_filters.any? && !@opts[:bypass_required_filters]
|
|
7
14
|
raise Errors::RequiredFilter.new(resource, missing_required_filters)
|
|
8
15
|
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Graphiti
|
|
2
|
+
class Scoping::FilterGroupValidator
|
|
3
|
+
VALID_REQUIRED_VALUES = %i[all any]
|
|
4
|
+
|
|
5
|
+
def self.raise_unless_filter_group_requirement_valid!(resource, requirement)
|
|
6
|
+
unless VALID_REQUIRED_VALUES.include?(requirement)
|
|
7
|
+
raise Errors::FilterGroupInvalidRequirement.new(
|
|
8
|
+
resource,
|
|
9
|
+
VALID_REQUIRED_VALUES
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(resource, query_hash)
|
|
17
|
+
@resource = resource
|
|
18
|
+
@query_hash = query_hash
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def raise_unless_filter_group_requirements_met!
|
|
22
|
+
return if grouped_filters.empty?
|
|
23
|
+
|
|
24
|
+
case filter_group_requirement
|
|
25
|
+
when :all
|
|
26
|
+
raise_unless_all_requirements_met!
|
|
27
|
+
when :any
|
|
28
|
+
raise_unless_any_requirements_met!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
attr_reader :resource, :query_hash
|
|
37
|
+
|
|
38
|
+
def raise_unless_all_requirements_met!
|
|
39
|
+
met = filter_group_names.all? { |filter_name| filter_group_filter_param.key?(filter_name) }
|
|
40
|
+
|
|
41
|
+
unless met
|
|
42
|
+
raise Errors::FilterGroupMissingRequiredFilters.new(
|
|
43
|
+
resource,
|
|
44
|
+
filter_group_names,
|
|
45
|
+
filter_group_requirement
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def raise_unless_any_requirements_met!
|
|
51
|
+
met = filter_group_names.any? { |filter_name| filter_group_filter_param.key?(filter_name) }
|
|
52
|
+
|
|
53
|
+
unless met
|
|
54
|
+
raise Errors::FilterGroupMissingRequiredFilters.new(
|
|
55
|
+
resource,
|
|
56
|
+
filter_group_names,
|
|
57
|
+
filter_group_requirement
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def filter_group_names
|
|
63
|
+
grouped_filters.fetch(:names, [])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def filter_group_requirement
|
|
67
|
+
grouped_filters.fetch(:required, :invalid)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def grouped_filters
|
|
71
|
+
resource.grouped_filters
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def filter_group_filter_param
|
|
75
|
+
query_hash.fetch(:filter, {})
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/graphiti/sideload.rb
CHANGED
|
@@ -136,6 +136,14 @@ module Graphiti
|
|
|
136
136
|
base_filter(parents)
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
+
def link_extra_fields
|
|
140
|
+
extra_fields_name = [association_name, resource.type].find { |param|
|
|
141
|
+
context.params.dig(:extra_fields, param)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
{resource.type => context.params.dig(:extra_fields, extra_fields_name)} if extra_fields_name
|
|
145
|
+
end
|
|
146
|
+
|
|
139
147
|
# The parent resource is a remote,
|
|
140
148
|
# AND the sideload is a remote to the same endpoint
|
|
141
149
|
def shared_remote?
|
data/lib/graphiti/util/link.rb
CHANGED
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.2.
|
|
4
|
+
version: 1.2.37
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lee Richmond
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-03-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jsonapi-serializable
|
|
@@ -308,6 +308,7 @@ files:
|
|
|
308
308
|
- lib/graphiti/scoping/default_filter.rb
|
|
309
309
|
- lib/graphiti/scoping/extra_attributes.rb
|
|
310
310
|
- lib/graphiti/scoping/filter.rb
|
|
311
|
+
- lib/graphiti/scoping/filter_group_validator.rb
|
|
311
312
|
- lib/graphiti/scoping/filterable.rb
|
|
312
313
|
- lib/graphiti/scoping/paginate.rb
|
|
313
314
|
- lib/graphiti/scoping/sort.rb
|