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
         
     |