graphql_client 0.3.3
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 +7 -0
- data/.gitignore +5 -0
- data/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml +1133 -0
- data/.rubocop.yml +24 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +45 -0
- data/CONTRIBUTING.md +28 -0
- data/CONTRIBUTING_DEVELOPER_CERTIFICATE_OF_ORIGIN.txt +37 -0
- data/Gemfile +8 -0
- data/LICENSE.md +21 -0
- data/README.md +96 -0
- data/Rakefile +13 -0
- data/bin/graphql-client +79 -0
- data/bin/rake +17 -0
- data/circle.yml +3 -0
- data/dev.yml +7 -0
- data/graphql_ruby_client.gemspec +26 -0
- data/lib/graphql_client.rb +46 -0
- data/lib/graphql_client/adapters/http_adapter.rb +72 -0
- data/lib/graphql_client/base.rb +53 -0
- data/lib/graphql_client/config.rb +42 -0
- data/lib/graphql_client/deserialization.rb +36 -0
- data/lib/graphql_client/error.rb +22 -0
- data/lib/graphql_client/graph_connection.rb +21 -0
- data/lib/graphql_client/graph_node.rb +24 -0
- data/lib/graphql_client/graph_object.rb +56 -0
- data/lib/graphql_client/introspection_query.rb +80 -0
- data/lib/graphql_client/query/add_inline_fragment.rb +42 -0
- data/lib/graphql_client/query/argument.rb +30 -0
- data/lib/graphql_client/query/document.rb +86 -0
- data/lib/graphql_client/query/field.rb +91 -0
- data/lib/graphql_client/query/fragment.rb +41 -0
- data/lib/graphql_client/query/has_selection_set.rb +72 -0
- data/lib/graphql_client/query/inline_fragment.rb +35 -0
- data/lib/graphql_client/query/mutation_document.rb +20 -0
- data/lib/graphql_client/query/operation.rb +46 -0
- data/lib/graphql_client/query/operations/mutation_operation.rb +17 -0
- data/lib/graphql_client/query/operations/query_operation.rb +17 -0
- data/lib/graphql_client/query/query_document.rb +20 -0
- data/lib/graphql_client/query/selection_set.rb +53 -0
- data/lib/graphql_client/response.rb +21 -0
- data/lib/graphql_client/response_connection.rb +18 -0
- data/lib/graphql_client/response_object.rb +32 -0
- data/lib/graphql_client/schema_patches.rb +17 -0
- data/lib/graphql_client/version.rb +7 -0
- data/shipit.rubygems.yml +1 -0
- data/shipit.yml +4 -0
- data/test/graphql_client/adapters/http_adapter_test.rb +111 -0
- data/test/graphql_client/config_test.rb +68 -0
- data/test/graphql_client/graph_connection_test.rb +8 -0
- data/test/graphql_client/graph_node_test.rb +8 -0
- data/test/graphql_client/graph_object_query_transforming_test.rb +156 -0
- data/test/graphql_client/graph_object_test.rb +142 -0
- data/test/graphql_client/graphql_client_test.rb +41 -0
- data/test/graphql_client/http_client_test.rb +52 -0
- data/test/graphql_client/query/add_inline_fragment_test.rb +57 -0
- data/test/graphql_client/query/arguments_test.rb +89 -0
- data/test/graphql_client/query/document_test.rb +246 -0
- data/test/graphql_client/query/field_test.rb +163 -0
- data/test/graphql_client/query/fragment_test.rb +45 -0
- data/test/graphql_client/query/inline_fragment_test.rb +37 -0
- data/test/graphql_client/query/mutation_document_test.rb +30 -0
- data/test/graphql_client/query/mutation_operation_test.rb +89 -0
- data/test/graphql_client/query/query_document_test.rb +30 -0
- data/test/graphql_client/query/query_operation_test.rb +262 -0
- data/test/graphql_client/query/selection_set_test.rb +116 -0
- data/test/graphql_client/response_connection_test.rb +50 -0
- data/test/graphql_client/response_object_test.rb +109 -0
- data/test/graphql_client/response_test.rb +67 -0
- data/test/support/fixtures/schema.json +13710 -0
- data/test/test_helper.rb +37 -0
- metadata +227 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
module Query
|
6
|
+
class QueryOperation < Operation
|
7
|
+
def operation_type
|
8
|
+
'query'
|
9
|
+
end
|
10
|
+
|
11
|
+
def resolver_type
|
12
|
+
schema.type(schema.query_root_name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
module Query
|
6
|
+
class QueryDocument
|
7
|
+
def self.new(schema, name = nil)
|
8
|
+
document = Document.new(schema)
|
9
|
+
query = document.add_query(name)
|
10
|
+
|
11
|
+
if block_given?
|
12
|
+
yield query
|
13
|
+
end
|
14
|
+
|
15
|
+
query
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
module Query
|
6
|
+
class SelectionSet
|
7
|
+
attr_reader :fields, :fragments, :fields, :inline_fragments
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@fragments = {}
|
11
|
+
@fields = {}
|
12
|
+
@inline_fragments = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_field(field)
|
16
|
+
@fields[field.name] = field
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_fragment(fragment)
|
20
|
+
@fragments[fragment.name] = fragment
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_inline_fragment(inline_fragment)
|
24
|
+
@inline_fragments << inline_fragment
|
25
|
+
end
|
26
|
+
|
27
|
+
def contains?(field_name)
|
28
|
+
fields.key?(field_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def empty?
|
32
|
+
selections.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def lookup(name)
|
36
|
+
fields.fetch(name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def selections
|
40
|
+
fields.values + fragments.values + inline_fragments
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_query(indent = '')
|
44
|
+
selections.map do |selection|
|
45
|
+
selection.to_query(indent: indent + ' ')
|
46
|
+
end.join("\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
alias_method :to_s, :to_query
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
class Response
|
6
|
+
attr_reader :body, :data, :errors, :extensions
|
7
|
+
|
8
|
+
def initialize(response_body)
|
9
|
+
response = JSON.parse(response_body)
|
10
|
+
data, errors, extensions = response.values_at('data', 'errors', 'extensions')
|
11
|
+
|
12
|
+
raise ResponseError, errors if !data && errors
|
13
|
+
|
14
|
+
@body = response
|
15
|
+
@data = data
|
16
|
+
@errors = errors.to_a
|
17
|
+
@extensions = extensions.to_a
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
class ResponseConnection < ResponseObject
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
def_delegator :page_info, :has_next_page, :has_next_page?
|
10
|
+
def_delegator :page_info, :has_previous_page, :has_previous_page?
|
11
|
+
|
12
|
+
def each
|
13
|
+
return enum_for(:each) unless block_given?
|
14
|
+
edges.each { |edge| yield edge.node }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
class ResponseObject
|
6
|
+
include Deserialization
|
7
|
+
|
8
|
+
attr_reader :data
|
9
|
+
|
10
|
+
def initialize(data)
|
11
|
+
@data = Hash(data)
|
12
|
+
|
13
|
+
@data.each do |field_name, value|
|
14
|
+
response_object = case value
|
15
|
+
when Hash
|
16
|
+
if value.key?('edges')
|
17
|
+
ResponseConnection.new(value)
|
18
|
+
else
|
19
|
+
self.class.new(value)
|
20
|
+
end
|
21
|
+
when Array
|
22
|
+
value.map { |v| v.is_a?(Hash) ? self.class.new(v) : v }
|
23
|
+
else
|
24
|
+
value
|
25
|
+
end
|
26
|
+
|
27
|
+
create_accessor_methods(field_name, response_object)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class GraphQLSchema
|
4
|
+
def query_root
|
5
|
+
type(query_root_name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def type(name)
|
9
|
+
types_by_name.fetch(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
class TypeDefinition
|
13
|
+
def field(name)
|
14
|
+
fields_by_name.fetch(name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/shipit.rubygems.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# using the default shipit config
|
data/shipit.yml
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
module Adapters
|
6
|
+
class HTTPAdapterTest < Minitest::Test
|
7
|
+
def test_request_builds_a_request
|
8
|
+
config = Config.new(url: 'http://example.org')
|
9
|
+
adapter = HTTPAdapter.new(config)
|
10
|
+
|
11
|
+
stub_request(:post, 'http://example.org')
|
12
|
+
.with(
|
13
|
+
body: {
|
14
|
+
query: 'query { shop }',
|
15
|
+
variables: {},
|
16
|
+
operation_name: nil
|
17
|
+
}.to_json,
|
18
|
+
headers: { 'Accept' => 'application/json' }
|
19
|
+
)
|
20
|
+
.to_return(body: { data: { id: 1 } }.to_json)
|
21
|
+
|
22
|
+
adapter.request('query { shop }')
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_send_request_builds_a_request_with_operation_name
|
26
|
+
config = Config.new(url: 'http://example.org')
|
27
|
+
adapter = HTTPAdapter.new(config)
|
28
|
+
|
29
|
+
stub_request(:post, 'http://example.org')
|
30
|
+
.with(
|
31
|
+
body: {
|
32
|
+
query: 'query shopQuery { shop }',
|
33
|
+
variables: {},
|
34
|
+
operation_name: 'shopQuery'
|
35
|
+
}.to_json,
|
36
|
+
headers: { 'Accept' => 'application/json' }
|
37
|
+
)
|
38
|
+
.to_return(body: { data: { id: 1 } }.to_json)
|
39
|
+
|
40
|
+
adapter.request('query shopQuery { shop }', operation_name: 'shopQuery')
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_send_request_returns_a_response_instance
|
44
|
+
config = Config.new(url: 'http://example.org')
|
45
|
+
adapter = HTTPAdapter.new(config)
|
46
|
+
|
47
|
+
stub_request(:post, 'http://example.org')
|
48
|
+
.with(
|
49
|
+
body: {
|
50
|
+
query: 'query { shop }',
|
51
|
+
variables: {},
|
52
|
+
operation_name: nil
|
53
|
+
}.to_json,
|
54
|
+
headers: { 'Accept' => 'application/json' }
|
55
|
+
)
|
56
|
+
.to_return(body: { data: { id: 1 } }.to_json)
|
57
|
+
|
58
|
+
response = adapter.request('query { shop }')
|
59
|
+
|
60
|
+
assert_instance_of Response, response
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_send_request_raises_an_exception_on_net_http_error
|
64
|
+
config = Config.new(url: 'http://example.org')
|
65
|
+
adapter = HTTPAdapter.new(config)
|
66
|
+
|
67
|
+
stub_request(:post, 'http://example.org')
|
68
|
+
.with(
|
69
|
+
body: {
|
70
|
+
query: 'query { shop }',
|
71
|
+
variables: {},
|
72
|
+
operation_name: nil
|
73
|
+
}.to_json,
|
74
|
+
headers: { 'Accept' => 'application/json' }
|
75
|
+
)
|
76
|
+
.to_return(status: [401, 'Unauthorized'])
|
77
|
+
|
78
|
+
exception = assert_raises ClientError do
|
79
|
+
adapter.request('query { shop }')
|
80
|
+
end
|
81
|
+
|
82
|
+
assert_equal '401 Unauthorized', exception.message
|
83
|
+
assert_equal '401', exception.response.code
|
84
|
+
assert(exception.response.body.nil? || exception.response.body == '')
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_send_request_raises_an_exception_on_net_http_open_timeout_error
|
88
|
+
config = Config.new(url: 'http://example.org')
|
89
|
+
adapter = HTTPAdapter.new(config)
|
90
|
+
|
91
|
+
stub_request(:post, 'http://example.org').to_raise(Net::OpenTimeout)
|
92
|
+
|
93
|
+
assert_raises OpenTimeoutError do
|
94
|
+
adapter.request('query { shop }')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_send_request_raises_an_exception_on_net_http_read_timeout_error
|
99
|
+
config = Config.new(url: 'http://example.org')
|
100
|
+
adapter = HTTPAdapter.new(config)
|
101
|
+
|
102
|
+
stub_request(:post, 'http://example.org').to_raise(Net::ReadTimeout)
|
103
|
+
|
104
|
+
assert_raises ReadTimeoutError do
|
105
|
+
adapter.request('query { shop }')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
class ConfigTest < Minitest::Test
|
6
|
+
def test_initialize_overrides_defaults
|
7
|
+
config = Config.new(
|
8
|
+
debug: true,
|
9
|
+
password: 'foo',
|
10
|
+
per_page: 5,
|
11
|
+
)
|
12
|
+
|
13
|
+
assert config.debug
|
14
|
+
assert_equal({}, config.headers)
|
15
|
+
assert_equal 'foo', config.password
|
16
|
+
assert_equal 5, config.per_page
|
17
|
+
assert_nil config.username
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_initialize_parses_url_as_uri
|
21
|
+
config = Config.new(url: 'http://example.com')
|
22
|
+
|
23
|
+
assert_kind_of URI, config.url
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_initialize_accepts_open_timeout_in_seconds
|
27
|
+
config = Config.new(open_timeout: 2)
|
28
|
+
assert_equal 2, config.open_timeout
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_initialize_accepts_read_timeout_in_seconds
|
32
|
+
config = Config.new(read_timeout: 5)
|
33
|
+
assert_equal 5, config.read_timeout
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_setting_open_timeout_after_initialization
|
37
|
+
config = Config.new
|
38
|
+
config.open_timeout = 2
|
39
|
+
|
40
|
+
assert_equal 2, config.open_timeout
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_setting_read_timeout_after_initialization
|
44
|
+
config = Config.new
|
45
|
+
config.read_timeout = 5
|
46
|
+
|
47
|
+
assert_equal 5, config.read_timeout
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_opent_timeout_is_5_seconds_when_not_set
|
51
|
+
config = Config.new
|
52
|
+
assert_equal 5, config.open_timeout
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_read_timeout_is_5_seconds_when_not_set
|
56
|
+
config = Config.new
|
57
|
+
assert_equal 5, config.read_timeout
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_url_writer_coerces_to_uri
|
61
|
+
config = Config.new
|
62
|
+
config.url = 'http://example.com'
|
63
|
+
|
64
|
+
assert_kind_of URI, config.url
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Client
|
5
|
+
class GraphObjectQueryTransformingTest < Minitest::Test
|
6
|
+
COLLECTION_ID = 'gid://shopify/Collection/67890'
|
7
|
+
COLLECTION_CURSOR = 'collection-cursor'
|
8
|
+
PRODUCT_ID = 'gid://shopify/Product/72727'
|
9
|
+
VARIANTS_CURSOR = 'variants-cursor'
|
10
|
+
GRAPH_FIXTURE = {
|
11
|
+
data: {
|
12
|
+
shop: {
|
13
|
+
name: 'my-shop',
|
14
|
+
privacyPolicy: {
|
15
|
+
body: 'Text'
|
16
|
+
},
|
17
|
+
collections: {
|
18
|
+
pageInfo: {
|
19
|
+
hasPreviousPage: false,
|
20
|
+
hasNextPage: true
|
21
|
+
},
|
22
|
+
edges: [{
|
23
|
+
cursor: COLLECTION_CURSOR,
|
24
|
+
node: {
|
25
|
+
id: COLLECTION_ID,
|
26
|
+
handle: 'fancy-poles'
|
27
|
+
}
|
28
|
+
}]
|
29
|
+
},
|
30
|
+
products: {
|
31
|
+
pageInfo: {
|
32
|
+
hasPreviousPage: false,
|
33
|
+
hasNextPage: true
|
34
|
+
},
|
35
|
+
edges: [{
|
36
|
+
cursor: 'product-cursor',
|
37
|
+
node: {
|
38
|
+
id: PRODUCT_ID,
|
39
|
+
handle: 'some-product',
|
40
|
+
variants: {
|
41
|
+
pageInfo: {
|
42
|
+
hasPreviousPage: false,
|
43
|
+
hasNextPage: true
|
44
|
+
},
|
45
|
+
edges: [{
|
46
|
+
cursor: VARIANTS_CURSOR,
|
47
|
+
node: {
|
48
|
+
id: PRODUCT_ID,
|
49
|
+
title: 'large'
|
50
|
+
}
|
51
|
+
}]
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}]
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
def setup
|
61
|
+
@schema = GraphQLSchema.new(schema_fixture('schema.json'))
|
62
|
+
@graphql_schema = GraphQL::Schema::Loader.load(schema_fixture('schema.json'))
|
63
|
+
|
64
|
+
@base_query = Query::QueryDocument.new(@schema) do |root|
|
65
|
+
root.add_field('shop') do |shop|
|
66
|
+
shop.add_field('name')
|
67
|
+
shop.add_field('privacyPolicy') do |address|
|
68
|
+
address.add_field('body')
|
69
|
+
end
|
70
|
+
shop.add_connection('collections', first: 1) do |collections|
|
71
|
+
collections.add_field('handle')
|
72
|
+
end
|
73
|
+
shop.add_connection('products', first: 1) do |products|
|
74
|
+
products.add_field('handle')
|
75
|
+
products.add_connection('variants', first: 1) do |variants|
|
76
|
+
variants.add_field('title')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
data = GRAPH_FIXTURE[:data].to_json
|
83
|
+
@graph = GraphObject.new(data: JSON.parse(data), query: @base_query)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_nodes_can_generate_a_query_to_refetch_themselves
|
87
|
+
query_string = <<~QUERY
|
88
|
+
query {
|
89
|
+
node(id: "#{COLLECTION_ID}") {
|
90
|
+
... on Collection {
|
91
|
+
id
|
92
|
+
handle
|
93
|
+
}
|
94
|
+
}
|
95
|
+
}
|
96
|
+
QUERY
|
97
|
+
|
98
|
+
assert_equal query_string, @graph.shop.collections.to_a[0].refetch_query.to_query
|
99
|
+
assert_valid_query query_string, @graphql_schema
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_arrays_of_nodes_can_generate_a_query_to_fetch_the_next_page
|
103
|
+
query_string = <<~QUERY
|
104
|
+
query {
|
105
|
+
shop {
|
106
|
+
collections(first: 1, after: "#{COLLECTION_CURSOR}") {
|
107
|
+
edges {
|
108
|
+
cursor
|
109
|
+
node {
|
110
|
+
id
|
111
|
+
handle
|
112
|
+
}
|
113
|
+
}
|
114
|
+
pageInfo {
|
115
|
+
hasPreviousPage
|
116
|
+
hasNextPage
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
QUERY
|
122
|
+
|
123
|
+
assert_equal query_string, @graph.shop.collections.next_page_query.to_query
|
124
|
+
assert_valid_query query_string, @graphql_schema
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_arrays_of_nodes_nested_under_a_truncated_query_to_fetch_their_next_page
|
128
|
+
query_string = <<~QUERY
|
129
|
+
query {
|
130
|
+
node(id: "#{PRODUCT_ID}") {
|
131
|
+
... on Product {
|
132
|
+
id
|
133
|
+
variants(first: 1, after: "#{VARIANTS_CURSOR}") {
|
134
|
+
edges {
|
135
|
+
cursor
|
136
|
+
node {
|
137
|
+
id
|
138
|
+
title
|
139
|
+
}
|
140
|
+
}
|
141
|
+
pageInfo {
|
142
|
+
hasPreviousPage
|
143
|
+
hasNextPage
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
QUERY
|
150
|
+
|
151
|
+
assert_equal query_string, @graph.shop.products.to_a[0].variants.next_page_query.to_query
|
152
|
+
assert_valid_query query_string, @graphql_schema
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|