Split api types creation and query type creation between two different classes
This commit is contained in:
@@ -12,7 +12,7 @@ api_config.each do |api_type_model, api_type_info|
|
|||||||
api_type_info['fields'].each do |field_name, field_type|
|
api_type_info['fields'].each do |field_name, field_type|
|
||||||
if field_type.is_a?(Array) # paginated association
|
if field_type.is_a?(Array) # paginated association
|
||||||
fields[field_name.to_sym] = [field_type.first.constantize]
|
fields[field_name.to_sym] = [field_type.first.constantize]
|
||||||
elsif GraphQL::TypeCreator::SCALAR_TYPES[field_type.to_sym]
|
elsif GraphQL::ApiTypesCreator::SCALAR_TYPES[field_type.to_sym]
|
||||||
fields[field_name.to_sym] = field_type.to_sym
|
fields[field_name.to_sym] = field_type.to_sym
|
||||||
else # simple association
|
else # simple association
|
||||||
fields[field_name.to_sym] = field_type.constantize
|
fields[field_name.to_sym] = field_type.constantize
|
||||||
@@ -22,8 +22,11 @@ api_config.each do |api_type_model, api_type_info|
|
|||||||
api_type_definitions[model] = { options: options, fields: fields }
|
api_type_definitions[model] = { options: options, fields: fields }
|
||||||
end
|
end
|
||||||
|
|
||||||
type_creator = GraphQL::TypeCreator.new(api_type_definitions)
|
api_types_creator = GraphQL::ApiTypesCreator.new(api_type_definitions)
|
||||||
QueryType = type_creator.query_type
|
created_api_types = api_types_creator.create
|
||||||
|
|
||||||
|
query_type_creator = GraphQL::QueryTypeCreator.new(created_api_types)
|
||||||
|
QueryType = query_type_creator.create
|
||||||
|
|
||||||
ConsulSchema = GraphQL::Schema.define do
|
ConsulSchema = GraphQL::Schema.define do
|
||||||
query QueryType
|
query QueryType
|
||||||
|
|||||||
68
lib/graph_ql/api_types_creator.rb
Normal file
68
lib/graph_ql/api_types_creator.rb
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
require 'graphql'
|
||||||
|
|
||||||
|
module GraphQL
|
||||||
|
class ApiTypesCreator
|
||||||
|
SCALAR_TYPES = {
|
||||||
|
integer: GraphQL::INT_TYPE,
|
||||||
|
boolean: GraphQL::BOOLEAN_TYPE,
|
||||||
|
float: GraphQL::FLOAT_TYPE,
|
||||||
|
double: GraphQL::FLOAT_TYPE,
|
||||||
|
string: GraphQL::STRING_TYPE
|
||||||
|
}
|
||||||
|
|
||||||
|
attr_accessor :created_types
|
||||||
|
|
||||||
|
def initialize(api_type_definitions)
|
||||||
|
@api_type_definitions = api_type_definitions
|
||||||
|
@created_types = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@api_type_definitions.each do |model, info|
|
||||||
|
self.create_type(model, info[:fields])
|
||||||
|
end
|
||||||
|
created_types
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.type_kind(type)
|
||||||
|
if SCALAR_TYPES[type]
|
||||||
|
:scalar
|
||||||
|
elsif type.class == Class
|
||||||
|
:simple_association
|
||||||
|
elsif type.class == Array
|
||||||
|
:paginated_association
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_type(model, fields)
|
||||||
|
api_types_creator = self
|
||||||
|
|
||||||
|
created_type = GraphQL::ObjectType.define do
|
||||||
|
|
||||||
|
name(model.name)
|
||||||
|
description("#{model.model_name.human}")
|
||||||
|
|
||||||
|
# Make a field for each column, association or method
|
||||||
|
fields.each do |field_name, field_type|
|
||||||
|
case ApiTypesCreator.type_kind(field_type)
|
||||||
|
when :scalar
|
||||||
|
field(field_name, SCALAR_TYPES[field_type])
|
||||||
|
when :simple_association
|
||||||
|
field(field_name, -> { api_types_creator.created_types[field_type] }) do
|
||||||
|
resolve GraphQL::AssociationResolver.new(field_name, field_type)
|
||||||
|
end
|
||||||
|
when :paginated_association
|
||||||
|
field_type = field_type.first
|
||||||
|
connection(field_name, -> { api_types_creator.created_types[field_type].connection_type }) do
|
||||||
|
resolve GraphQL::AssociationResolver.new(field_name, field_type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
created_types[model] = created_type
|
||||||
|
return created_type # GraphQL::ObjectType
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
40
lib/graph_ql/query_type_creator.rb
Normal file
40
lib/graph_ql/query_type_creator.rb
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
require 'graphql'
|
||||||
|
|
||||||
|
module GraphQL
|
||||||
|
class QueryTypeCreator
|
||||||
|
|
||||||
|
attr_accessor :created_api_types
|
||||||
|
|
||||||
|
def initialize(created_api_types)
|
||||||
|
@created_api_types = created_api_types
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
query_type_creator = self
|
||||||
|
|
||||||
|
GraphQL::ObjectType.define do
|
||||||
|
name 'QueryType'
|
||||||
|
description 'The root query for the schema'
|
||||||
|
|
||||||
|
query_type_creator.created_api_types.each do |model, created_type|
|
||||||
|
# debugger
|
||||||
|
if created_type.fields['id']
|
||||||
|
field model.name.underscore.to_sym do
|
||||||
|
type created_type
|
||||||
|
description "Find one #{model.model_name.human} by ID"
|
||||||
|
argument :id, !types.ID
|
||||||
|
resolve GraphQL::RootElementResolver.new(model)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
connection model.name.underscore.pluralize.to_sym, created_type.connection_type do
|
||||||
|
description "Find all #{model.model_name.human.pluralize}"
|
||||||
|
resolve GraphQL::RootCollectionResolver.new(model)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
require 'graphql'
|
|
||||||
|
|
||||||
module GraphQL
|
|
||||||
class TypeCreator
|
|
||||||
SCALAR_TYPES = {
|
|
||||||
integer: GraphQL::INT_TYPE,
|
|
||||||
boolean: GraphQL::BOOLEAN_TYPE,
|
|
||||||
float: GraphQL::FLOAT_TYPE,
|
|
||||||
double: GraphQL::FLOAT_TYPE,
|
|
||||||
string: GraphQL::STRING_TYPE
|
|
||||||
}
|
|
||||||
|
|
||||||
attr_accessor :created_types, :api_type_definitions, :query_type
|
|
||||||
|
|
||||||
def initialize(api_type_definitions)
|
|
||||||
@api_type_definitions = api_type_definitions
|
|
||||||
@created_types = {}
|
|
||||||
create_api_types
|
|
||||||
create_query_type
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.type_kind(type)
|
|
||||||
if SCALAR_TYPES[type]
|
|
||||||
:scalar
|
|
||||||
elsif type.class == Class
|
|
||||||
:simple_association
|
|
||||||
elsif type.class == Array
|
|
||||||
:paginated_association
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: this method shouldn't be public just for testing purposes, ¿smell?
|
|
||||||
def create_type(model, fields)
|
|
||||||
type_creator = self
|
|
||||||
|
|
||||||
created_type = GraphQL::ObjectType.define do
|
|
||||||
|
|
||||||
name(model.name)
|
|
||||||
description("#{model.model_name.human}")
|
|
||||||
|
|
||||||
# Make a field for each column, association or method
|
|
||||||
fields.each do |field_name, field_type|
|
|
||||||
case TypeCreator.type_kind(field_type)
|
|
||||||
when :scalar
|
|
||||||
field(field_name, SCALAR_TYPES[field_type])
|
|
||||||
when :simple_association
|
|
||||||
field(field_name, -> { type_creator.created_types[field_type] }) do
|
|
||||||
resolve GraphQL::AssociationResolver.new(field_name, field_type)
|
|
||||||
end
|
|
||||||
when :paginated_association
|
|
||||||
field_type = field_type.first
|
|
||||||
connection(field_name, -> { type_creator.created_types[field_type].connection_type }) do
|
|
||||||
resolve GraphQL::AssociationResolver.new(field_name, field_type)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
created_types[model] = created_type
|
|
||||||
return created_type # GraphQL::ObjectType
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def create_api_types
|
|
||||||
api_type_definitions.each do |model, info|
|
|
||||||
self.create_type(model, info[:fields])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_query_type
|
|
||||||
type_creator = self
|
|
||||||
|
|
||||||
@query_type = GraphQL::ObjectType.define do
|
|
||||||
name 'QueryType'
|
|
||||||
description 'The root query for this schema'
|
|
||||||
|
|
||||||
type_creator.created_types.each do |model, created_type|
|
|
||||||
# create field to retrive a single object
|
|
||||||
if type_creator.api_type_definitions[model][:fields][:id]
|
|
||||||
field model.name.underscore.to_sym do
|
|
||||||
type created_type
|
|
||||||
description "Find one #{model.model_name.human} by ID"
|
|
||||||
argument :id, !types.ID
|
|
||||||
resolve GraphQL::RootElementResolver.new(model)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# create connection to retrive a collection
|
|
||||||
connection model.name.underscore.pluralize.to_sym, created_type.connection_type do
|
|
||||||
description "Find all #{model.model_name.human.pluralize}"
|
|
||||||
resolve GraphQL::RootCollectionResolver.new(model)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
58
spec/lib/graph_ql/api_types_creator_spec.rb
Normal file
58
spec/lib/graph_ql/api_types_creator_spec.rb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe GraphQL::ApiTypesCreator do
|
||||||
|
let(:api_types_creator) { GraphQL::ApiTypesCreator.new( {} ) }
|
||||||
|
|
||||||
|
describe "::create_type" do
|
||||||
|
it "creates fields for Int attributes" do
|
||||||
|
debate_type = api_types_creator.create_type(Debate, { id: :integer })
|
||||||
|
created_field = debate_type.fields['id']
|
||||||
|
|
||||||
|
expect(created_field).to be_a(GraphQL::Field)
|
||||||
|
expect(created_field.type).to be_a(GraphQL::ScalarType)
|
||||||
|
expect(created_field.type.name).to eq('Int')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates fields for String attributes" do
|
||||||
|
debate_type = api_types_creator.create_type(Debate, { title: :string })
|
||||||
|
created_field = debate_type.fields['title']
|
||||||
|
|
||||||
|
expect(created_field).to be_a(GraphQL::Field)
|
||||||
|
expect(created_field.type).to be_a(GraphQL::ScalarType)
|
||||||
|
expect(created_field.type.name).to eq('String')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates connections for :belongs_to associations" do
|
||||||
|
user_type = api_types_creator.create_type(User, { id: :integer })
|
||||||
|
debate_type = api_types_creator.create_type(Debate, { author: User })
|
||||||
|
|
||||||
|
connection = debate_type.fields['author']
|
||||||
|
|
||||||
|
expect(connection).to be_a(GraphQL::Field)
|
||||||
|
expect(connection.type).to eq(user_type)
|
||||||
|
expect(connection.name).to eq('author')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates connections for :has_one associations" do
|
||||||
|
user_type = api_types_creator.create_type(User, { organization: Organization })
|
||||||
|
organization_type = api_types_creator.create_type(Organization, { id: :integer })
|
||||||
|
|
||||||
|
connection = user_type.fields['organization']
|
||||||
|
|
||||||
|
expect(connection).to be_a(GraphQL::Field)
|
||||||
|
expect(connection.type).to eq(organization_type)
|
||||||
|
expect(connection.name).to eq('organization')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates connections for :has_many associations" do
|
||||||
|
comment_type = api_types_creator.create_type(Comment, { id: :integer })
|
||||||
|
debate_type = api_types_creator.create_type(Debate, { comments: [Comment] })
|
||||||
|
|
||||||
|
connection = debate_type.fields['comments']
|
||||||
|
|
||||||
|
expect(connection).to be_a(GraphQL::Field)
|
||||||
|
expect(connection.type).to eq(comment_type.connection_type)
|
||||||
|
expect(connection.name).to eq('comments')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
39
spec/lib/graph_ql/query_type_creator_spec.rb
Normal file
39
spec/lib/graph_ql/query_type_creator_spec.rb
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe GraphQL::QueryTypeCreator do
|
||||||
|
let(:api_type_definitions) do
|
||||||
|
{
|
||||||
|
ProposalNotification => { fields: { title: 'string' } },
|
||||||
|
Proposal => { fields: { id: 'integer', title: 'string' } }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:api_types_creator) { GraphQL::ApiTypesCreator.new(api_type_definitions) }
|
||||||
|
let(:created_api_types) { api_types_creator.create }
|
||||||
|
let(:query_type_creator) { GraphQL::QueryTypeCreator.new(created_api_types) }
|
||||||
|
|
||||||
|
describe "::create" do
|
||||||
|
let(:query_type) { query_type_creator.create }
|
||||||
|
|
||||||
|
it 'creates a QueryType with fields to retrieve single objects whose model fields included an ID' do
|
||||||
|
field = query_type.fields['proposal']
|
||||||
|
proposal_type = query_type_creator.created_api_types[Proposal]
|
||||||
|
|
||||||
|
expect(field).to be_a(GraphQL::Field)
|
||||||
|
expect(field.type).to eq(proposal_type)
|
||||||
|
expect(field.name).to eq('proposal')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a QueryType without fields to retrieve single objects whose model fields did not include an ID' do
|
||||||
|
expect(query_type.fields['proposal_notification']).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a QueryType with connections to retrieve collections of objects" do
|
||||||
|
connection = query_type.fields['proposals']
|
||||||
|
proposal_type = query_type_creator.created_api_types[Proposal]
|
||||||
|
|
||||||
|
expect(connection).to be_a(GraphQL::Field)
|
||||||
|
expect(connection.type).to eq(proposal_type.connection_type)
|
||||||
|
expect(connection.name).to eq('proposals')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
describe GraphQL::TypeCreator do
|
|
||||||
let(:api_type_definitions) { {} }
|
|
||||||
let(:type_creator) { GraphQL::TypeCreator.new(api_type_definitions) }
|
|
||||||
|
|
||||||
describe "::query_type" do
|
|
||||||
let(:api_type_definitions) do
|
|
||||||
{
|
|
||||||
ProposalNotification => { fields: { title: 'string' } },
|
|
||||||
Proposal => { fields: { id: 'integer', title: 'string' } }
|
|
||||||
}
|
|
||||||
end
|
|
||||||
let(:query_type) { type_creator.query_type }
|
|
||||||
|
|
||||||
it 'has fields to retrieve single objects whose model fields included an ID' do
|
|
||||||
field = query_type.fields['proposal']
|
|
||||||
proposal_type = type_creator.created_types[Proposal]
|
|
||||||
|
|
||||||
expect(field).to be_a(GraphQL::Field)
|
|
||||||
expect(field.type).to eq(proposal_type)
|
|
||||||
expect(field.name).to eq('proposal')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not have fields to retrieve single objects whose model fields did not include an ID' do
|
|
||||||
expect(query_type.fields['proposal_notification']).to be_nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it "has connections to retrieve collections of objects" do
|
|
||||||
connection = query_type.fields['proposals']
|
|
||||||
proposal_type = type_creator.created_types[Proposal]
|
|
||||||
|
|
||||||
expect(connection).to be_a(GraphQL::Field)
|
|
||||||
expect(connection.type).to eq(proposal_type.connection_type)
|
|
||||||
expect(connection.name).to eq('proposals')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "::create_type" do
|
|
||||||
it "creates fields for Int attributes" do
|
|
||||||
debate_type = type_creator.create_type(Debate, { id: :integer })
|
|
||||||
created_field = debate_type.fields['id']
|
|
||||||
|
|
||||||
expect(created_field).to be_a(GraphQL::Field)
|
|
||||||
expect(created_field.type).to be_a(GraphQL::ScalarType)
|
|
||||||
expect(created_field.type.name).to eq('Int')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "creates fields for String attributes" do
|
|
||||||
debate_type = type_creator.create_type(Debate, { title: :string })
|
|
||||||
created_field = debate_type.fields['title']
|
|
||||||
|
|
||||||
expect(created_field).to be_a(GraphQL::Field)
|
|
||||||
expect(created_field.type).to be_a(GraphQL::ScalarType)
|
|
||||||
expect(created_field.type.name).to eq('String')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "creates connections for :belongs_to associations" do
|
|
||||||
user_type = type_creator.create_type(User, { id: :integer })
|
|
||||||
debate_type = type_creator.create_type(Debate, { author: User })
|
|
||||||
|
|
||||||
connection = debate_type.fields['author']
|
|
||||||
|
|
||||||
expect(connection).to be_a(GraphQL::Field)
|
|
||||||
expect(connection.type).to eq(user_type)
|
|
||||||
expect(connection.name).to eq('author')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "creates connections for :has_one associations" do
|
|
||||||
user_type = type_creator.create_type(User, { organization: Organization })
|
|
||||||
organization_type = type_creator.create_type(Organization, { id: :integer })
|
|
||||||
|
|
||||||
connection = user_type.fields['organization']
|
|
||||||
|
|
||||||
expect(connection).to be_a(GraphQL::Field)
|
|
||||||
expect(connection.type).to eq(organization_type)
|
|
||||||
expect(connection.name).to eq('organization')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "creates connections for :has_many associations" do
|
|
||||||
comment_type = type_creator.create_type(Comment, { id: :integer })
|
|
||||||
debate_type = type_creator.create_type(Debate, { comments: [Comment] })
|
|
||||||
|
|
||||||
connection = debate_type.fields['comments']
|
|
||||||
|
|
||||||
expect(connection).to be_a(GraphQL::Field)
|
|
||||||
expect(connection.type).to eq(comment_type.connection_type)
|
|
||||||
expect(connection.name).to eq('comments')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user