diff --git a/app/graph/exposable_field.rb b/app/graph/exposable_field.rb new file mode 100644 index 000000000..2b54083a7 --- /dev/null +++ b/app/graph/exposable_field.rb @@ -0,0 +1,26 @@ +class ExposableField + attr_reader :name, :graphql_type + + def initialize(column, options = {}) + @name = column.name + @graphql_type = ExposableField.convert_type(column.type) + end + + private + + # Return a GraphQL type for 'database_type' + def self.convert_type(database_type) + case database_type + when :integer + GraphQL::INT_TYPE + when :boolean + GraphQL::BOOLEAN_TYPE + when :float + GraphQL::FLOAT_TYPE + when :double + GraphQL::FLOAT_TYPE + else + GraphQL::STRING_TYPE + end + end +end diff --git a/app/graph/exposable_model.rb b/app/graph/exposable_model.rb new file mode 100644 index 000000000..9e4b228ad --- /dev/null +++ b/app/graph/exposable_model.rb @@ -0,0 +1,31 @@ +class ExposableModel + attr_reader :exposed_fields, :name, :description + + def initialize(model_class, options = {}) + @model_class = model_class + @filter_list = options[:filter_list] || [] + @name = model_class.name + @description = model_class.model_name.human + @filter_strategy = options[:filter_strategy] + set_exposed_fields + end + + private + + # determine which model fields are exposed to the API + def set_exposed_fields + case @filter_strategy + when :whitelist + @exposed_fields = @model_class.columns.select do |column| + @filter_list.include? column.name + end + when :blacklist + @exposed_fields = @model_class.columns.select do |column| + !(@filter_list.include? column.name) + end + else + @exposed_fields = [] + end + end + +end diff --git a/app/graph/types/query_root.rb b/app/graph/query_root.rb similarity index 77% rename from app/graph/types/query_root.rb rename to app/graph/query_root.rb index 0c3ed66b9..928dceb6a 100644 --- a/app/graph/types/query_root.rb +++ b/app/graph/query_root.rb @@ -3,7 +3,7 @@ QueryRoot = GraphQL::ObjectType.define do description "The query root for this schema" field :proposal do - type ProposalType + type TYPE_BUILDER.types[Proposal] description "Find a Proposal by id" argument :id, !types.ID resolve -> (object, arguments, context) { @@ -11,7 +11,8 @@ QueryRoot = GraphQL::ObjectType.define do } end - connection :proposals, ProposalType.connection_type do + field :proposals do + type types[TYPE_BUILDER.types[Proposal]] description "Find all Proposals" resolve -> (object, arguments, context) { Proposal.all @@ -19,7 +20,7 @@ QueryRoot = GraphQL::ObjectType.define do end field :debate do - type DebateType + type TYPE_BUILDER.types[Debate] description "Find a Debate by id" argument :id, !types.ID resolve -> (object, arguments, context) { @@ -27,7 +28,8 @@ QueryRoot = GraphQL::ObjectType.define do } end - connection :debates, DebateType.connection_type do + field :debates do + type types[TYPE_BUILDER.types[Debate]] description "Find all Debates" resolve -> (object, arguments, context) { Debate.all @@ -35,7 +37,7 @@ QueryRoot = GraphQL::ObjectType.define do end field :comment do - type CommentType + type TYPE_BUILDER.types[Comment] description "Find a Comment by id" argument :id, !types.ID resolve -> (object, arguments, context) { @@ -43,7 +45,8 @@ QueryRoot = GraphQL::ObjectType.define do } end - connection :comments, CommentType.connection_type do + field :comments do + type types[TYPE_BUILDER.types[Comment]] description "Find all Comments" resolve -> (object, arguments, context) { Comment.all @@ -51,7 +54,7 @@ QueryRoot = GraphQL::ObjectType.define do end field :user do - type UserType + type TYPE_BUILDER.types[User] description "Find a User by id" argument :id, !types.ID resolve -> (object, arguments, context) { diff --git a/app/graph/type_builder.rb b/app/graph/type_builder.rb new file mode 100644 index 000000000..7de5bfd23 --- /dev/null +++ b/app/graph/type_builder.rb @@ -0,0 +1,57 @@ +class TypeBuilder + attr_reader :filter_strategy, :graphql_models + + def initialize(graphql_models, options = {}) + @graphql_models = graphql_models + @graphql_types = {} + + # determine filter strategy for this field + if (options[:filter_strategy] == :blacklist) + @filter_strategy = :blacklist + else + @filter_strategy = :whitelist + end + + create_all_types + end + + def types + @graphql_types + end + +private + + def create_all_types + @graphql_models.keys.each do |model_class| + @graphql_types[model_class] = create_type(model_class) + end + end + + def create_type(model_class) + 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]) + + name(em.name) + description(em.description) + + em.exposed_fields.each do |column| + ef = ExposableField.new(column) + field(ef.name, ef.graphql_type) + end + end + + return graphql_type + end +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']) + +TYPE_BUILDER = TypeBuilder.new(graphql_models, filter_strategy: :whitelist) diff --git a/config/application.rb b/config/application.rb index 3182e319c..623afc4f1 100644 --- a/config/application.rb +++ b/config/application.rb @@ -46,7 +46,7 @@ module Consul config.paths['app/views'].unshift(Rails.root.join('app', 'views', 'custom')) # Add GraphQL directories to the autoload path - config.autoload_paths << Rails.root.join('app', 'graph', 'types') + config.autoload_paths << Rails.root.join('app', 'graph') end end