Class: ElasticGraph::GraphQL::DecodedCursor

Inherits:
Object
  • Object
show all
Defined in:
lib/elastic_graph/graphql/decoded_cursor.rb

Overview

Provides the in-memory representation of a cursor after it has been decoded, as a simple hash of sort values.

The datastore’s ‘search_after` pagination uses an array of values (which represent values of the fields you are sorting by). A cursor returned when we applied one sort is generally not valid when we apply a completely different sort. To ensure we can detect this, the encoder encodes a hash of sort fields and values, ensuring each value in the cursor is properly labeled with what field it came from. This allows us to detect situations where the client uses a cursor with a completely different sort applied, while allowing some minor variation in the sort. The following are still allowed:

- Changing the direction of the sort (from `asc` to `desc` or vice-versa)
- Re-ordering the sort (e.g. changing from `[amount_money_DESC, created_at_ASC]`
  to `[created_at_ASC, amount_money_DESC]`
- Removing fields from the sort (e.g. changing from `[amount_money_DESC, created_at_ASC]`
  to `[amount_money_DESC]`) -- but adding fields is not allowed

While we don’t necessarily recommend clients change these things between pagination requests (the behavior may be surprising to the user), there is no ambiguity in how to support them, and we do not feel like it makes sense to restrict it at this point.

Defined Under Namespace

Classes: Factory

Constant Summary collapse

SINGLETON =

A special cursor instance for when we need a cursor but have only a static collection of a single element without any sort of key we can encode.

new({}).tap do |sc|
  # Ensure the special string value is returned even though our `sort_values` are empty.
  def sc.encode
    SINGLETON_CURSOR
  end
end

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.decode!(string) ⇒ Object

Tries to decode the given string cursor, raising an ‘InvalidCursorError` if it’s invalid.



47
48
49
50
51
52
53
# File 'lib/elastic_graph/graphql/decoded_cursor.rb', line 47

def self.decode!(string)
  return SINGLETON if string == SINGLETON_CURSOR
  json = ::Base64.urlsafe_decode64(string)
  new(::JSON.parse(json))
rescue ::ArgumentError, ::JSON::ParserError
  raise InvalidCursorError, "`#{string}` is an invalid cursor."
end

.try_decode(string) ⇒ Object

Tries to decode the given string cursor, returning ‘nil` if it is invalid.



40
41
42
43
44
# File 'lib/elastic_graph/graphql/decoded_cursor.rb', line 40

def self.try_decode(string)
  decode!(string)
rescue InvalidCursorError
  nil
end

Instance Method Details

#encodeObject

Encodes the cursor to a string using JSON and Base64 encoding.



56
57
58
59
60
61
# File 'lib/elastic_graph/graphql/decoded_cursor.rb', line 56

def encode
  @encode ||= begin
    json = ::JSON.fast_generate(sort_values)
    ::Base64.urlsafe_encode64(json, padding: false)
  end
end