File: connections.rb

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (135 lines) | stat: -rw-r--r-- 4,183 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# frozen_string_literal: true

module GraphQL
  module Pagination
    # A schema-level connection wrapper manager.
    #
    # Attach as a plugin.
    #
    # @example Adding a custom wrapper
    #   class MySchema < GraphQL::Schema
    #     connections.add(MyApp::SearchResults, MyApp::SearchResultsConnection)
    #   end
    #
    # @example Removing default connection support for arrays (they can still be manually wrapped)
    #   class MySchema < GraphQL::Schema
    #     connections.delete(Array)
    #   end
    #
    # @see {Schema.connections}
    class Connections
      class ImplementationMissingError < GraphQL::Error
      end

      def initialize(schema:)
        @schema = schema
        @wrappers = {}
        add_default
      end

      def add(nodes_class, implementation)
        @wrappers[nodes_class] = implementation
      end

      def delete(nodes_class)
        @wrappers.delete(nodes_class)
      end

      def all_wrappers
        all_wrappers = {}
        @schema.ancestors.reverse_each do |schema_class|
          if schema_class.respond_to?(:connections) && (c = schema_class.connections)
            all_wrappers.merge!(c.wrappers)
          end
        end
        all_wrappers
      end

      def wrapper_for(items, wrappers: all_wrappers)
        impl = nil

        items.class.ancestors.each { |cls|
          impl = wrappers[cls]
          break if impl
        }

        impl
      end

      # Used by the runtime to wrap values in connection wrappers.
      # @api Private
      def wrap(field, parent, items, arguments, context)
        return items if GraphQL::Execution::Interpreter::RawValue === items
        wrappers = context ? context.namespace(:connections)[:all_wrappers] : all_wrappers
        impl = wrapper_for(items, wrappers: wrappers)

        if impl
          impl.new(
            items,
            context: context,
            parent: parent,
            field: field,
            max_page_size: field.has_max_page_size? ? field.max_page_size : context.schema.default_max_page_size,
            default_page_size: field.has_default_page_size? ? field.default_page_size : context.schema.default_page_size,
            first: arguments[:first],
            after: arguments[:after],
            last: arguments[:last],
            before: arguments[:before],
            arguments: arguments,
            edge_class: edge_class_for_field(field),
          )
        else
          raise ImplementationMissingError, "Couldn't find a connection wrapper for #{items.class} during #{field.path} (#{items.inspect})"
        end
      end

      # use an override if there is one
      # @api private
      def edge_class_for_field(field)
        conn_type = field.type.unwrap
        conn_type_edge_type = conn_type.respond_to?(:edge_class) && conn_type.edge_class
        if conn_type_edge_type && conn_type_edge_type != Pagination::Connection::Edge
          conn_type_edge_type
        else
          nil
        end
      end
      protected

      attr_reader :wrappers

      private

      def add_default
        add(Array, Pagination::ArrayConnection)

        if defined?(ActiveRecord::Relation)
          add(ActiveRecord::Relation, Pagination::ActiveRecordRelationConnection)
        end

        if defined?(Sequel::Dataset)
          add(Sequel::Dataset, Pagination::SequelDatasetConnection)
        end

        if defined?(Mongoid::Criteria)
          add(Mongoid::Criteria, Pagination::MongoidRelationConnection)
        end

        # Mongoid 5 and 6
        if defined?(Mongoid::Relations::Targets::Enumerable)
          add(Mongoid::Relations::Targets::Enumerable, Pagination::MongoidRelationConnection)
        end

        # Mongoid 7
        if defined?(Mongoid::Association::Referenced::HasMany::Targets::Enumerable)
          add(Mongoid::Association::Referenced::HasMany::Targets::Enumerable, Pagination::MongoidRelationConnection)
        end

        # Mongoid 7.3+
        if defined?(Mongoid::Association::Referenced::HasMany::Enumerable)
          add(Mongoid::Association::Referenced::HasMany::Enumerable, Pagination::MongoidRelationConnection)
        end
      end
    end
  end
end