Support class-based schemas and types
This commit is contained in:
Родитель
a37a69ed94
Коммит
4fdbb218ba
|
@ -70,7 +70,10 @@ module GraphQL::Relay::Walker
|
|||
def inline_fragment_ast(type, with_children: true)
|
||||
selections = []
|
||||
if with_children
|
||||
type.all_fields.each do |field|
|
||||
# Class-based types return all fields in `.fields`
|
||||
all_fields = type.respond_to?(:all_fields) ? type.all_fields : type.fields.values
|
||||
all_fields = all_fields.sort_by(&:graphql_name)
|
||||
all_fields.each do |field|
|
||||
field_type = field.type.unwrap
|
||||
if node_field?(field) && include?(field_type)
|
||||
selections << node_field_ast(field)
|
||||
|
@ -88,7 +91,7 @@ module GraphQL::Relay::Walker
|
|||
nil
|
||||
else
|
||||
GraphQL::Language::Nodes::InlineFragment.new(
|
||||
type: make_type_name_node(type.name),
|
||||
type: make_type_name_node(type.graphql_name),
|
||||
selections: selections,
|
||||
)
|
||||
end
|
||||
|
@ -114,12 +117,12 @@ module GraphQL::Relay::Walker
|
|||
if !required_args_are_present
|
||||
nil
|
||||
else
|
||||
f_alias = field.name == 'id' ? nil : random_alias
|
||||
f_alias = field.graphql_name == 'id' ? nil : random_alias
|
||||
f_args = arguments.map do |name, value|
|
||||
GraphQL::Language::Nodes::Argument.new(name: name, value: value)
|
||||
end
|
||||
|
||||
GraphQL::Language::Nodes::Field.new(name: field.name, alias: f_alias, arguments: f_args)
|
||||
GraphQL::Language::Nodes::Field.new(name: field.graphql_name, alias: f_alias, arguments: f_args)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -213,11 +216,12 @@ module GraphQL::Relay::Walker
|
|||
# `node` field that is a relay node. Returns false otherwise.
|
||||
def connection_field?(field)
|
||||
type = field.type.unwrap
|
||||
|
||||
if edges_field = type.get_field('edges')
|
||||
edges = edges_field.type.unwrap
|
||||
if node_field = edges.get_field('node')
|
||||
return node_field?(node_field)
|
||||
if type.kind.fields?
|
||||
if edges_field = type.get_field('edges')
|
||||
edges = edges_field.type.unwrap
|
||||
if node_field = edges.get_field('node')
|
||||
return node_field?(node_field)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -268,7 +272,7 @@ module GraphQL::Relay::Walker
|
|||
end
|
||||
|
||||
def valid_input?(type, input)
|
||||
type.valid_isolated_input?(input)
|
||||
type.valid_input?(input, GraphQL::Query::NullContext)
|
||||
end
|
||||
|
||||
def make_type_name_node(type_name)
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
require "pry"
|
||||
require 'graphql/client'
|
||||
require 'graphql/relay/walker'
|
||||
require 'graphql/relay/walker/client_ext'
|
||||
|
||||
describe GraphQL::Relay::Walker::ClientExt do
|
||||
let(:schema_path) { 'spec/fixtures/swapi_schema.json' }
|
||||
let(:query_path) { 'spec/fixtures/swapi_query.graphql' }
|
||||
let(:schema) { GraphQL::Client.load_schema(schema_path) }
|
||||
let(:client) { GraphQL::Client.new(schema: schema) }
|
||||
[
|
||||
GraphQL::Client.load_schema('spec/fixtures/swapi_schema.json'),
|
||||
GraphQL::Schema.from_definition("spec/fixtures/swapi_schema.graphql")
|
||||
].each do |schema|
|
||||
describe "with #{schema.class} schema" do
|
||||
describe '#walk' do
|
||||
let(:client) { GraphQL::Client.new(schema: schema) }
|
||||
it 'allows passing additional variables through to GraphQLClient#query' do
|
||||
expected = { variables: { 'foo' => 'bar', 'id' => '12345' }, context: {} }
|
||||
expect(client).to receive(:query).with(anything, expected).and_return({})
|
||||
client.walk(from_id: '12345', variables: { 'foo' => 'bar' })
|
||||
end
|
||||
|
||||
describe '#walk' do
|
||||
it 'allows passing additional variables through to GraphQLClient#query' do
|
||||
expected = { variables: { 'foo' => 'bar', 'id' => '12345' }, context: {} }
|
||||
expect(client).to receive(:query).with(anything, expected).and_return({})
|
||||
client.walk(from_id: '12345', variables: { 'foo' => 'bar' })
|
||||
end
|
||||
|
||||
it 'allows passing additional context through to GraphQLClient#query' do
|
||||
viewer = Object.new
|
||||
expected = { variables: { 'id' => '12345' }, context: { viewer: viewer } }
|
||||
expect(client).to receive(:query).with(anything, expected).and_return({})
|
||||
client.walk(from_id: '12345', context: { viewer: viewer })
|
||||
it 'allows passing additional context through to GraphQLClient#query' do
|
||||
viewer = Object.new
|
||||
expected = { variables: { 'id' => '12345' }, context: { viewer: viewer } }
|
||||
expect(client).to receive(:query).with(anything, expected).and_return({})
|
||||
client.walk(from_id: '12345', context: { viewer: viewer })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,6 +2,20 @@ query($id: ID!) {
|
|||
node(id: $id) {
|
||||
id
|
||||
... on Film {
|
||||
characterConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
planetConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
speciesConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
|
@ -23,25 +37,8 @@ query($id: ID!) {
|
|||
}
|
||||
}
|
||||
}
|
||||
characterConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
planetConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on Person {
|
||||
homeworld {
|
||||
id
|
||||
}
|
||||
filmConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
|
@ -49,6 +46,9 @@ query($id: ID!) {
|
|||
}
|
||||
}
|
||||
}
|
||||
homeworld {
|
||||
id
|
||||
}
|
||||
species {
|
||||
id
|
||||
}
|
||||
|
@ -68,14 +68,14 @@ query($id: ID!) {
|
|||
}
|
||||
}
|
||||
... on Planet {
|
||||
residentConnection(first: 5) {
|
||||
filmConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
filmConnection(first: 5) {
|
||||
residentConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
|
@ -84,6 +84,13 @@ query($id: ID!) {
|
|||
}
|
||||
}
|
||||
... on Species {
|
||||
filmConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
homeworld {
|
||||
id
|
||||
}
|
||||
|
@ -94,23 +101,16 @@ query($id: ID!) {
|
|||
}
|
||||
}
|
||||
}
|
||||
filmConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on Starship {
|
||||
pilotConnection(first: 5) {
|
||||
filmConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
filmConnection(first: 5) {
|
||||
pilotConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
|
@ -119,14 +119,14 @@ query($id: ID!) {
|
|||
}
|
||||
}
|
||||
... on Vehicle {
|
||||
pilotConnection(first: 5) {
|
||||
filmConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
filmConnection(first: 5) {
|
||||
pilotConnection(first: 5) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2,55 +2,59 @@ require 'graphql/relay/walker'
|
|||
require 'graphql/client'
|
||||
|
||||
describe GraphQL::Relay::Walker::QueryBuilder do
|
||||
let(:schema_path) { 'spec/fixtures/swapi_schema.json' }
|
||||
let(:query_path) { 'spec/fixtures/swapi_query.graphql' }
|
||||
let(:schema) { GraphQL::Client.load_schema(schema_path) }
|
||||
let(:client) { GraphQL::Client.new(schema: schema) }
|
||||
let(:query_builder) { described_class.new(schema) }
|
||||
let(:ast) { query_builder.ast }
|
||||
let(:query_string) { query_builder.query_string }
|
||||
[
|
||||
GraphQL::Client.load_schema('spec/fixtures/swapi_schema.json'),
|
||||
GraphQL::Schema.from_definition("spec/fixtures/swapi_schema.graphql")
|
||||
].each do |schema|
|
||||
describe "With #{schema.class} schema" do
|
||||
let(:query_path) { 'spec/fixtures/swapi_query.graphql' }
|
||||
let(:client) { GraphQL::Client.new(schema: schema) }
|
||||
let(:query_builder) { described_class.new(schema) }
|
||||
let(:ast) { query_builder.ast }
|
||||
let(:query_string) { query_builder.query_string }
|
||||
describe 'ast' do
|
||||
subject { ast }
|
||||
|
||||
describe 'ast' do
|
||||
subject { ast }
|
||||
it 'adds an alias to all fields except id and node' do
|
||||
fields(ast).reject do |node|
|
||||
%w[node id].include?(node.name)
|
||||
end.each do |field|
|
||||
expect(field.alias).not_to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'adds an alias to all fields except id and node' do
|
||||
fields(ast).reject do |node|
|
||||
%w[node id].include?(node.name)
|
||||
end.each do |field|
|
||||
expect(field.alias).not_to be_nil
|
||||
describe 'query_string' do
|
||||
subject { query_string }
|
||||
|
||||
it 'generates a valid query for the schema' do
|
||||
expect { client.parse(query_string) }.not_to raise_error
|
||||
end
|
||||
|
||||
describe 'with aliases removed' do
|
||||
it 'matches the expected query string' do
|
||||
# Replace the aliases, leaving the leading whitespace in place
|
||||
string_without_aliases = subject.gsub(/ [a-z]{12}: /, " ")
|
||||
expect(string_without_aliases).to eq(File.read(query_path).strip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fields(ast)
|
||||
nodes(ast).select { |node| node.is_a?(GraphQL::Language::Nodes::Field) }
|
||||
end
|
||||
|
||||
def nodes(ast)
|
||||
children = if ast.respond_to?(:selections)
|
||||
ast.selections
|
||||
elsif ast.respond_to?(:definitions)
|
||||
ast.definitions
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
children + children.map { |child| nodes(child) }.flatten
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'query_string' do
|
||||
subject { query_string }
|
||||
|
||||
it 'generates a valid query for the schema' do
|
||||
expect { client.parse(query_string) }.not_to raise_error
|
||||
end
|
||||
|
||||
describe 'with aliases removed' do
|
||||
it 'matches the expected query string' do
|
||||
# Replace the aliases, leaving the leading whitespace in place
|
||||
string_without_aliases = subject.gsub(/ [a-z]{12}: /, " ")
|
||||
expect(string_without_aliases).to eq(File.read(query_path).strip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fields(ast)
|
||||
nodes(ast).select { |node| node.is_a?(GraphQL::Language::Nodes::Field) }
|
||||
end
|
||||
|
||||
def nodes(ast)
|
||||
children = if ast.respond_to?(:selections)
|
||||
ast.selections
|
||||
elsif ast.respond_to?(:definitions)
|
||||
ast.definitions
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
children + children.map { |child| nodes(child) }.flatten
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче