Added support for associations (WITHOUT PAGINATION)
This commit is contained in:
8
app/graph/exposable_association.rb
Normal file
8
app/graph/exposable_association.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class ExposableAssociation
|
||||
attr_reader :name, :type
|
||||
|
||||
def initialize(association)
|
||||
@name = association.name
|
||||
@type = association.macro # :has_one, :belongs_to or :has_many
|
||||
end
|
||||
end
|
||||
@@ -1,30 +1,35 @@
|
||||
class ExposableModel
|
||||
attr_reader :exposed_fields, :name, :description
|
||||
attr_reader :name, :description, :exposed_fields, :exposed_associations
|
||||
|
||||
def initialize(model_class, options = {})
|
||||
@model_class = model_class
|
||||
@filter_list = options[:filter_list] || []
|
||||
@name = model_class.name
|
||||
@description = model_class.model_name.human
|
||||
@field_filter_list = options[:field_filter_list] || []
|
||||
@assoc_filter_list = options[:assoc_filter_list] || []
|
||||
@filter_strategy = options[:filter_strategy]
|
||||
set_exposed_fields
|
||||
set_exposed_items(model_class)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# determine which model fields are exposed to the API
|
||||
def set_exposed_fields
|
||||
# determine which model fields and associations are exposed to the API
|
||||
def set_exposed_items(model_class)
|
||||
@exposed_fields = check_against_safety_list(model_class.columns, @field_filter_list)
|
||||
@exposed_associations = check_against_safety_list(model_class.reflect_on_all_associations, @assoc_filter_list)
|
||||
end
|
||||
|
||||
def check_against_safety_list(all_items, safety_list)
|
||||
case @filter_strategy
|
||||
when :whitelist
|
||||
@exposed_fields = @model_class.columns.select do |column|
|
||||
@filter_list.include? column.name
|
||||
exposed_items = all_items.select do |column|
|
||||
safety_list.include? column.name.to_s # works for both symbols and strings
|
||||
end
|
||||
when :blacklist
|
||||
@exposed_fields = @model_class.columns.select do |column|
|
||||
!(@filter_list.include? column.name)
|
||||
exposed_items = all_items.select do |column|
|
||||
!(safety_list.include? column.name.to_s)
|
||||
end
|
||||
else
|
||||
@exposed_fields = []
|
||||
exposed_items = []
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class TypeBuilder
|
||||
attr_reader :filter_strategy, :graphql_models
|
||||
attr_accessor :graphql_types # contains all generated GraphQL types
|
||||
|
||||
def initialize(graphql_models, options = {})
|
||||
@graphql_models = graphql_models
|
||||
@@ -31,14 +32,30 @@ private
|
||||
type_builder = self
|
||||
|
||||
graphql_type = GraphQL::ObjectType.define do
|
||||
em = ExposableModel.new(model_class, filter_strategy: type_builder.filter_strategy, filter_list: type_builder.graphql_models[model_class])
|
||||
em = ExposableModel.new(
|
||||
model_class,
|
||||
filter_strategy: type_builder.filter_strategy,
|
||||
field_filter_list: type_builder.graphql_models[model_class][:fields],
|
||||
assoc_filter_list: type_builder.graphql_models[model_class][:associations]
|
||||
)
|
||||
|
||||
name(em.name)
|
||||
description(em.description)
|
||||
|
||||
em.exposed_fields.each do |column|
|
||||
ef = ExposableField.new(column)
|
||||
field(ef.name, ef.graphql_type)
|
||||
field(ef.name, ef.graphql_type) # returns a GraphQL::Field
|
||||
end
|
||||
|
||||
em.exposed_associations.each do |association|
|
||||
ea = ExposableAssociation.new(association)
|
||||
if ea.type.in? [:has_one, :belongs_to]
|
||||
field(ea.name, -> { type_builder.graphql_types[association.klass] })
|
||||
elsif ea.type.in? [:has_many]
|
||||
field(ea.name, -> {
|
||||
types[type_builder.graphql_types[association.klass]]
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,10 +65,25 @@ end
|
||||
|
||||
graphql_models = {}
|
||||
|
||||
graphql_models.store(User, ['id', 'username'])
|
||||
graphql_models.store(Proposal, ['id', 'title', 'description', 'author_id', 'created_at'])
|
||||
graphql_models.store(Debate, ['id', 'title', 'description', 'author_id', 'created_at'])
|
||||
graphql_models.store(Comment, ['id', 'commentable_id', 'commentable_type', 'body'])
|
||||
graphql_models.store(Geozone, ['id', 'name', 'html_map_coordinates'])
|
||||
graphql_models.store(User, {
|
||||
fields: ['id', 'username'],
|
||||
associations: ['proposals', 'debates']
|
||||
})
|
||||
graphql_models.store(Proposal, {
|
||||
fields: ['id', 'title', 'description', 'author_id', 'created_at'],
|
||||
associations: ['author']
|
||||
})
|
||||
graphql_models.store(Debate, {
|
||||
fields: ['id', 'title', 'description', 'author_id', 'created_at'],
|
||||
associations: ['author']
|
||||
})
|
||||
graphql_models.store(Comment, {
|
||||
fields: ['id', 'commentable_id', 'commentable_type', 'body'],
|
||||
associations: ['author']
|
||||
})
|
||||
graphql_models.store(Geozone, {
|
||||
fields: ['id', 'name', 'html_map_coordinates'],
|
||||
associations: []
|
||||
})
|
||||
|
||||
TYPE_BUILDER = TypeBuilder.new(graphql_models, filter_strategy: :whitelist)
|
||||
|
||||
Reference in New Issue
Block a user