Merge pull request #17 from github/support-class-based-types
Support class-based schemas and types
This commit is contained in:
Коммит
0e9385ecf7
|
@ -1,6 +1,6 @@
|
||||||
language: ruby
|
language: ruby
|
||||||
rvm:
|
rvm:
|
||||||
- 2.2.2
|
- 2.3.8
|
||||||
- ruby-head
|
- ruby-head
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
|
|
|
@ -9,9 +9,9 @@ Gem::Specification.new do |s|
|
||||||
s.files += Dir.glob('lib/**/*.rb')
|
s.files += Dir.glob('lib/**/*.rb')
|
||||||
s.homepage = 'https://github.com/github/graphql-relay-walker'
|
s.homepage = 'https://github.com/github/graphql-relay-walker'
|
||||||
|
|
||||||
s.add_dependency 'graphql', '>= 1.5.6'
|
s.add_dependency 'graphql', '~> 1.8'
|
||||||
|
|
||||||
s.add_development_dependency 'graphql-client', '~> 0.2'
|
s.add_development_dependency 'graphql-client', '~> 0.15'
|
||||||
s.add_development_dependency 'rake', '~> 11.3'
|
s.add_development_dependency 'rake', '~> 11.3'
|
||||||
s.add_development_dependency 'rspec', '~> 3.5'
|
s.add_development_dependency 'rspec', '~> 3.5'
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,7 +70,10 @@ module GraphQL::Relay::Walker
|
||||||
def inline_fragment_ast(type, with_children: true)
|
def inline_fragment_ast(type, with_children: true)
|
||||||
selections = []
|
selections = []
|
||||||
if with_children
|
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
|
field_type = field.type.unwrap
|
||||||
if node_field?(field) && include?(field_type)
|
if node_field?(field) && include?(field_type)
|
||||||
selections << node_field_ast(field)
|
selections << node_field_ast(field)
|
||||||
|
@ -88,7 +91,7 @@ module GraphQL::Relay::Walker
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
GraphQL::Language::Nodes::InlineFragment.new(
|
GraphQL::Language::Nodes::InlineFragment.new(
|
||||||
type: make_type_name_node(type.name),
|
type: make_type_name_node(type.graphql_name),
|
||||||
selections: selections,
|
selections: selections,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -114,12 +117,12 @@ module GraphQL::Relay::Walker
|
||||||
if !required_args_are_present
|
if !required_args_are_present
|
||||||
nil
|
nil
|
||||||
else
|
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|
|
f_args = arguments.map do |name, value|
|
||||||
GraphQL::Language::Nodes::Argument.new(name: name, value: value)
|
GraphQL::Language::Nodes::Argument.new(name: name, value: value)
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -213,11 +216,12 @@ module GraphQL::Relay::Walker
|
||||||
# `node` field that is a relay node. Returns false otherwise.
|
# `node` field that is a relay node. Returns false otherwise.
|
||||||
def connection_field?(field)
|
def connection_field?(field)
|
||||||
type = field.type.unwrap
|
type = field.type.unwrap
|
||||||
|
if type.kind.fields?
|
||||||
if edges_field = type.get_field('edges')
|
if edges_field = type.get_field('edges')
|
||||||
edges = edges_field.type.unwrap
|
edges = edges_field.type.unwrap
|
||||||
if node_field = edges.get_field('node')
|
if node_field = edges.get_field('node')
|
||||||
return node_field?(node_field)
|
return node_field?(node_field)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -268,7 +272,7 @@ module GraphQL::Relay::Walker
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_input?(type, input)
|
def valid_input?(type, input)
|
||||||
type.valid_isolated_input?(input)
|
type.valid_input?(input, GraphQL::Query::NullContext)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_type_name_node(type_name)
|
def make_type_name_node(type_name)
|
||||||
|
|
|
@ -3,23 +3,26 @@ require 'graphql/relay/walker'
|
||||||
require 'graphql/relay/walker/client_ext'
|
require 'graphql/relay/walker/client_ext'
|
||||||
|
|
||||||
describe GraphQL::Relay::Walker::ClientExt do
|
describe GraphQL::Relay::Walker::ClientExt do
|
||||||
let(:schema_path) { 'spec/fixtures/swapi_schema.json' }
|
[
|
||||||
let(:query_path) { 'spec/fixtures/swapi_query.graphql' }
|
GraphQL::Client.load_schema('spec/fixtures/swapi_schema.json'),
|
||||||
let(:schema) { GraphQL::Client.load_schema(schema_path) }
|
GraphQL::Schema.from_definition("spec/fixtures/swapi_schema.graphql")
|
||||||
let(:client) { GraphQL::Client.new(schema: schema) }
|
].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 context through to GraphQLClient#query' do
|
||||||
it 'allows passing additional variables through to GraphQLClient#query' do
|
viewer = Object.new
|
||||||
expected = { variables: { 'foo' => 'bar', 'id' => '12345' }, context: {} }
|
expected = { variables: { 'id' => '12345' }, context: { viewer: viewer } }
|
||||||
expect(client).to receive(:query).with(anything, expected).and_return({})
|
expect(client).to receive(:query).with(anything, expected).and_return({})
|
||||||
client.walk(from_id: '12345', variables: { 'foo' => 'bar' })
|
client.walk(from_id: '12345', context: { viewer: viewer })
|
||||||
end
|
end
|
||||||
|
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 })
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,20 @@ query($id: ID!) {
|
||||||
node(id: $id) {
|
node(id: $id) {
|
||||||
id
|
id
|
||||||
... on Film {
|
... on Film {
|
||||||
|
characterConnection(first: 5) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
planetConnection(first: 5) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
speciesConnection(first: 5) {
|
speciesConnection(first: 5) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -23,25 +37,8 @@ query($id: ID!) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
characterConnection(first: 5) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
planetConnection(first: 5) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
... on Person {
|
... on Person {
|
||||||
homeworld {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
filmConnection(first: 5) {
|
filmConnection(first: 5) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
|
@ -49,6 +46,9 @@ query($id: ID!) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
homeworld {
|
||||||
|
id
|
||||||
|
}
|
||||||
species {
|
species {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -68,14 +68,14 @@ query($id: ID!) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on Planet {
|
... on Planet {
|
||||||
residentConnection(first: 5) {
|
filmConnection(first: 5) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filmConnection(first: 5) {
|
residentConnection(first: 5) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
@ -84,6 +84,13 @@ query($id: ID!) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on Species {
|
... on Species {
|
||||||
|
filmConnection(first: 5) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
homeworld {
|
homeworld {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -94,23 +101,16 @@ query($id: ID!) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filmConnection(first: 5) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
... on Starship {
|
... on Starship {
|
||||||
pilotConnection(first: 5) {
|
filmConnection(first: 5) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filmConnection(first: 5) {
|
pilotConnection(first: 5) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
@ -119,14 +119,14 @@ query($id: ID!) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on Vehicle {
|
... on Vehicle {
|
||||||
pilotConnection(first: 5) {
|
filmConnection(first: 5) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filmConnection(first: 5) {
|
pilotConnection(first: 5) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2,55 +2,59 @@ require 'graphql/relay/walker'
|
||||||
require 'graphql/client'
|
require 'graphql/client'
|
||||||
|
|
||||||
describe GraphQL::Relay::Walker::QueryBuilder do
|
describe GraphQL::Relay::Walker::QueryBuilder do
|
||||||
let(:schema_path) { 'spec/fixtures/swapi_schema.json' }
|
[
|
||||||
let(:query_path) { 'spec/fixtures/swapi_query.graphql' }
|
GraphQL::Client.load_schema('spec/fixtures/swapi_schema.json'),
|
||||||
let(:schema) { GraphQL::Client.load_schema(schema_path) }
|
GraphQL::Schema.from_definition("spec/fixtures/swapi_schema.graphql")
|
||||||
let(:client) { GraphQL::Client.new(schema: schema) }
|
].each do |schema|
|
||||||
let(:query_builder) { described_class.new(schema) }
|
describe "With #{schema.class} schema" do
|
||||||
let(:ast) { query_builder.ast }
|
let(:query_path) { 'spec/fixtures/swapi_query.graphql' }
|
||||||
let(:query_string) { query_builder.query_string }
|
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
|
it 'adds an alias to all fields except id and node' do
|
||||||
subject { ast }
|
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
|
describe 'query_string' do
|
||||||
fields(ast).reject do |node|
|
subject { query_string }
|
||||||
%w[node id].include?(node.name)
|
|
||||||
end.each do |field|
|
it 'generates a valid query for the schema' do
|
||||||
expect(field.alias).not_to be_nil
|
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
|
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
|
end
|
||||||
|
|
Загрузка…
Ссылка в новой задаче