Read GraphQL field types from file instead of from database

This commit is contained in:
Alberto Miedes Garcés
2017-01-01 19:36:07 +01:00
parent 0f663603d0
commit 7dc4d80e7b
3 changed files with 120 additions and 45 deletions

View File

@@ -1,12 +1,84 @@
API_TYPE_DEFINITIONS = { API_TYPE_DEFINITIONS = {
User => %I[ id username gender geozone_id geozone ], User => {
Debate => %I[ id title description created_at cached_votes_total cached_votes_up cached_votes_down comments_count hot_score confidence_score geozone_id geozone comments public_author ], id: :integer,
Proposal => %I[ id title description external_url cached_votes_up comments_count hot_score confidence_score created_at summary video_url geozone_id retired_at retired_reason retired_explanation geozone comments proposal_notifications public_author ], username: :string,
Comment => %I[ id commentable_id commentable_type body created_at cached_votes_total cached_votes_up cached_votes_down ancestry confidence_score public_author ], gender: :string,
Geozone => %I[ id name ], geozone_id: :integer,
ProposalNotification => %I[ title body proposal_id created_at proposal ], geozone: Geozone
Tag => %I[ id name taggings_count kind ], },
Vote => %I[ votable_id votable_type created_at public_voter ] Debate => {
id: :integer,
title: :string,
description: :string,
created_at: :string,
cached_votes_total: :integer,
cached_votes_up: :integer,
cached_votes_down: :integer,
comments_count: :integer,
hot_score: :integer,
confidence_score: :integer,
geozone_id: :integer,
geozone: Geozone,
comments: [Comment],
public_author: User
},
Proposal => {
id: :integer,
title: :string,
description: :sting,
external_url: :string,
cached_votes_up: :integer,
comments_count: :integer,
hot_score: :integer,
confidence_score: :integer,
created_at: :string,
summary: :string,
video_url: :string,
geozone_id: :integer,
retired_at: :string,
retired_reason: :string,
retired_explanation: :string,
geozone: Geozone,
comments: [Comment],
proposal_notifications: [ProposalNotification],
public_author: User
},
Comment => {
id: :integer,
commentable_id: :integer,
commentable_type: :string,
body: :string,
created_at: :string,
cached_votes_total: :integer,
cached_votes_up: :integer,
cached_votes_down: :integer,
ancestry: :string,
confidence_score: :integer,
public_author: User
},
Geozone => {
id: :integer,
name: :string
},
ProposalNotification => {
title: :string,
body: :string,
proposal_id: :integer,
created_at: :string,
proposal: Proposal
},
Tag => {
id: :integer,
name: :string,
taggings_count: :integer,
kind: :string
},
Vote => {
votable_id: :integer,
votable_type: :string,
created_at: :string,
public_voter: User
}
} }
type_creator = GraphQL::TypeCreator.new type_creator = GraphQL::TypeCreator.new
@@ -35,7 +107,7 @@ QueryRoot = GraphQL::ObjectType.define do
type_creator.created_types.each do |model, created_type| type_creator.created_types.each do |model, created_type|
# create an entry field to retrive a single object # create an entry field to retrive a single object
if API_TYPE_DEFINITIONS[model].include?(:id) if API_TYPE_DEFINITIONS[model][:id]
field model.name.underscore.to_sym do field model.name.underscore.to_sym do
type created_type type created_type
description "Find one #{model.model_name.human} by ID" description "Find one #{model.model_name.human} by ID"

View File

@@ -1,12 +1,13 @@
module GraphQL module GraphQL
class TypeCreator class TypeCreator
# Return a GraphQL type for a 'database_type' # Return a GraphQL type for a 'database_type'
TYPES_CONVERSION = Hash.new(GraphQL::STRING_TYPE).merge( SCALAR_TYPES = {
integer: GraphQL::INT_TYPE, integer: GraphQL::INT_TYPE,
boolean: GraphQL::BOOLEAN_TYPE, boolean: GraphQL::BOOLEAN_TYPE,
float: GraphQL::FLOAT_TYPE, float: GraphQL::FLOAT_TYPE,
double: GraphQL::FLOAT_TYPE double: GraphQL::FLOAT_TYPE,
) string: GraphQL::STRING_TYPE
}
attr_accessor :created_types attr_accessor :created_types
@@ -14,7 +15,7 @@ module GraphQL
@created_types = {} @created_types = {}
end end
def create(model, field_names) def create(model, fields)
type_creator = self type_creator = self
created_type = GraphQL::ObjectType.define do created_type = GraphQL::ObjectType.define do
@@ -23,41 +24,43 @@ module GraphQL
description("#{model.model_name.human}") description("#{model.model_name.human}")
# Make a field for each column, association or method # Make a field for each column, association or method
field_names.each do |field_name| fields.each do |name, type|
if model.column_names.include?(field_name.to_s) case GraphQL::TypeCreator.type_kind(type)
field(field_name.to_s, TYPES_CONVERSION[model.columns_hash[field_name.to_s].type]) when :scalar
else field name, SCALAR_TYPES[type]
association = type_creator.class.association?(model, field_name) when :simple_association
target_model = association.klass field(name, -> { type_creator.created_types[type] }) do
public_elements = target_model.respond_to?(:public_for_api) ? target_model.public_for_api : target_model.all resolve -> (object, arguments, context) do
target_public_elements = type.respond_to?(:public_for_api) ? type.public_for_api : type.all
if type_creator.class.needs_pagination?(association) wanted_element = object.send(name)
connection(association.name, -> { type_creator.created_types[target_model].connection_type }) do target_public_elements.include?(wanted_element) ? wanted_element : nil
resolve -> (object, arguments, context) do
object.send(association.name).all & public_elements.all
end
end end
else end
field(association.name, -> { type_creator.created_types[target_model] }) do when :paginated_association
resolve -> (object, arguments, context) do type = type.first
linked_element = object.send(field_name) connection(name, -> { type_creator.created_types[type].connection_type }) do
public_elements.include?(linked_element) ? linked_element : nil resolve -> (object, arguments, context) do
end target_public_elements = type.respond_to?(:public_for_api) ? type.public_for_api : type.all
object.send(name).all & target_public_elements.all
end end
end end
end end
end end
end end
created_types[model] = created_type created_types[model] = created_type
return created_type # GraphQL::ObjectType return created_type # GraphQL::ObjectType
end end
def self.association?(model, field_name) def self.type_kind(type)
model.reflect_on_all_associations.find { |a| a.name == field_name } if SCALAR_TYPES[type]
:scalar
elsif type.class == Class
:simple_association
elsif type.class == Array
:paginated_association
end
end end
def self.needs_pagination?(association)
association.macro == :has_many
end
end end
end end

View File

@@ -5,7 +5,7 @@ describe GraphQL::TypeCreator do
describe "::create" do describe "::create" do
it "creates fields for Int attributes" do it "creates fields for Int attributes" do
debate_type = type_creator.create(Debate, %I[ id ]) debate_type = type_creator.create(Debate, { id: :integer })
created_field = debate_type.fields['id'] created_field = debate_type.fields['id']
expect(created_field).to be_a(GraphQL::Field) expect(created_field).to be_a(GraphQL::Field)
@@ -14,7 +14,7 @@ describe GraphQL::TypeCreator do
end end
it "creates fields for String attributes" do it "creates fields for String attributes" do
debate_type = type_creator.create(Debate, %I[ title ]) debate_type = type_creator.create(Debate, { title: :string })
created_field = debate_type.fields['title'] created_field = debate_type.fields['title']
expect(created_field).to be_a(GraphQL::Field) expect(created_field).to be_a(GraphQL::Field)
@@ -23,8 +23,8 @@ describe GraphQL::TypeCreator do
end end
it "creates connections for :belongs_to associations" do it "creates connections for :belongs_to associations" do
user_type = type_creator.create(User, %I[ id ]) user_type = type_creator.create(User, { id: :integer })
debate_type = type_creator.create(Debate, %I[ author ]) debate_type = type_creator.create(Debate, { author: User })
connection = debate_type.fields['author'] connection = debate_type.fields['author']
@@ -34,8 +34,8 @@ describe GraphQL::TypeCreator do
end end
it "creates connections for :has_one associations" do it "creates connections for :has_one associations" do
user_type = type_creator.create(User, %I[ organization ]) user_type = type_creator.create(User, { organization: Organization })
organization_type = type_creator.create(Organization, %I[ id ]) organization_type = type_creator.create(Organization, { id: :integer })
connection = user_type.fields['organization'] connection = user_type.fields['organization']
@@ -45,8 +45,8 @@ describe GraphQL::TypeCreator do
end end
it "creates connections for :has_many associations" do it "creates connections for :has_many associations" do
comment_type = type_creator.create(Comment, %I[ id ]) comment_type = type_creator.create(Comment, { id: :integer })
debate_type = type_creator.create(Debate, %I[ comments ]) debate_type = type_creator.create(Debate, { comments: [Comment] })
connection = debate_type.fields['comments'] connection = debate_type.fields['comments']