Class: GraphQL::Pagination::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/pagination/connection.rb

Overview

A Connection wraps a list of items and provides cursor-based pagination over it.

Connections were introduced by Facebook’s Relay front-end framework, but proved to be generally useful for GraphQL APIs. When in doubt, use connections to serve lists (like Arrays, ActiveRecord::Relations) via GraphQL.

Unlike the previous connection implementation, these default to bidirectional pagination.

Pagination arguments and context may be provided at initialization or assigned later (see Schema::Field::ConnectionExtension).

Direct Known Subclasses

ArrayConnection, RelationConnection

Defined Under Namespace

Classes: Edge, PaginationImplementationMissingError

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(items, parent: nil, field: nil, context: nil, first: nil, after: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, last: nil, before: nil, edge_class: nil, arguments: nil) ⇒ Connection

Returns a new instance of Connection.

Parameters:

  • items (Object)

    some unpaginated collection item, like an Array or ActiveRecord::Relation

  • context (Query::Context) (defaults to: nil)
  • parent (Object) (defaults to: nil)

    The object this collection belongs to

  • first (Integer, nil) (defaults to: nil)

    The limit parameter from the client, if it provided one

  • after (String, nil) (defaults to: nil)

    A cursor for pagination, if the client provided one

  • last (Integer, nil) (defaults to: nil)

    Limit parameter from the client, if provided

  • before (String, nil) (defaults to: nil)

    A cursor for pagination, if the client provided one.

  • arguments (Hash) (defaults to: nil)

    The arguments to the field that returned the collection wrapped by this connection

  • max_page_size (Integer, nil) (defaults to: NOT_CONFIGURED)

    A configured value to cap the result size. Applied as first if neither first or last are given and no default_page_size is set.

  • default_page_size (Integer, nil) (defaults to: NOT_CONFIGURED)

    A configured value to determine the result size when neither first or last are given.



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
# File 'lib/graphql/pagination/connection.rb', line 69

def initialize(items, parent: nil, field: nil, context: nil, first: nil, after: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, last: nil, before: nil, edge_class: nil, arguments: nil)
  @items = items
  @parent = parent
  @context = context
  @field = field
  @first_value = first
  @after_value = after
  @last_value = last
  @before_value = before
  @arguments = arguments
  @edge_class = edge_class || self.class::Edge
  # This is only true if the object was _initialized_ with an override
  # or if one is assigned later.
  @has_max_page_size_override = max_page_size != NOT_CONFIGURED
  @max_page_size = if max_page_size == NOT_CONFIGURED
    nil
  else
    max_page_size
  end
  @has_default_page_size_override = default_page_size != NOT_CONFIGURED
  @default_page_size = if default_page_size == NOT_CONFIGURED
    nil
  else
    default_page_size
  end
  @was_authorized_by_scope_items = detect_was_authorized_by_scope_items
end

Instance Attribute Details

#after_valueObject

Raw access to client-provided values. (max_page_size not applied to first or last.)



36
37
38
# File 'lib/graphql/pagination/connection.rb', line 36

def after_value
  @after_value
end

#argumentsHash<Symbol => Object>

Returns The field arguments from the field that returned this connection.

Returns:

  • (Hash<Symbol => Object>)

    The field arguments from the field that returned this connection



57
58
59
# File 'lib/graphql/pagination/connection.rb', line 57

def arguments
  @arguments
end

#before_valueObject

Raw access to client-provided values. (max_page_size not applied to first or last.)



36
37
38
# File 'lib/graphql/pagination/connection.rb', line 36

def before_value
  @before_value
end

#contextGraphQL::Query::Context



22
23
24
# File 'lib/graphql/pagination/connection.rb', line 22

def context
  @context
end

#edge_classClass

Returns A wrapper class for edges of this connection.

Returns:

  • (Class)

    A wrapper class for edges of this connection



176
177
178
# File 'lib/graphql/pagination/connection.rb', line 176

def edge_class
  @edge_class
end

#fieldGraphQL::Schema::Field

Returns The field this connection was returned by.

Returns:



179
180
181
# File 'lib/graphql/pagination/connection.rb', line 179

def field
  @field
end

#firstInteger?

Returns A clamped first value. (The underlying instance variable doesn’t have limits on it.) If neither first nor last is given, but default_page_size is present, default_page_size is used for first. If default_page_size is greater than max_page_size, it'll be clamped down to max_page_size. If default_page_size is nil, use max_page_size`.

Returns:

  • (Integer, nil)

    A clamped first value. (The underlying instance variable doesn’t have limits on it.) If neither first nor last is given, but default_page_size is present, default_page_size is used for first. If default_page_size is greater than max_page_size, it'll be clamped down to max_page_size. If default_page_size is nil, use max_page_size`.



145
146
147
148
149
150
151
152
153
# File 'lib/graphql/pagination/connection.rb', line 145

def first
  @first ||= begin
    capped = limit_pagination_argument(@first_value, max_page_size)
    if capped.nil? && last.nil?
      capped = limit_pagination_argument(default_page_size, max_page_size) || max_page_size
    end
    capped
  end
end

#first_valueObject

Raw access to client-provided values. (max_page_size not applied to first or last.)



36
37
38
# File 'lib/graphql/pagination/connection.rb', line 36

def first_value
  @first_value
end

#itemsObject (readonly)

Returns A list object, from the application. This is the unpaginated value passed into the connection.

Returns:

  • (Object)

    A list object, from the application. This is the unpaginated value passed into the connection.



19
20
21
# File 'lib/graphql/pagination/connection.rb', line 19

def items
  @items
end

#lastInteger?

Returns A clamped last value. (The underlying instance variable doesn’t have limits on it).

Returns:

  • (Integer, nil)

    A clamped last value. (The underlying instance variable doesn’t have limits on it)



166
167
168
# File 'lib/graphql/pagination/connection.rb', line 166

def last
  @last ||= limit_pagination_argument(@last_value, max_page_size)
end

#last_valueObject

Raw access to client-provided values. (max_page_size not applied to first or last.)



36
37
38
# File 'lib/graphql/pagination/connection.rb', line 36

def last_value
  @last_value
end

#parentObject

Returns the object this collection belongs to.

Returns:

  • (Object)

    the object this collection belongs to



33
34
35
# File 'lib/graphql/pagination/connection.rb', line 33

def parent
  @parent
end

#was_authorized_by_scope_items=(value) ⇒ Object (writeonly)

Sets the attribute was_authorized_by_scope_items

Parameters:

  • value

    the value to set the attribute was_authorized_by_scope_items to.



97
98
99
# File 'lib/graphql/pagination/connection.rb', line 97

def was_authorized_by_scope_items=(value)
  @was_authorized_by_scope_items = value
end

Instance Method Details

#afterString?

Returns the client-provided cursor. "" is treated as nil.

Returns:

  • (String, nil)

    the client-provided cursor. "" is treated as nil.



48
49
50
51
52
53
54
# File 'lib/graphql/pagination/connection.rb', line 48

def after
  if defined?(@after)
    @after
  else
    @after = @after_value == "" ? nil : @after_value
  end
end

#beforeString?

Returns the client-provided cursor. "" is treated as nil.

Returns:

  • (String, nil)

    the client-provided cursor. "" is treated as nil.



39
40
41
42
43
44
45
# File 'lib/graphql/pagination/connection.rb', line 39

def before
  if defined?(@before)
    @before
  else
    @before = @before_value == "" ? nil : @before_value
  end
end

#cursor_for(item) ⇒ String

Return a cursor for this item.

Parameters:

  • item (Object)

    one of the passed in #items, taken from #nodes

Returns:

  • (String)

Raises:



220
221
222
# File 'lib/graphql/pagination/connection.rb', line 220

def cursor_for(item)
  raise PaginationImplementationMissingError, "Implement #{self.class}#cursor_for(item) to return the cursor for #{item.inspect}"
end

#default_page_sizeObject



125
126
127
128
129
130
131
# File 'lib/graphql/pagination/connection.rb', line 125

def default_page_size
  if @has_default_page_size_override
    @default_page_size
  else
    context.schema.default_page_size
  end
end

#default_page_size=(new_value) ⇒ Object



120
121
122
123
# File 'lib/graphql/pagination/connection.rb', line 120

def default_page_size=(new_value)
  @has_default_page_size_override = true
  @default_page_size = new_value
end

#edge_nodesObject

Deprecated.

use #nodes instead

A dynamic alias for compatibility with Relay::BaseConnection.



188
189
190
# File 'lib/graphql/pagination/connection.rb', line 188

def edge_nodes
  nodes
end

#edgesArray<Edge>

Returns #nodes, but wrapped with Edge instances.

Returns:

  • (Array<Edge>)

    #nodes, but wrapped with Edge instances



171
172
173
# File 'lib/graphql/pagination/connection.rb', line 171

def edges
  @edges ||= nodes.map { |n| @edge_class.new(n, self) }
end

#end_cursorString

Returns The cursor of the last item in #nodes.

Returns:

  • (String)

    The cursor of the last item in #nodes



213
214
215
# File 'lib/graphql/pagination/connection.rb', line 213

def end_cursor
  nodes.last && cursor_for(nodes.last)
end

#has_default_page_size_override?Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/graphql/pagination/connection.rb', line 133

def has_default_page_size_override?
  @has_default_page_size_override
end

#has_max_page_size_override?Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/graphql/pagination/connection.rb', line 116

def has_max_page_size_override?
  @has_max_page_size_override
end

#has_next_pageBoolean

Returns True if there are more items after this page.

Returns:

  • (Boolean)

    True if there are more items after this page

Raises:



198
199
200
# File 'lib/graphql/pagination/connection.rb', line 198

def has_next_page
  raise PaginationImplementationMissingError, "Implement #{self.class}#has_next_page to return the next-page check"
end

#has_previous_pageBoolean

Returns True if there were items before these items.

Returns:

  • (Boolean)

    True if there were items before these items

Raises:



203
204
205
# File 'lib/graphql/pagination/connection.rb', line 203

def has_previous_page
  raise PaginationImplementationMissingError, "Implement #{self.class}#has_previous_page to return the previous-page check"
end

#max_page_sizeObject



108
109
110
111
112
113
114
# File 'lib/graphql/pagination/connection.rb', line 108

def max_page_size
  if @has_max_page_size_override
    @max_page_size
  else
    context.schema.default_max_page_size
  end
end

#max_page_size=(new_value) ⇒ Object



103
104
105
106
# File 'lib/graphql/pagination/connection.rb', line 103

def max_page_size=(new_value)
  @has_max_page_size_override = true
  @max_page_size = new_value
end

#nodesArray<Object>

Returns A slice of #items, constrained by @first_value/@after_value/@last_value/@before_value.

Returns:

  • (Array<Object>)

    A slice of #items, constrained by @first_value/@after_value/@last_value/@before_value

Raises:



182
183
184
# File 'lib/graphql/pagination/connection.rb', line 182

def nodes
  raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`"
end

#page_infoObject

The connection object itself implements PageInfo fields



193
194
195
# File 'lib/graphql/pagination/connection.rb', line 193

def page_info
  self
end

#range_add_edge(item) ⇒ Edge

This is called by Relay::RangeAdd – it can be overridden when item needs some modifications based on this connection’s state.

Parameters:

  • item (Object)

    An item newly added to items

Returns:



160
161
162
# File 'lib/graphql/pagination/connection.rb', line 160

def range_add_edge(item)
  edge_class.new(item, self)
end

#start_cursorString

Returns The cursor of the first item in #nodes.

Returns:

  • (String)

    The cursor of the first item in #nodes



208
209
210
# File 'lib/graphql/pagination/connection.rb', line 208

def start_cursor
  nodes.first && cursor_for(nodes.first)
end

#was_authorized_by_scope_items?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/graphql/pagination/connection.rb', line 99

def was_authorized_by_scope_items?
  @was_authorized_by_scope_items
end