graphiti 1.2.16 → 1.3.9
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/.github/workflows/ci.yml +96 -0
- data/.standard.yml +4 -4
- data/Appraisals +23 -17
- data/CHANGELOG.md +7 -1
- data/Guardfile +5 -5
- data/deprecated_generators/graphiti/generator_mixin.rb +1 -0
- data/deprecated_generators/graphiti/resource_generator.rb +1 -1
- data/gemfiles/{rails_5.gemfile → rails_5_2.gemfile} +2 -2
- data/gemfiles/{rails_5_graphiti_rails.gemfile → rails_5_2_graphiti_rails.gemfile} +3 -4
- data/gemfiles/rails_6.gemfile +1 -1
- data/gemfiles/rails_6_graphiti_rails.gemfile +2 -3
- data/gemfiles/{rails_4.gemfile → rails_7.gemfile} +2 -2
- data/gemfiles/rails_7_graphiti_rails.gemfile +19 -0
- data/graphiti.gemspec +16 -16
- data/lib/graphiti/adapters/abstract.rb +20 -5
- data/lib/graphiti/adapters/active_record/belongs_to_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/has_many_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/has_one_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/{inferrence.rb → inference.rb} +2 -2
- data/lib/graphiti/adapters/active_record/many_to_many_sideload.rb +19 -0
- data/lib/graphiti/adapters/active_record.rb +119 -74
- data/lib/graphiti/adapters/graphiti_api.rb +1 -1
- data/lib/graphiti/adapters/null.rb +1 -1
- data/lib/graphiti/adapters/persistence/associations.rb +78 -0
- data/lib/graphiti/configuration.rb +3 -1
- data/lib/graphiti/debugger.rb +12 -8
- data/lib/graphiti/delegates/pagination.rb +47 -13
- data/lib/graphiti/deserializer.rb +3 -3
- data/lib/graphiti/errors.rb +109 -15
- data/lib/graphiti/extensions/extra_attribute.rb +4 -4
- data/lib/graphiti/extensions/temp_id.rb +1 -1
- data/lib/graphiti/filter_operators.rb +0 -1
- data/lib/graphiti/hash_renderer.rb +198 -21
- data/lib/graphiti/query.rb +105 -73
- data/lib/graphiti/railtie.rb +5 -5
- data/lib/graphiti/renderer.rb +19 -1
- data/lib/graphiti/request_validator.rb +10 -10
- data/lib/graphiti/request_validators/update_validator.rb +4 -5
- data/lib/graphiti/request_validators/validator.rb +38 -24
- data/lib/graphiti/resource/configuration.rb +35 -7
- data/lib/graphiti/resource/dsl.rb +34 -8
- data/lib/graphiti/resource/interface.rb +13 -3
- data/lib/graphiti/resource/links.rb +3 -3
- data/lib/graphiti/resource/persistence.rb +2 -1
- data/lib/graphiti/resource/polymorphism.rb +8 -2
- data/lib/graphiti/resource/remote.rb +2 -2
- data/lib/graphiti/resource/sideloading.rb +4 -4
- data/lib/graphiti/resource.rb +12 -1
- data/lib/graphiti/resource_proxy.rb +23 -3
- data/lib/graphiti/runner.rb +5 -5
- data/lib/graphiti/schema.rb +36 -11
- data/lib/graphiti/schema_diff.rb +44 -4
- data/lib/graphiti/scope.rb +8 -10
- data/lib/graphiti/scoping/base.rb +3 -3
- data/lib/graphiti/scoping/filter.rb +36 -15
- data/lib/graphiti/scoping/filter_group_validator.rb +78 -0
- data/lib/graphiti/scoping/paginate.rb +47 -3
- data/lib/graphiti/scoping/sort.rb +5 -7
- data/lib/graphiti/serializer.rb +49 -7
- data/lib/graphiti/sideload/belongs_to.rb +1 -1
- data/lib/graphiti/sideload/has_many.rb +19 -1
- data/lib/graphiti/sideload/many_to_many.rb +11 -4
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +3 -4
- data/lib/graphiti/sideload.rb +47 -23
- data/lib/graphiti/stats/dsl.rb +0 -1
- data/lib/graphiti/stats/payload.rb +12 -9
- data/lib/graphiti/types.rb +15 -15
- data/lib/graphiti/util/attribute_check.rb +1 -1
- data/lib/graphiti/util/class.rb +6 -0
- data/lib/graphiti/util/link.rb +10 -2
- data/lib/graphiti/util/persistence.rb +21 -78
- data/lib/graphiti/util/relationship_payload.rb +4 -4
- data/lib/graphiti/util/remote_params.rb +9 -4
- data/lib/graphiti/util/remote_serializer.rb +1 -0
- data/lib/graphiti/util/serializer_attributes.rb +41 -11
- data/lib/graphiti/util/simple_errors.rb +4 -4
- data/lib/graphiti/util/transaction_hooks_recorder.rb +1 -1
- data/lib/graphiti/version.rb +1 -1
- data/lib/graphiti.rb +6 -3
- metadata +46 -37
- data/.travis.yml +0 -59
data/lib/graphiti/errors.rb
CHANGED
@@ -2,6 +2,31 @@ module Graphiti
|
|
2
2
|
module Errors
|
3
3
|
class Base < StandardError; end
|
4
4
|
|
5
|
+
class UnreadableAttribute < Base
|
6
|
+
def initialize(resource_class, name)
|
7
|
+
@resource_class = resource_class
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def message
|
12
|
+
"#{@resource_class}: Requested field #{@name}, but not authorized to read it"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class NullRelation
|
17
|
+
attr_accessor :id, :errors, :pointer
|
18
|
+
|
19
|
+
def initialize(id, pointer)
|
20
|
+
@id = id
|
21
|
+
@pointer = pointer
|
22
|
+
@errors = Graphiti::Util::SimpleErrors.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.human_attribute_name(attr, options = {})
|
26
|
+
attr
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
5
30
|
class AdapterNotImplemented < Base
|
6
31
|
def initialize(adapter, attribute, method)
|
7
32
|
@adapter = adapter
|
@@ -10,7 +35,7 @@ module Graphiti
|
|
10
35
|
end
|
11
36
|
|
12
37
|
def message
|
13
|
-
|
38
|
+
<<~MSG
|
14
39
|
The adapter #{@adapter.class} does not implement method '#{@method}', which was requested for attribute '#{@attribute}'. Add this method to your adapter to support this filter operator.
|
15
40
|
MSG
|
16
41
|
end
|
@@ -24,7 +49,7 @@ module Graphiti
|
|
24
49
|
end
|
25
50
|
|
26
51
|
def message
|
27
|
-
|
52
|
+
<<~MSG
|
28
53
|
#{@parent_resource_class} sideload :#{@name} - #{@message}
|
29
54
|
MSG
|
30
55
|
end
|
@@ -53,7 +78,7 @@ module Graphiti
|
|
53
78
|
end
|
54
79
|
|
55
80
|
def message
|
56
|
-
|
81
|
+
<<~MSG
|
57
82
|
#{@resource_class}: Tried to pass block to .#{@method_name}, which only accepts a method name.
|
58
83
|
MSG
|
59
84
|
end
|
@@ -65,7 +90,7 @@ module Graphiti
|
|
65
90
|
end
|
66
91
|
|
67
92
|
def message
|
68
|
-
|
93
|
+
<<~MSG
|
69
94
|
#{@resource_class}: Tried to perform write operation. Writes are not supported for remote resources - hit the endpoint directly.
|
70
95
|
MSG
|
71
96
|
end
|
@@ -80,7 +105,7 @@ module Graphiti
|
|
80
105
|
end
|
81
106
|
|
82
107
|
def message
|
83
|
-
|
108
|
+
<<~MSG
|
84
109
|
#{@resource.class}: Tried to filter #{@filter_name.inspect} on operator #{@operator.inspect}, but not supported! Supported operators are #{@supported}.
|
85
110
|
MSG
|
86
111
|
end
|
@@ -93,7 +118,7 @@ module Graphiti
|
|
93
118
|
end
|
94
119
|
|
95
120
|
def message
|
96
|
-
|
121
|
+
<<~MSG
|
97
122
|
#{@sideload.parent_resource.class.name}: tried to sideload #{@sideload.name.inspect}, but more than one #{@sideload.parent_resource.model.name} was passed!
|
98
123
|
|
99
124
|
This is because you marked the sideload #{@sideload.name.inspect} with single: true
|
@@ -114,7 +139,7 @@ module Graphiti
|
|
114
139
|
end
|
115
140
|
|
116
141
|
def message
|
117
|
-
|
142
|
+
<<~MSG
|
118
143
|
#{@resource.class.name}: tried to sort on attribute #{@attribute.inspect}, but passed #{@direction.inspect} when only #{@allowlist.inspect} is supported.
|
119
144
|
MSG
|
120
145
|
end
|
@@ -127,7 +152,7 @@ module Graphiti
|
|
127
152
|
end
|
128
153
|
|
129
154
|
def message
|
130
|
-
|
155
|
+
<<~MSG
|
131
156
|
#{@resource_class.name}: called .on_extra_attribute #{@name.inspect}, but extra attribute #{@name.inspect} does not exist!
|
132
157
|
MSG
|
133
158
|
end
|
@@ -143,8 +168,13 @@ module Graphiti
|
|
143
168
|
def message
|
144
169
|
allow = @filter.values[0][:allow]
|
145
170
|
deny = @filter.values[0][:deny]
|
146
|
-
|
147
|
-
|
171
|
+
value_string = if @value == "(empty)"
|
172
|
+
"empty value"
|
173
|
+
else
|
174
|
+
"value #{@value.inspect}"
|
175
|
+
end
|
176
|
+
msg = <<~MSG
|
177
|
+
#{@resource.class.name}: tried to filter on #{@filter.keys[0].inspect}, but passed invalid #{value_string}.
|
148
178
|
MSG
|
149
179
|
msg << "\nAllowlist: #{allow.inspect}" if allow
|
150
180
|
msg << "\nDenylist: #{deny.inspect}" if deny
|
@@ -160,7 +190,7 @@ module Graphiti
|
|
160
190
|
end
|
161
191
|
|
162
192
|
def message
|
163
|
-
|
193
|
+
<<~MSG
|
164
194
|
#{@resource_class.name} You declared an attribute or filter of type "#{@enum_type}" without providing a list of permitted values, which is required.
|
165
195
|
|
166
196
|
When declaring an attribute:
|
@@ -184,12 +214,12 @@ module Graphiti
|
|
184
214
|
end
|
185
215
|
|
186
216
|
def message
|
187
|
-
|
217
|
+
<<~MSG
|
188
218
|
#{@resource_class.name}: Cannot link to sideload #{@sideload.name.inspect}!
|
189
219
|
|
190
220
|
Make sure the endpoint "#{@sideload.resource.endpoint[:full_path]}" exists with action #{@action.inspect}, or customize the endpoint for #{@sideload.resource.class.name}.
|
191
221
|
|
192
|
-
If you do not wish to generate a link, pass link: false or set self.
|
222
|
+
If you do not wish to generate a link, pass link: false or set self.autolink = false.
|
193
223
|
MSG
|
194
224
|
end
|
195
225
|
end
|
@@ -316,14 +346,14 @@ module Graphiti
|
|
316
346
|
sortable: "sort on",
|
317
347
|
filterable: "filter on",
|
318
348
|
readable: "read",
|
319
|
-
writable: "write"
|
349
|
+
writable: "write"
|
320
350
|
}[@flag]
|
321
351
|
else
|
322
352
|
{
|
323
353
|
sortable: "add sort",
|
324
354
|
filterable: "add filter",
|
325
355
|
readable: "read",
|
326
|
-
writable: "write"
|
356
|
+
writable: "write"
|
327
357
|
}[@flag]
|
328
358
|
end
|
329
359
|
end
|
@@ -361,6 +391,20 @@ module Graphiti
|
|
361
391
|
end
|
362
392
|
end
|
363
393
|
|
394
|
+
class UndefinedIDLookup < Base
|
395
|
+
def initialize(resource_class)
|
396
|
+
@resource_class = resource_class
|
397
|
+
end
|
398
|
+
|
399
|
+
def message
|
400
|
+
<<~MSG
|
401
|
+
Tried to resolve #{@resource_class} with an :id filter, but the filter was nil.
|
402
|
+
This can result in unscoping a query, which can cause incorrect values to be
|
403
|
+
returned which may or may not bypass standard access controls.
|
404
|
+
MSG
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
364
408
|
class UnknownAttribute < AttributeError
|
365
409
|
def message
|
366
410
|
"#{super}, but could not find an attribute with that name."
|
@@ -689,6 +733,12 @@ module Graphiti
|
|
689
733
|
end
|
690
734
|
end
|
691
735
|
|
736
|
+
class UnsupportedBeforeCursor < Base
|
737
|
+
def message
|
738
|
+
"Passing in page[before] and page[number] is not supported. Please create an issue if you need it!"
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
692
742
|
class InvalidInclude < Base
|
693
743
|
def initialize(resource, relationship)
|
694
744
|
@resource = resource
|
@@ -722,6 +772,21 @@ module Graphiti
|
|
722
772
|
end
|
723
773
|
|
724
774
|
class RecordNotFound < Base
|
775
|
+
def initialize(resource = nil, id = nil, path = nil)
|
776
|
+
@resource = resource
|
777
|
+
@id = id
|
778
|
+
@path = path
|
779
|
+
end
|
780
|
+
|
781
|
+
def message
|
782
|
+
if !@resource.nil? && !@id.nil?
|
783
|
+
"The referenced resource '#{@resource}' with id '#{@id}' could not be found.".tap do |msg|
|
784
|
+
msg << " Referenced at '#{@path}'" unless @path.nil?
|
785
|
+
end
|
786
|
+
else
|
787
|
+
"Specified Record Not Found"
|
788
|
+
end
|
789
|
+
end
|
725
790
|
end
|
726
791
|
|
727
792
|
class RequiredFilter < Base
|
@@ -757,5 +822,34 @@ module Graphiti
|
|
757
822
|
|
758
823
|
class ConflictRequest < InvalidRequest
|
759
824
|
end
|
825
|
+
|
826
|
+
class FilterGroupInvalidRequirement < Base
|
827
|
+
def initialize(resource, valid_required_values)
|
828
|
+
@resource = resource
|
829
|
+
@valid_required_values = valid_required_values
|
830
|
+
end
|
831
|
+
|
832
|
+
def message
|
833
|
+
<<-MSG.gsub(/\s+/, " ").strip
|
834
|
+
The filter group required: value on resource #{@resource.class} must be one of the following:
|
835
|
+
#{@valid_required_values.join(", ")}
|
836
|
+
MSG
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
class FilterGroupMissingRequiredFilters < Base
|
841
|
+
def initialize(resource, filter_names, required)
|
842
|
+
@resource = resource
|
843
|
+
@filter_names = filter_names
|
844
|
+
@required_label = required == :all ? "All" : "One"
|
845
|
+
end
|
846
|
+
|
847
|
+
def message
|
848
|
+
<<-MSG.gsub(/\s+/, " ").strip
|
849
|
+
#{@required_label} of the following filters must be provided on resource #{@resource.type}:
|
850
|
+
#{@filter_names.join(", ")}
|
851
|
+
MSG
|
852
|
+
end
|
853
|
+
end
|
760
854
|
end
|
761
855
|
end
|
@@ -43,12 +43,12 @@ module Graphiti
|
|
43
43
|
def extra_attribute(name, options = {}, &blk)
|
44
44
|
allow_field = proc {
|
45
45
|
if options[:if]
|
46
|
-
next false unless
|
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
|
@@ -13,7 +13,7 @@ module Graphiti
|
|
13
13
|
# Common interface for jsonapi-rb extensions
|
14
14
|
def as_jsonapi(*)
|
15
15
|
super.tap do |hash|
|
16
|
-
if (temp_id = @object.instance_variable_get(
|
16
|
+
if (temp_id = @object.instance_variable_get(:@_jsonapi_temp_id))
|
17
17
|
hash[:'temp-id'] = temp_id
|
18
18
|
end
|
19
19
|
end
|
@@ -1,55 +1,232 @@
|
|
1
1
|
module Graphiti
|
2
2
|
module SerializableHash
|
3
|
-
def to_hash(fields: nil, include: {})
|
3
|
+
def to_hash(fields: nil, include: {}, name_chain: [], graphql: false)
|
4
4
|
{}.tap do |hash|
|
5
|
-
|
5
|
+
if fields
|
6
|
+
fields_list = nil
|
7
|
+
|
8
|
+
# Dot syntax wins over jsonapi type
|
9
|
+
if name_chain.length > 0
|
10
|
+
fields_list = fields[name_chain.join(".").to_sym]
|
11
|
+
end
|
12
|
+
|
13
|
+
if fields_list.nil?
|
14
|
+
fields_list = fields[jsonapi_type]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# polymorphic resources - merge the PARENT type
|
19
|
+
if polymorphic_subclass?
|
20
|
+
if fields[@resource.type]
|
21
|
+
fields_list ||= []
|
22
|
+
fields_list |= fields[@resource.type]
|
23
|
+
end
|
24
|
+
|
25
|
+
if fields[jsonapi_type]
|
26
|
+
fields_list ||= []
|
27
|
+
fields_list |= fields[jsonapi_type]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
6
31
|
attrs = requested_attributes(fields_list).each_with_object({}) { |(k, v), h|
|
7
|
-
|
32
|
+
name = graphql ? k.to_s.camelize(:lower).to_sym : k
|
33
|
+
h[name] = instance_eval(&v)
|
34
|
+
}
|
35
|
+
|
36
|
+
# The main logic here is just !!include[k]
|
37
|
+
# But we also have special on__<type>--<name> includes
|
38
|
+
# Where we only include when matching the polymorphic type
|
39
|
+
rels = @_relationships.select { |k, v|
|
40
|
+
if include[k]
|
41
|
+
true
|
42
|
+
else
|
43
|
+
included = false
|
44
|
+
include.keys.each do |key|
|
45
|
+
split = key.to_s.split(/^on__/)
|
46
|
+
if split.length > 1
|
47
|
+
requested_type, key = split[1].split("--")
|
48
|
+
if requested_type.to_sym == jsonapi_type
|
49
|
+
included = k == key.to_sym
|
50
|
+
break
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
included
|
55
|
+
end
|
8
56
|
}
|
9
|
-
|
57
|
+
|
10
58
|
rels.each_with_object({}) do |(k, v), h|
|
59
|
+
nested_include = include[k]
|
60
|
+
|
61
|
+
# This logic only fires if it's a special on__<type>--<name> include
|
62
|
+
unless include.has_key?(k)
|
63
|
+
include.keys.each do |include_key|
|
64
|
+
if k == include_key.to_s.split("--")[1].to_sym
|
65
|
+
nested_include = include[include_key]
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
11
71
|
serializers = v.send(:resources)
|
12
|
-
|
13
|
-
|
14
|
-
|
72
|
+
name = graphql ? k.to_s.camelize(:lower) : k
|
73
|
+
name_chain = name_chain.dup
|
74
|
+
name_chain << k unless name_chain.last == k
|
75
|
+
|
76
|
+
unless remote_resource? && serializers.nil?
|
77
|
+
payload = if serializers.is_a?(Array)
|
78
|
+
data = serializers.map { |rr|
|
79
|
+
rr.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain: name_chain)
|
80
|
+
}
|
81
|
+
graphql ? {nodes: data} : data
|
82
|
+
elsif serializers.nil?
|
83
|
+
if @resource.class.respond_to?(:sideload)
|
84
|
+
if @resource.class.sideload(k).type.to_s.include?("_many")
|
85
|
+
graphql ? {nodes: []} : []
|
86
|
+
end
|
87
|
+
end
|
88
|
+
else
|
89
|
+
serializers.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain: name_chain)
|
15
90
|
end
|
16
|
-
|
17
|
-
|
18
|
-
else
|
19
|
-
serializers.to_hash(fields: fields, include: include[k])
|
91
|
+
|
92
|
+
attrs[name.to_sym] = payload
|
20
93
|
end
|
21
94
|
end
|
22
95
|
|
23
|
-
|
96
|
+
if !graphql || (fields_list || []).include?(:id)
|
97
|
+
hash[:id] = jsonapi_id
|
98
|
+
end
|
99
|
+
|
100
|
+
if (fields_list || []).include?(:_type)
|
101
|
+
hash[:_type] = jsonapi_type.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
if (fields_list || []).include?(:_cursor)
|
105
|
+
hash[:_cursor] = cursor
|
106
|
+
end
|
107
|
+
|
108
|
+
if (fields_list || []).include?(:__typename)
|
109
|
+
resource_class = @resource.class
|
110
|
+
if polymorphic_subclass?
|
111
|
+
resource_class = @resource.class.resource_for_type(jsonapi_type)
|
112
|
+
end
|
113
|
+
hash[:__typename] = ::Graphiti::Util::Class
|
114
|
+
.graphql_type_name(resource_class.name)
|
115
|
+
end
|
116
|
+
|
24
117
|
hash.merge!(attrs) if attrs.any?
|
25
118
|
end
|
26
119
|
end
|
120
|
+
|
121
|
+
def polymorphic_subclass?
|
122
|
+
!remote_resource? &&
|
123
|
+
@resource.polymorphic? &&
|
124
|
+
@resource.type != jsonapi_type
|
125
|
+
end
|
126
|
+
|
127
|
+
# See hack in util/remote_serializer.rb
|
128
|
+
def remote_resource?
|
129
|
+
@resource == 1
|
130
|
+
end
|
27
131
|
end
|
28
132
|
|
29
133
|
class HashRenderer
|
30
|
-
def initialize(resource)
|
134
|
+
def initialize(resource, graphql: false)
|
31
135
|
@resource = resource
|
136
|
+
@graphql = graphql
|
32
137
|
end
|
33
138
|
|
34
139
|
def render(options)
|
35
140
|
serializers = options[:data]
|
36
141
|
opts = options.slice(:fields, :include)
|
37
|
-
|
38
|
-
|
142
|
+
opts[:graphql] = @graphql
|
143
|
+
top_level_key = get_top_level_key(@resource, serializers.is_a?(Array))
|
144
|
+
|
145
|
+
hash = {top_level_key => {}}
|
146
|
+
nodes = get_nodes(serializers, opts)
|
147
|
+
add_nodes(hash, top_level_key, options, nodes, @graphql)
|
148
|
+
add_stats(hash, top_level_key, options, @graphql)
|
149
|
+
if @graphql
|
150
|
+
add_page_info(hash, serializers, top_level_key, options)
|
39
151
|
end
|
152
|
+
|
153
|
+
hash
|
40
154
|
end
|
41
155
|
|
42
156
|
private
|
43
157
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
158
|
+
def get_top_level_key(resource, is_many)
|
159
|
+
key = :data
|
160
|
+
|
161
|
+
if @graphql
|
162
|
+
key = @resource.graphql_entrypoint
|
163
|
+
key = key.to_s.singularize.to_sym unless is_many
|
164
|
+
end
|
165
|
+
|
166
|
+
key
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_nodes(serializers, opts)
|
170
|
+
if serializers.is_a?(Array)
|
171
|
+
serializers.each_with_index.map do |s, index|
|
172
|
+
s.to_hash(**opts)
|
173
|
+
end
|
174
|
+
else
|
175
|
+
serializers.to_hash(**opts)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def add_nodes(hash, top_level_key, opts, nodes, graphql)
|
180
|
+
payload = nodes
|
181
|
+
if graphql && nodes.is_a?(Array)
|
182
|
+
payload = {nodes: nodes}
|
183
|
+
end
|
184
|
+
|
185
|
+
# Don't render nodes if we only requested stats
|
186
|
+
unless graphql && opts[:fields].values == [[:stats]]
|
187
|
+
hash[top_level_key] = payload
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def add_stats(hash, top_level_key, options, graphql)
|
192
|
+
if options[:meta] && !options[:meta].empty?
|
193
|
+
if @graphql
|
194
|
+
if (stats = options[:meta][:stats])
|
195
|
+
camelized = {}
|
196
|
+
stats.each_pair do |key, value|
|
197
|
+
camelized[key.to_s.camelize(:lower).to_sym] = value
|
198
|
+
end
|
199
|
+
hash[top_level_key][:stats] = camelized
|
49
200
|
end
|
50
201
|
else
|
51
|
-
|
202
|
+
hash.merge!(options.slice(:meta))
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# NB - this is only for top-level right now
|
208
|
+
# The casing here is GQL-specific, we can update later if needed.
|
209
|
+
def add_page_info(hash, serializers, top_level_key, options)
|
210
|
+
if (fields = options[:fields].try(:[], :page_info))
|
211
|
+
info = {}
|
212
|
+
|
213
|
+
if fields.include?(:has_next_page)
|
214
|
+
info[:hasNextPage] = options[:proxy].pagination.has_next_page?
|
215
|
+
end
|
216
|
+
|
217
|
+
if fields.include?(:has_previous_page)
|
218
|
+
info[:hasPreviousPage] = options[:proxy].pagination.has_previous_page?
|
219
|
+
end
|
220
|
+
|
221
|
+
if fields.include?(:start_cursor)
|
222
|
+
info[:startCursor] = serializers.first.try(:cursor)
|
223
|
+
end
|
224
|
+
|
225
|
+
if fields.include?(:end_cursor)
|
226
|
+
info[:endCursor] = serializers.last.try(:cursor)
|
52
227
|
end
|
228
|
+
|
229
|
+
hash[top_level_key][:pageInfo] = info
|
53
230
|
end
|
54
231
|
end
|
55
232
|
end
|