Support class-based schemas and types

This commit is contained in:
Robert Mosolgo 2019-09-11 11:54:17 -04:00
Родитель a37a69ed94
Коммит 4fdbb218ba
5 изменённых файлов: 1689 добавлений и 102 удалений

Просмотреть файл

@ -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

60
spec/fixtures/swapi_query.graphql поставляемый
Просмотреть файл

@ -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

1575
spec/fixtures/swapi_schema.graphql поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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