graphql 1.5.15 → 1.6.0
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/lib/graphql.rb +4 -19
- data/lib/graphql/analysis/analyze_query.rb +27 -2
- data/lib/graphql/analysis/query_complexity.rb +10 -11
- data/lib/graphql/argument.rb +7 -6
- data/lib/graphql/backwards_compatibility.rb +47 -0
- data/lib/graphql/compatibility/execution_specification.rb +14 -0
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +6 -1
- data/lib/graphql/compatibility/lazy_execution_specification.rb +19 -0
- data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +15 -6
- data/lib/graphql/directive.rb +1 -6
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution/execute.rb +174 -160
- data/lib/graphql/execution/field_result.rb +5 -1
- data/lib/graphql/execution/lazy.rb +2 -2
- data/lib/graphql/execution/lazy/resolve.rb +8 -11
- data/lib/graphql/execution/multiplex.rb +134 -0
- data/lib/graphql/execution/selection_result.rb +5 -0
- data/lib/graphql/field.rb +1 -8
- data/lib/graphql/filter.rb +53 -0
- data/lib/graphql/internal_representation/node.rb +11 -6
- data/lib/graphql/internal_representation/rewrite.rb +3 -3
- data/lib/graphql/query.rb +160 -78
- data/lib/graphql/query/arguments.rb +14 -25
- data/lib/graphql/query/arguments_cache.rb +6 -13
- data/lib/graphql/query/context.rb +28 -10
- data/lib/graphql/query/executor.rb +1 -0
- data/lib/graphql/query/literal_input.rb +10 -4
- data/lib/graphql/query/null_context.rb +1 -1
- data/lib/graphql/query/serial_execution/field_resolution.rb +5 -1
- data/lib/graphql/query/validation_pipeline.rb +12 -7
- data/lib/graphql/query/variables.rb +1 -1
- data/lib/graphql/rake_task.rb +140 -0
- data/lib/graphql/relay/array_connection.rb +29 -48
- data/lib/graphql/relay/base_connection.rb +9 -7
- data/lib/graphql/relay/mutation.rb +0 -11
- data/lib/graphql/relay/mutation/instrumentation.rb +2 -2
- data/lib/graphql/relay/mutation/resolve.rb +7 -10
- data/lib/graphql/relay/relation_connection.rb +98 -61
- data/lib/graphql/scalar_type.rb +1 -15
- data/lib/graphql/schema.rb +90 -25
- data/lib/graphql/schema/build_from_definition.rb +22 -23
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +70 -0
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +47 -0
- data/lib/graphql/schema/middleware_chain.rb +1 -1
- data/lib/graphql/schema/printer.rb +2 -1
- data/lib/graphql/schema/timeout_middleware.rb +6 -6
- data/lib/graphql/schema/type_map.rb +1 -1
- data/lib/graphql/schema/warden.rb +5 -9
- data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/analysis/analyze_query_spec.rb +2 -2
- data/spec/graphql/analysis/max_query_complexity_spec.rb +28 -0
- data/spec/graphql/argument_spec.rb +3 -3
- data/spec/graphql/execution/lazy_spec.rb +8 -114
- data/spec/graphql/execution/multiplex_spec.rb +131 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +10 -0
- data/spec/graphql/query/arguments_spec.rb +14 -16
- data/spec/graphql/query/context_spec.rb +14 -1
- data/spec/graphql/query/literal_input_spec.rb +19 -13
- data/spec/graphql/query/variables_spec.rb +1 -1
- data/spec/graphql/query_spec.rb +12 -1
- data/spec/graphql/rake_task_spec.rb +57 -0
- data/spec/graphql/relay/array_connection_spec.rb +24 -3
- data/spec/graphql/relay/connection_instrumentation_spec.rb +23 -0
- data/spec/graphql/relay/mutation_spec.rb +2 -10
- data/spec/graphql/relay/page_info_spec.rb +2 -2
- data/spec/graphql/relay/relation_connection_spec.rb +167 -3
- data/spec/graphql/schema/build_from_definition_spec.rb +93 -19
- data/spec/graphql/schema/warden_spec.rb +80 -0
- data/spec/graphql/schema_spec.rb +26 -2
- data/spec/spec_helper.rb +4 -2
- data/spec/support/lazy_helpers.rb +152 -0
- data/spec/support/star_wars/schema.rb +23 -0
- metadata +28 -3
- data/lib/graphql/schema/mask.rb +0 -55
@@ -5,10 +5,9 @@ module GraphQL
|
|
5
5
|
#
|
6
6
|
# {Arguments} recursively wraps the input in {Arguments} instances.
|
7
7
|
class Arguments
|
8
|
-
extend
|
8
|
+
extend Forwardable
|
9
9
|
|
10
10
|
def initialize(values, argument_definitions:)
|
11
|
-
@original_values = values
|
12
11
|
@argument_values = values.inject({}) do |memo, (inner_key, inner_value)|
|
13
12
|
arg_defn = argument_definitions[inner_key.to_s]
|
14
13
|
|
@@ -22,24 +21,29 @@ module GraphQL
|
|
22
21
|
# @param key [String, Symbol] name or index of value to access
|
23
22
|
# @return [Object] the argument at that key
|
24
23
|
def [](key)
|
25
|
-
|
26
|
-
@argument_values.fetch(key_s, NULL_ARGUMENT_VALUE).value
|
24
|
+
@argument_values.fetch(key.to_s, NULL_ARGUMENT_VALUE).value
|
27
25
|
end
|
28
26
|
|
29
27
|
# @param key [String, Symbol] name of value to access
|
30
28
|
# @return [Boolean] true if the argument was present in this field
|
31
29
|
def key?(key)
|
32
|
-
|
33
|
-
@argument_values.key?(key_s)
|
30
|
+
@argument_values.key?(key.to_s)
|
34
31
|
end
|
35
32
|
|
36
|
-
# Get the
|
37
|
-
# @return [Hash] the
|
33
|
+
# Get the hash of all values, with stringified keys
|
34
|
+
# @return [Hash] the stringified hash
|
38
35
|
def to_h
|
39
|
-
@
|
36
|
+
@to_h ||= begin
|
37
|
+
h = {}
|
38
|
+
each_value do |arg_value|
|
39
|
+
arg_key = arg_value.definition.expose_as
|
40
|
+
h[arg_key] = unwrap_value(arg_value.value)
|
41
|
+
end
|
42
|
+
h
|
43
|
+
end
|
40
44
|
end
|
41
45
|
|
42
|
-
def_delegators :
|
46
|
+
def_delegators :to_h, :keys, :values, :each
|
43
47
|
|
44
48
|
# Access each key, value and type for the arguments in this set.
|
45
49
|
# @yield [argument_value] The {ArgumentValue} for each argument
|
@@ -101,21 +105,6 @@ module GraphQL
|
|
101
105
|
value
|
102
106
|
end
|
103
107
|
end
|
104
|
-
|
105
|
-
def string_key_values
|
106
|
-
@string_key_values ||= stringify_keys(to_h)
|
107
|
-
end
|
108
|
-
|
109
|
-
def stringify_keys(value)
|
110
|
-
case value
|
111
|
-
when Hash
|
112
|
-
value.inject({}) { |memo, (k, v)| memo[k.to_s] = stringify_keys(v); memo }
|
113
|
-
when Array
|
114
|
-
value.map { |v| stringify_keys(v) }
|
115
|
-
else
|
116
|
-
value
|
117
|
-
end
|
118
|
-
end
|
119
108
|
end
|
120
109
|
end
|
121
110
|
end
|
@@ -5,21 +5,14 @@ module GraphQL
|
|
5
5
|
# @return [Hash<InternalRepresentation::Node, GraphQL::Language::NodesDirectiveNode => Hash<GraphQL::Field, GraphQL::Directive => GraphQL::Query::Arguments>>]
|
6
6
|
def self.build(query)
|
7
7
|
Hash.new do |h1, irep_or_ast_node|
|
8
|
-
|
8
|
+
Hash.new do |h2, definition|
|
9
9
|
ast_node = irep_or_ast_node.is_a?(GraphQL::InternalRepresentation::Node) ? irep_or_ast_node.ast_node : irep_or_ast_node
|
10
10
|
ast_arguments = ast_node.arguments
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
else
|
17
|
-
GraphQL::Query::LiteralInput.from_arguments(
|
18
|
-
ast_arguments,
|
19
|
-
definition.arguments,
|
20
|
-
query.variables,
|
21
|
-
)
|
22
|
-
end
|
11
|
+
GraphQL::Query::LiteralInput.from_arguments(
|
12
|
+
ast_arguments,
|
13
|
+
definition.arguments,
|
14
|
+
query.variables,
|
15
|
+
)
|
23
16
|
end
|
24
17
|
end
|
25
18
|
end
|
@@ -4,6 +4,7 @@ module GraphQL
|
|
4
4
|
# Expose some query-specific info to field resolve functions.
|
5
5
|
# It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`.
|
6
6
|
class Context
|
7
|
+
extend Forwardable
|
7
8
|
attr_reader :execution_strategy
|
8
9
|
# `strategy` is required by GraphQL::Batch
|
9
10
|
alias_method :strategy, :execution_strategy
|
@@ -41,24 +42,32 @@ module GraphQL
|
|
41
42
|
def initialize(query:, values:)
|
42
43
|
@query = query
|
43
44
|
@schema = query.schema
|
44
|
-
@
|
45
|
+
@provided_values = values || {}
|
46
|
+
# Namespaced storage, where user-provided values are in `nil` namespace:
|
47
|
+
@storage = Hash.new { |h, k| h[k] = {} }
|
48
|
+
@storage[nil] = @provided_values
|
45
49
|
@errors = []
|
46
50
|
@path = []
|
47
51
|
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
def_delegators :@provided_values, :[], :[]=, :to_h, :key?, :fetch
|
54
|
+
|
55
|
+
# @!method [](key)
|
56
|
+
# Lookup `key` from the hash passed to {Schema#execute} as `context:`
|
57
|
+
|
58
|
+
# @!method []=(key, value)
|
59
|
+
# Reassign `key` to the hash passed to {Schema#execute} as `context:`
|
53
60
|
|
54
61
|
# @return [GraphQL::Schema::Warden]
|
55
62
|
def warden
|
56
63
|
@warden ||= @query.warden
|
57
64
|
end
|
58
65
|
|
59
|
-
#
|
60
|
-
|
61
|
-
|
66
|
+
# Get an isolated hash for `ns`. Doesn't affect user-provided storage.
|
67
|
+
# @param ns [Object] a usage-specific namespace identifier
|
68
|
+
# @return [Hash] namespaced storage
|
69
|
+
def namespace(ns)
|
70
|
+
@storage[ns]
|
62
71
|
end
|
63
72
|
|
64
73
|
def spawn(key:, selection:, parent_type:, field:)
|
@@ -71,8 +80,14 @@ module GraphQL
|
|
71
80
|
)
|
72
81
|
end
|
73
82
|
|
83
|
+
# Return this value to tell the runtime
|
84
|
+
# to exclude this field from the response altogether
|
85
|
+
def skip
|
86
|
+
GraphQL::Execution::Execute::SKIP
|
87
|
+
end
|
88
|
+
|
74
89
|
class FieldResolutionContext
|
75
|
-
extend
|
90
|
+
extend Forwardable
|
76
91
|
|
77
92
|
attr_reader :path, :selection, :field, :parent_type
|
78
93
|
|
@@ -84,7 +99,10 @@ module GraphQL
|
|
84
99
|
@parent_type = parent_type
|
85
100
|
end
|
86
101
|
|
87
|
-
def_delegators :@context,
|
102
|
+
def_delegators :@context,
|
103
|
+
:[], :[]=, :key?, :fetch, :to_h, :namespace,
|
104
|
+
:spawn, :query, :schema, :warden, :errors,
|
105
|
+
:execution_strategy, :strategy, :skip
|
88
106
|
|
89
107
|
# @return [GraphQL::Language::Nodes::Field] The AST node for the currently-executing field
|
90
108
|
def ast_node
|
@@ -32,7 +32,7 @@ module GraphQL
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.defaults_for(argument_defns)
|
35
|
-
if argument_defns.none?
|
35
|
+
if argument_defns.values.none?(&:default_value?)
|
36
36
|
GraphQL::Query::Arguments::NO_ARGS
|
37
37
|
else
|
38
38
|
from_arguments([], argument_defns, nil)
|
@@ -40,7 +40,8 @@ module GraphQL
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.from_arguments(ast_arguments, argument_defns, variables)
|
43
|
-
|
43
|
+
# Variables is nil when making .defaults_for
|
44
|
+
context = variables ? variables.context : nil
|
44
45
|
values_hash = {}
|
45
46
|
indexed_arguments = ast_arguments.each_with_object({}) { |a, memo| memo[a.name] = a }
|
46
47
|
|
@@ -56,7 +57,7 @@ module GraphQL
|
|
56
57
|
if (!value_is_a_variable || (value_is_a_variable && variables.key?(ast_arg.value.name)))
|
57
58
|
|
58
59
|
value = coerce(arg_defn.type, ast_arg.value, variables)
|
59
|
-
value = arg_defn.prepare(value)
|
60
|
+
value = arg_defn.prepare(value, context)
|
60
61
|
|
61
62
|
if value.is_a?(GraphQL::ExecutionError)
|
62
63
|
value.ast_node = ast_arg
|
@@ -72,7 +73,12 @@ module GraphQL
|
|
72
73
|
# a value wasn't provided from the AST,
|
73
74
|
# then add the default value.
|
74
75
|
if arg_defn.default_value? && !values_hash.key?(arg_name)
|
75
|
-
|
76
|
+
value = arg_defn.default_value
|
77
|
+
# `context` isn't present when pre-calculating defaults
|
78
|
+
if context
|
79
|
+
value = arg_defn.prepare(value, context)
|
80
|
+
end
|
81
|
+
values_hash[arg_name] = value
|
76
82
|
end
|
77
83
|
end
|
78
84
|
|
@@ -24,7 +24,11 @@ module GraphQL
|
|
24
24
|
def result
|
25
25
|
result_name = irep_node.name
|
26
26
|
raw_value = get_raw_value
|
27
|
-
|
27
|
+
if raw_value == GraphQL::Execution::Execute::SKIP
|
28
|
+
{}
|
29
|
+
else
|
30
|
+
{ result_name => get_finished_value(raw_value) }
|
31
|
+
end
|
28
32
|
end
|
29
33
|
|
30
34
|
# GraphQL::Batch depends on this
|
@@ -52,6 +52,11 @@ module GraphQL
|
|
52
52
|
@internal_representation
|
53
53
|
end
|
54
54
|
|
55
|
+
def analyzers
|
56
|
+
ensure_has_validated
|
57
|
+
@query_analyzers
|
58
|
+
end
|
59
|
+
|
55
60
|
private
|
56
61
|
|
57
62
|
# If the pipeline wasn't run yet, run it.
|
@@ -80,13 +85,13 @@ module GraphQL
|
|
80
85
|
end
|
81
86
|
|
82
87
|
if @validation_errors.none?
|
83
|
-
query_analyzers = build_analyzers(@schema, @max_depth, @max_complexity)
|
84
|
-
if query_analyzers.any?
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
88
|
+
@query_analyzers = build_analyzers(@schema, @max_depth, @max_complexity)
|
89
|
+
# if query_analyzers.any?
|
90
|
+
# analysis_results = GraphQL::Analysis.analyze_query(@query, query_analyzers)
|
91
|
+
# @analysis_errors = analysis_results
|
92
|
+
# .flatten # accept n-dimensional array
|
93
|
+
# .select { |r| r.is_a?(GraphQL::AnalysisError) }
|
94
|
+
# end
|
90
95
|
end
|
91
96
|
end
|
92
97
|
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
class Query
|
4
4
|
# Read-only access to query variables, applying default values if needed.
|
5
5
|
class Variables
|
6
|
-
extend
|
6
|
+
extend Forwardable
|
7
7
|
|
8
8
|
# @return [Array<GraphQL::Query::VariableValidationError>] Any errors encountered when parsing the provided variables and literal values
|
9
9
|
attr_reader :errors
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "fileutils"
|
3
|
+
module GraphQL
|
4
|
+
# A rake task for dumping a schema as IDL or JSON.
|
5
|
+
#
|
6
|
+
# By default, schemas are looked up by name as constants using `schema_name:`.
|
7
|
+
# You can provide a `load_schema` function to return your schema another way.
|
8
|
+
#
|
9
|
+
# `load_context:`, `only:` and `except:` are supported so that
|
10
|
+
# you can keep an eye on how filters affect your schema.
|
11
|
+
#
|
12
|
+
# @example Dump a Schema to .graphql + .json files
|
13
|
+
# require "graphql/rake_task"
|
14
|
+
# GraphQL::RakeTask.new(schema_name: "MySchema")
|
15
|
+
#
|
16
|
+
# # $ rake graphql:schema:dump
|
17
|
+
# # Schema IDL dumped to ./schema.graphql
|
18
|
+
# # Schema JSON dumped to ./schema.json
|
19
|
+
#
|
20
|
+
# @example Invoking the task from Ruby
|
21
|
+
# require "rake"
|
22
|
+
# Rake::Task["graphql:schema:dump"].invoke
|
23
|
+
class RakeTask
|
24
|
+
include Rake::DSL
|
25
|
+
|
26
|
+
DEFAULT_OPTIONS = {
|
27
|
+
namespace: "graphql",
|
28
|
+
dependencies: nil,
|
29
|
+
schema_name: nil,
|
30
|
+
load_schema: ->(task) { Object.const_get(task.schema_name) },
|
31
|
+
load_context: ->(task) { {} },
|
32
|
+
only: nil,
|
33
|
+
except: nil,
|
34
|
+
directory: ".",
|
35
|
+
idl_outfile: "schema.graphql",
|
36
|
+
json_outfile: "schema.json",
|
37
|
+
}
|
38
|
+
|
39
|
+
# @return [String] Namespace for generated tasks
|
40
|
+
attr_writer :namespace
|
41
|
+
|
42
|
+
def rake_namespace
|
43
|
+
@namespace
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Array<String>]
|
47
|
+
attr_accessor :dependencies
|
48
|
+
|
49
|
+
# @return [String] By default, used to find the schema as a constant.
|
50
|
+
# @see {#load_schema} for loading a schema another way
|
51
|
+
attr_accessor :schema_name
|
52
|
+
|
53
|
+
# @return [<#call(task)>] A proc for loading the target GraphQL schema
|
54
|
+
attr_accessor :load_schema
|
55
|
+
|
56
|
+
# @return [<#call(task)>] A callable for loading the query context
|
57
|
+
attr_accessor :load_context
|
58
|
+
|
59
|
+
# @return [<#call(member, ctx)>, nil] A filter for this task
|
60
|
+
attr_accessor :only
|
61
|
+
|
62
|
+
# @return [<#call(member, ctx)>, nil] A filter for this task
|
63
|
+
attr_accessor :except
|
64
|
+
|
65
|
+
# @return [String] target for IDL task
|
66
|
+
attr_accessor :idl_outfile
|
67
|
+
|
68
|
+
# @return [String] target for JSON task
|
69
|
+
attr_accessor :json_outfile
|
70
|
+
|
71
|
+
# @return [String] directory for IDL & JSON files
|
72
|
+
attr_accessor :directory
|
73
|
+
|
74
|
+
# Set the parameters of this task by passing keyword arguments
|
75
|
+
# or assigning attributes inside the block
|
76
|
+
def initialize(options = {})
|
77
|
+
default_dependencies = if Rake::Task.task_defined?("environment")
|
78
|
+
[:environment]
|
79
|
+
else
|
80
|
+
[]
|
81
|
+
end
|
82
|
+
|
83
|
+
all_options = DEFAULT_OPTIONS
|
84
|
+
.merge(dependencies: default_dependencies)
|
85
|
+
.merge(options)
|
86
|
+
all_options.each do |k, v|
|
87
|
+
self.public_send("#{k}=", v)
|
88
|
+
end
|
89
|
+
|
90
|
+
if block_given?
|
91
|
+
yield(self)
|
92
|
+
end
|
93
|
+
|
94
|
+
define_task
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Use the provided `method_name` to generate a string from the specified schema
|
100
|
+
# then write it to `file`.
|
101
|
+
def write_outfile(method_name, file)
|
102
|
+
schema = @load_schema.call(self)
|
103
|
+
context = @load_context.call(self)
|
104
|
+
result = schema.public_send(method_name, only: @only, except: @except, context: context)
|
105
|
+
dir = File.dirname(file)
|
106
|
+
FileUtils.mkdir_p(dir)
|
107
|
+
File.write(file, result)
|
108
|
+
end
|
109
|
+
|
110
|
+
def idl_path
|
111
|
+
File.join(@directory, @idl_outfile)
|
112
|
+
end
|
113
|
+
|
114
|
+
def json_path
|
115
|
+
File.join(@directory, @json_outfile)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Use the Rake DSL to add tasks
|
119
|
+
def define_task
|
120
|
+
namespace(@namespace) do
|
121
|
+
namespace("schema") do
|
122
|
+
desc("Dump the schema to IDL in #{idl_path}")
|
123
|
+
task :idl => @dependencies do
|
124
|
+
write_outfile(:to_definition, idl_path)
|
125
|
+
puts "Schema IDL dumped into #{idl_path}"
|
126
|
+
end
|
127
|
+
|
128
|
+
desc("Dump the schema to JSON in #{json_path}")
|
129
|
+
task :json => @dependencies do
|
130
|
+
write_outfile(:to_json, json_path)
|
131
|
+
puts "Schema JSON dumped into #{json_path}"
|
132
|
+
end
|
133
|
+
|
134
|
+
desc("Dump the schema to JSON and IDL")
|
135
|
+
task :dump => [:idl, :json]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -3,78 +3,59 @@ module GraphQL
|
|
3
3
|
module Relay
|
4
4
|
class ArrayConnection < BaseConnection
|
5
5
|
def cursor_from_node(item)
|
6
|
-
idx =
|
6
|
+
idx = (after ? index_from_cursor(after) : 0) + sliced_nodes.find_index(item) + 1
|
7
7
|
encode(idx.to_s)
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
private
|
11
|
+
|
12
|
+
def first
|
13
|
+
return @first if defined? @first
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
@first = get_limited_arg(:first)
|
16
|
+
@first = max_page_size if @first && max_page_size && @first > max_page_size
|
17
|
+
@first
|
16
18
|
end
|
17
19
|
|
18
|
-
|
20
|
+
def last
|
21
|
+
return @last if defined? @last
|
22
|
+
|
23
|
+
@last = get_limited_arg(:last)
|
24
|
+
@last = max_page_size if @last && max_page_size && @last > max_page_size
|
25
|
+
@last
|
26
|
+
end
|
19
27
|
|
20
28
|
# apply first / last limit results
|
21
29
|
def paged_nodes
|
22
30
|
@paged_nodes ||= begin
|
23
31
|
items = sliced_nodes
|
24
32
|
|
25
|
-
if
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
33
|
+
items = items.first(first) if first
|
34
|
+
items = items.last(last) if last
|
35
|
+
items = items.first(max_page_size) if max_page_size && !first && !last
|
36
|
+
|
37
|
+
items
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
33
41
|
# Apply cursors to edges
|
34
42
|
def sliced_nodes
|
35
|
-
@sliced_nodes ||=
|
36
|
-
|
37
|
-
|
38
|
-
def index_from_cursor(cursor)
|
39
|
-
decode(cursor).to_i
|
40
|
-
end
|
41
|
-
|
42
|
-
def starting_offset
|
43
|
-
@starting_offset = if before
|
44
|
-
[previous_offset, 0].max
|
45
|
-
elsif last
|
46
|
-
[nodes.count - last, 0].max
|
47
|
-
else
|
48
|
-
previous_offset
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def previous_offset
|
53
|
-
@previous_offset ||= if after
|
54
|
-
index_from_cursor(after)
|
43
|
+
@sliced_nodes ||= if before && after
|
44
|
+
nodes[index_from_cursor(after)..index_from_cursor(before)-1] || []
|
55
45
|
elsif before
|
56
|
-
|
57
|
-
|
46
|
+
nodes[0..index_from_cursor(before)-2] || []
|
47
|
+
elsif after
|
48
|
+
nodes[index_from_cursor(after)..-1] || []
|
58
49
|
else
|
59
|
-
|
50
|
+
nodes
|
60
51
|
end
|
61
52
|
end
|
62
53
|
|
63
|
-
def
|
64
|
-
|
65
|
-
limit_from_arguments = if first
|
66
|
-
first
|
67
|
-
else
|
68
|
-
if previous_offset < 0
|
69
|
-
previous_offset + (last ? last : 0)
|
70
|
-
else
|
71
|
-
last
|
72
|
-
end
|
73
|
-
end
|
74
|
-
[limit_from_arguments, max_page_size].compact.min
|
75
|
-
end
|
54
|
+
def index_from_cursor(cursor)
|
55
|
+
decode(cursor).to_i
|
76
56
|
end
|
77
57
|
end
|
58
|
+
|
78
59
|
BaseConnection.register_connection_implementation(Array, ArrayConnection)
|
79
60
|
end
|
80
61
|
end
|