diff --git a/config/initializers/graphql.rb b/config/initializers/graphql.rb index 971a565e4..55c34ec1f 100644 --- a/config/initializers/graphql.rb +++ b/config/initializers/graphql.rb @@ -12,7 +12,7 @@ api_config.each do |api_type_model, api_type_info| api_type_info['fields'].each do |field_name, field_type| if field_type.is_a?(Array) # paginated association 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 else # simple association 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 } end -type_creator = GraphQL::TypeCreator.new(api_type_definitions) -QueryType = type_creator.query_type +api_types_creator = GraphQL::ApiTypesCreator.new(api_type_definitions) +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 query QueryType diff --git a/lib/graph_ql/api_types_creator.rb b/lib/graph_ql/api_types_creator.rb new file mode 100644 index 000000000..51a7af0e8 --- /dev/null +++ b/lib/graph_ql/api_types_creator.rb @@ -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 diff --git a/lib/graph_ql/query_type_creator.rb b/lib/graph_ql/query_type_creator.rb new file mode 100644 index 000000000..b7bde9f4e --- /dev/null +++ b/lib/graph_ql/query_type_creator.rb @@ -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 diff --git a/lib/graph_ql/type_creator.rb b/lib/graph_ql/type_creator.rb deleted file mode 100644 index b705839c3..000000000 --- a/lib/graph_ql/type_creator.rb +++ /dev/null @@ -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 diff --git a/spec/lib/graph_ql/api_types_creator_spec.rb b/spec/lib/graph_ql/api_types_creator_spec.rb new file mode 100644 index 000000000..10b43ba28 --- /dev/null +++ b/spec/lib/graph_ql/api_types_creator_spec.rb @@ -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 diff --git a/spec/lib/graph_ql/query_type_creator_spec.rb b/spec/lib/graph_ql/query_type_creator_spec.rb new file mode 100644 index 000000000..980c4f1a6 --- /dev/null +++ b/spec/lib/graph_ql/query_type_creator_spec.rb @@ -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 diff --git a/spec/lib/graph_ql/type_creator_spec.rb b/spec/lib/graph_ql/type_creator_spec.rb deleted file mode 100644 index d5291c961..000000000 --- a/spec/lib/graph_ql/type_creator_spec.rb +++ /dev/null @@ -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