graphql 1.5.15 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|