diff --git a/.rubocop.yml b/.rubocop.yml index ef60510..2f7b16f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -48,6 +48,8 @@ Style/FrozenStringLiteralComment: Metrics/BlockLength: Exclude: - 'db/schema.rb' + - spec/graphql/**/* + - spec/requests/**/* Style/NumericLiterals: Exclude: diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index ba67df0..9818c34 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -10,9 +10,9 @@ # class GraphqlController < ApplicationController def execute - variables = ensure_hash(params[:variables]) - query = params[:query] - operation_name = params[:operationName] + variables = ensure_hash(graphql_params[:variables]) + query = graphql_params[:query] + operation_name = graphql_params[:operationName] context = { # Query context goes here, for example: # current_user: current_user, @@ -32,6 +32,10 @@ def execute private + def graphql_params + params.permit(:query, :variables, :operationName) + end + # Handle form data, JSON body, or a blank value def ensure_hash(ambiguous_param) case ambiguous_param diff --git a/spec/graphql/mutations/statistics/create_statistic_mutation_spec.rb b/spec/graphql/mutations/statistics/create_statistic_mutation_spec.rb new file mode 100644 index 0000000..4ad546b --- /dev/null +++ b/spec/graphql/mutations/statistics/create_statistic_mutation_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Mutations::Statistics::CreateStatisticMutation do + let(:sourceId) { '12345' } + let(:sourceType) { 'pull_request' } + let(:source) { 'github' } + let(:state) { 'open' } + let(:url) { 'https://api.github.com/repos/org/repo/issues/123' } + let(:title) { 'Some title' } + let(:sourceCreatedAt) { '2018-10-17T20:31:41Z' } + let(:organization) { create :organization } + let(:mutation) do + <<-GRAPHQL + mutation { + createStatistic(attributes: { + sourceId: "#{sourceId}", + sourceType: "#{sourceType}", + source: "#{source}", + state: "#{state}", + organizationId: #{organization.id}, + url: "#{url}", + title: "#{title}", + sourceCreatedAt: "#{sourceCreatedAt}" + }) { + statistic { + source + sourceId + sourceType + state + organizationId + url + title + sourceCreatedAt + id + } + errors + } + } + GRAPHQL + end + + it 'creates a Statistic record' do + expect do + MagnifierSchema.execute(mutation, context: {}) + end.to change { Statistic.count }.from(0).to(1) + end + + it 'creates a Statistic record with the passed attributes', :aggregate_failures do + response = MagnifierSchema.execute(mutation, context: {}) + statistic = Statistic.first + attributes = %w[sourceId sourceType source state url title sourceCreatedAt] + + attributes.each do |attribute| + expect(statistic.send(attribute.underscore)).to eq send(attribute) + end + end +end diff --git a/spec/graphql/queries/organizations/all_organizations_query_spec.rb b/spec/graphql/queries/organizations/all_organizations_query_spec.rb new file mode 100644 index 0000000..bc236cb --- /dev/null +++ b/spec/graphql/queries/organizations/all_organizations_query_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Queries::Organizations::AllOrganizationsQuery do + before { 6.times { create :organization } } + + context 'with no arguments' do + let(:query) do + <<-GRAPHQL + query { + allOrganizations { + url + name + createdAt + updatedAt + } + } + GRAPHQL + end + let(:response) { MagnifierSchema.execute(query, context: {}) } + let(:results) { response.dig('data', 'allOrganizations') } + + it 'returns all organizations in the db' do + expect(results.size).to eq(6) + end + + it 'returns the requested db attributes' do + expect(results.first.keys).to match( + %w[ + url + name + createdAt + updatedAt + ] + ) + end + end + + context 'with an argument of "limit"' do + let(:limited_query) do + <<-GRAPHQL + query { + allOrganizations(limit: 3) { + url + name + } + } + GRAPHQL + end + + it 'limits the response items to the requested limit' do + response = MagnifierSchema.execute limited_query, context: {} + results = response.dig('data', 'allOrganizations') + + expect(results.size).to eq(3) + end + end +end diff --git a/spec/graphql/queries/organizations/organization_query_spec.rb b/spec/graphql/queries/organizations/organization_query_spec.rb new file mode 100644 index 0000000..cffc387 --- /dev/null +++ b/spec/graphql/queries/organizations/organization_query_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Queries::Organizations::OrganizationQuery do + before { 3.times { create :organization } } + + let(:org) { Organization.second } + let(:fields) { %w[id url name] } + + context 'with an argument of "name"' do + let(:query) do + <<-GRAPHQL + query { + organization(name: "#{org.name}") { + id + url + name + } + } + GRAPHQL + end + let(:response) { MagnifierSchema.execute(query, context: {}) } + let(:results) { response.dig('data', 'organization') } + + it 'returns the expected organization', :aggregate_failures do + fields.each do |field| + expect(results[field]).to eq org.send(field) + end + end + end + + context 'with an argument of "id"' do + let(:query) do + <<-GRAPHQL + query { + organization(id: "#{org.id}") { + id + url + name + } + } + GRAPHQL + end + let(:response) { MagnifierSchema.execute(query, context: {}) } + let(:results) { response.dig('data', 'organization') } + + it 'returns the expected organization', :aggregate_failures do + fields.each do |field| + expect(results[field]).to eq org.send(field) + end + end + end +end diff --git a/spec/requests/graphql_request_spec.rb b/spec/requests/graphql_request_spec.rb new file mode 100644 index 0000000..87f2d13 --- /dev/null +++ b/spec/requests/graphql_request_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'GraphQL requests', type: :request do + describe '/graphql' do + before { 3.times { create :organization } } + + let(:query) do + <<-GRAPHQL + query { + allOrganizations { + url + name + createdAt + updatedAt + } + } + GRAPHQL + end + + it 'queries our GraphQL schema and returns the expected response', :aggregated_failures do + post '/graphql', params: { query: query } + + body = JSON.parse(response.body) + results = body.dig('data', 'allOrganizations') + + expect(results.length).to eq 3 + expect(results.first.keys).to match( + %w[ + url + name + createdAt + updatedAt + ] + ) + end + + context 'with a malformed query' do + let(:bad_query) do + <<-GRAPHQL + query { + allOrganizations { + } + } + GRAPHQL + end + + it 'surfaces the relevant error', :aggregated_failures do + post '/graphql', params: { query: bad_query } + + body = JSON.parse(response.body) + errors = body.dig('errors') + message = errors.first['message'] + + expect(errors).to be_present + expect(message).to be_present + end + end + end +end