Read GraphQL field types from file instead of from database
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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']
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user