Commit aa7b1cfc authored by Brett Walker's avatar Brett Walker Committed by Mayra Cabrera

Upgrade GraphQL gem to 1.8.17

- Due to https://github.com/exAspArk/batch-loader/pull/32,
we  changed BatchLoader.for into BatchLoader::GraphQL.for
- since our results are wrapped in a BatchLoader::GraphQL,
calling `sync` during authorization is required to get real object
- `graphql` now has it's own authorization system.  Our
`authorized?` method conflicted and required renaming
parent 29e3a08b
...@@ -83,7 +83,7 @@ gem 'grape-entity', '~> 0.7.1' ...@@ -83,7 +83,7 @@ gem 'grape-entity', '~> 0.7.1'
gem 'rack-cors', '~> 1.0.0', require: 'rack/cors' gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'
# GraphQL API # GraphQL API
gem 'graphql', '= 1.8.4' gem 'graphql', '= 1.8.17'
gem 'graphiql-rails', '~> 1.4.10' gem 'graphiql-rails', '~> 1.4.10'
gem 'apollo_upload_server', '~> 2.0.0.beta3' gem 'apollo_upload_server', '~> 2.0.0.beta3'
gem 'graphql-docs', '~> 1.6.0', group: [:development, :test] gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
......
...@@ -377,7 +377,7 @@ GEM ...@@ -377,7 +377,7 @@ GEM
graphiql-rails (1.4.10) graphiql-rails (1.4.10)
railties railties
sprockets-rails sprockets-rails
graphql (1.8.4) graphql (1.8.17)
graphql-docs (1.6.0) graphql-docs (1.6.0)
commonmarker (~> 0.16) commonmarker (~> 0.16)
escape_utils (~> 1.2) escape_utils (~> 1.2)
...@@ -1110,7 +1110,7 @@ DEPENDENCIES ...@@ -1110,7 +1110,7 @@ DEPENDENCIES
grape-path-helpers (~> 1.1) grape-path-helpers (~> 1.1)
grape_logging (~> 1.7) grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10) graphiql-rails (~> 1.4.10)
graphql (= 1.8.4) graphql (= 1.8.17)
graphql-docs (~> 1.6.0) graphql-docs (~> 1.6.0)
grpc (~> 1.19.0) grpc (~> 1.19.0)
haml_lint (~> 0.31.0) haml_lint (~> 0.31.0)
......
...@@ -18,8 +18,6 @@ module Mutations ...@@ -18,8 +18,6 @@ module Mutations
required: true, required: true,
description: copy_field_description(Types::Notes::NoteType, :body) description: copy_field_description(Types::Notes::NoteType, :body)
private
def resolve(args) def resolve(args)
noteable = authorized_find!(id: args[:noteable_id]) noteable = authorized_find!(id: args[:noteable_id])
...@@ -37,6 +35,8 @@ module Mutations ...@@ -37,6 +35,8 @@ module Mutations
} }
end end
private
def create_note_params(noteable, args) def create_note_params(noteable, args)
{ {
noteable: noteable, noteable: noteable,
......
...@@ -11,7 +11,7 @@ module Resolvers ...@@ -11,7 +11,7 @@ module Resolvers
end end
def model_by_full_path(model, full_path) def model_by_full_path(model, full_path)
BatchLoader.for(full_path).batch(key: model) do |full_paths, loader, args| BatchLoader::GraphQL.for(full_path).batch(key: model) do |full_paths, loader, args|
# `with_route` avoids an N+1 calculating full_path # `with_route` avoids an N+1 calculating full_path
args[:key].where_full_path_in(full_paths).with_route.each do |model_instance| args[:key].where_full_path_in(full_paths).with_route.each do |model_instance|
loader.call(model_instance.full_path, model_instance) loader.call(model_instance.full_path, model_instance)
......
...@@ -41,13 +41,11 @@ module Resolvers ...@@ -41,13 +41,11 @@ module Resolvers
type Types::IssueType, null: true type Types::IssueType, null: true
alias_method :project, :object
def resolve(**args) def resolve(**args)
# The project could have been loaded in batch by `BatchLoader`. # The project could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` of the project to query for issues, so # At this point we need the `id` of the project to query for issues, so
# make sure it's loaded and not `nil` before continuing. # make sure it's loaded and not `nil` before continuing.
project.sync if project.respond_to?(:sync) project = object.respond_to?(:sync) ? object.sync : object
return Issue.none if project.nil? return Issue.none if project.nil?
# Will need to be be made group & namespace aware with # Will need to be be made group & namespace aware with
......
...@@ -25,8 +25,10 @@ module Resolvers ...@@ -25,8 +25,10 @@ module Resolvers
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def batch_load(iid) def batch_load(iid)
BatchLoader.for(iid.to_s).batch(key: project) do |iids, loader, args| BatchLoader::GraphQL.for(iid.to_s).batch(key: project) do |iids, loader, args|
args[:key].merge_requests.where(iid: iids).each do |mr| arg_key = args[:key].respond_to?(:sync) ? args[:key].sync : args[:key]
arg_key.merge_requests.where(iid: iids).each do |mr|
loader.call(mr.iid.to_s, mr) loader.call(mr.iid.to_s, mr)
end end
end end
......
...@@ -9,13 +9,11 @@ module Resolvers ...@@ -9,13 +9,11 @@ module Resolvers
type Types::ProjectType, null: true type Types::ProjectType, null: true
alias_method :namespace, :object
def resolve(include_subgroups:) def resolve(include_subgroups:)
# The namespace could have been loaded in batch by `BatchLoader`. # The namespace could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` or the `full_path` of the namespace # At this point we need the `id` or the `full_path` of the namespace
# to query for projects, so make sure it's loaded and not `nil` before continuing. # to query for projects, so make sure it's loaded and not `nil` before continuing.
namespace.sync if namespace.respond_to?(:sync) namespace = object.respond_to?(:sync) ? object.sync : object
return Project.none if namespace.nil? return Project.none if namespace.nil?
if include_subgroups if include_subgroups
......
...@@ -54,14 +54,14 @@ module Gitlab ...@@ -54,14 +54,14 @@ module Gitlab
# The field is a built-in/scalar type, or a list of scalars # The field is a built-in/scalar type, or a list of scalars
# authorize using the parent's object # authorize using the parent's object
parent_typed_object.object parent_typed_object.object
elsif resolved_type.respond_to?(:object)
# The field is a type representing a single object, we'll authorize
# against the object directly
resolved_type.object
elsif @field.connection? || resolved_type.is_a?(Array) elsif @field.connection? || resolved_type.is_a?(Array)
# The field is a connection or a list of non-built-in types, we'll # The field is a connection or a list of non-built-in types, we'll
# authorize each element when rendering # authorize each element when rendering
nil nil
elsif resolved_type.respond_to?(:object)
# The field is a type representing a single object, we'll authorize
# against the object directly
resolved_type.object
else else
# Resolved type is a single object that might not be loaded yet by # Resolved type is a single object that might not be loaded yet by
# the batchloader, we'll authorize that # the batchloader, we'll authorize that
......
...@@ -29,19 +29,25 @@ module Gitlab ...@@ -29,19 +29,25 @@ module Gitlab
def authorized_find!(*args) def authorized_find!(*args)
object = find_object(*args) object = find_object(*args)
object = object.sync if object.respond_to?(:sync)
authorize!(object) authorize!(object)
object object
end end
def authorize!(object) def authorize!(object)
unless authorized?(object) unless authorized_resource?(object)
raise Gitlab::Graphql::Errors::ResourceNotAvailable, raise Gitlab::Graphql::Errors::ResourceNotAvailable,
"The resource that you are attempting to access does not exist or you don't have permission to perform this action" "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
end end
end end
def authorized?(object) # this was named `#authorized?`, however it conflicts with the native
# graphql gem version
# TODO consider adopting the gem's built in authorization system
# https://gitlab.com/gitlab-org/gitlab-ee/issues/13984
def authorized_resource?(object)
# Sanity check. We don't want to accidentally allow a developer to authorize # Sanity check. We don't want to accidentally allow a developer to authorize
# without first adding permissions to authorize against # without first adding permissions to authorize against
if self.class.required_permissions.empty? if self.class.required_permissions.empty?
......
...@@ -9,7 +9,7 @@ module Gitlab ...@@ -9,7 +9,7 @@ module Gitlab
end end
def find def find
BatchLoader.for(blob_id).batch(key: repository) do |blob_ids, loader, batch_args| BatchLoader::GraphQL.for(blob_id).batch(key: repository) do |blob_ids, loader, batch_args|
Gitlab::Git::Blob.batch_lfs_pointers(batch_args[:key], blob_ids).each do |loaded_blob| Gitlab::Git::Blob.batch_lfs_pointers(batch_args[:key], blob_ids).each do |loaded_blob|
loader.call(loaded_blob.id, loaded_blob.lfs_oid) loader.call(loaded_blob.id, loaded_blob.lfs_oid)
end end
......
...@@ -12,7 +12,7 @@ module Gitlab ...@@ -12,7 +12,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def find def find
BatchLoader.for({ model: model_class, id: model_id.to_i }).batch do |loader_info, loader| BatchLoader::GraphQL.for({ model: model_class, id: model_id.to_i }).batch do |loader_info, loader|
per_model = loader_info.group_by { |info| info[:model] } per_model = loader_info.group_by { |info| info[:model] }
per_model.each do |model, info| per_model.each do |model, info|
ids = info.map { |i| i[:id] } ids = info.map { |i| i[:id] }
......
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
end end
def find def find
BatchLoader.for(project_id).batch do |project_ids, loader| BatchLoader::GraphQL.for(project_id).batch do |project_ids, loader|
ProjectStatistics.for_project_ids(project_ids).each do |statistics| ProjectStatistics.for_project_ids(project_ids).each do |statistics|
loader.call(statistics.project_id, statistics) loader.call(statistics.project_id, statistics)
end end
......
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
end end
def find def find
BatchLoader.for(namespace_id).batch do |namespace_ids, loader| BatchLoader::GraphQL.for(namespace_id).batch do |namespace_ids, loader|
Namespace::RootStorageStatistics.for_namespace_ids(namespace_ids).each do |statistics| Namespace::RootStorageStatistics.for_namespace_ids(namespace_ids).each do |statistics|
loader.call(statistics.namespace_id, statistics) loader.call(statistics.namespace_id, statistics)
end end
......
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
end end
def find_last def find_last
BatchLoader.for(sha).batch(key: project) do |shas, loader, args| BatchLoader::GraphQL.for(sha).batch(key: project) do |shas, loader, args|
pipelines = args[:key].ci_pipelines.latest_for_shas(shas) pipelines = args[:key].ci_pipelines.latest_for_shas(shas)
pipelines.each do |pipeline| pipelines.each do |pipeline|
......
...@@ -129,7 +129,7 @@ describe GitlabSchema do ...@@ -129,7 +129,7 @@ describe GitlabSchema do
result = described_class.object_from_id(user.to_global_id.to_s) result = described_class.object_from_id(user.to_global_id.to_s)
expect(result.__sync).to eq(user) expect(result.sync).to eq(user)
end end
it 'batchloads the queries' do it 'batchloads the queries' do
...@@ -138,7 +138,7 @@ describe GitlabSchema do ...@@ -138,7 +138,7 @@ describe GitlabSchema do
expect do expect do
[described_class.object_from_id(user1.to_global_id), [described_class.object_from_id(user1.to_global_id),
described_class.object_from_id(user2.to_global_id)].map(&:__sync) described_class.object_from_id(user2.to_global_id)].map(&:sync)
end.not_to exceed_query_limit(1) end.not_to exceed_query_limit(1)
end end
end end
...@@ -149,7 +149,7 @@ describe GitlabSchema do ...@@ -149,7 +149,7 @@ describe GitlabSchema do
result = described_class.object_from_id(note.to_global_id) result = described_class.object_from_id(note.to_global_id)
expect(result.__sync).to eq(note) expect(result.sync).to eq(note)
end end
it 'batchloads the queries' do it 'batchloads the queries' do
...@@ -158,7 +158,7 @@ describe GitlabSchema do ...@@ -158,7 +158,7 @@ describe GitlabSchema do
expect do expect do
[described_class.object_from_id(note1.to_global_id), [described_class.object_from_id(note1.to_global_id),
described_class.object_from_id(note2.to_global_id)].map(&:__sync) described_class.object_from_id(note2.to_global_id)].map(&:sync)
end.not_to exceed_query_limit(1) end.not_to exceed_query_limit(1)
end end
end end
......
...@@ -14,6 +14,6 @@ describe Mutations::ResolvesProject do ...@@ -14,6 +14,6 @@ describe Mutations::ResolvesProject do
project = create(:project) project = create(:project)
expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context).and_call_original expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context).and_call_original
expect(mutation.resolve_project(full_path: project.full_path)).to eq(project) expect(mutation.resolve_project(full_path: project.full_path).sync).to eq(project)
end end
end end
...@@ -12,7 +12,7 @@ describe Resolvers::GroupResolver do ...@@ -12,7 +12,7 @@ describe Resolvers::GroupResolver do
it 'batch-resolves groups by full path' do it 'batch-resolves groups by full path' do
paths = [group1.full_path, group2.full_path] paths = [group1.full_path, group2.full_path]
result = batch(max_queries: 1) do result = batch_sync(max_queries: 1) do
paths.map { |path| resolve_group(path) } paths.map { |path| resolve_group(path) }
end end
...@@ -20,7 +20,7 @@ describe Resolvers::GroupResolver do ...@@ -20,7 +20,7 @@ describe Resolvers::GroupResolver do
end end
it 'resolves an unknown full_path to nil' do it 'resolves an unknown full_path to nil' do
result = batch { resolve_group('unknown/project') } result = batch_sync { resolve_group('unknown/project') }
expect(result).to be_nil expect(result).to be_nil
end end
......
...@@ -110,7 +110,7 @@ describe Resolvers::IssuesResolver do ...@@ -110,7 +110,7 @@ describe Resolvers::IssuesResolver do
context "when passing a non existent, batch loaded project" do context "when passing a non existent, batch loaded project" do
let(:project) do let(:project) do
BatchLoader.for("non-existent-path").batch do |_fake_paths, loader, _| BatchLoader::GraphQL.for("non-existent-path").batch do |_fake_paths, loader, _|
loader.call("non-existent-path", nil) loader.call("non-existent-path", nil)
end end
end end
......
...@@ -17,7 +17,7 @@ describe Resolvers::MergeRequestsResolver do ...@@ -17,7 +17,7 @@ describe Resolvers::MergeRequestsResolver do
describe '#resolve' do describe '#resolve' do
it 'batch-resolves by target project full path and individual IID' do it 'batch-resolves by target project full path and individual IID' do
result = batch(max_queries: 2) do result = batch_sync(max_queries: 2) do
resolve_mr(project, iid: iid_1) + resolve_mr(project, iid: iid_2) resolve_mr(project, iid: iid_1) + resolve_mr(project, iid: iid_2)
end end
...@@ -25,7 +25,7 @@ describe Resolvers::MergeRequestsResolver do ...@@ -25,7 +25,7 @@ describe Resolvers::MergeRequestsResolver do
end end
it 'batch-resolves by target project full path and IIDS' do it 'batch-resolves by target project full path and IIDS' do
result = batch(max_queries: 2) do result = batch_sync(max_queries: 2) do
resolve_mr(project, iids: [iid_1, iid_2]) resolve_mr(project, iids: [iid_1, iid_2])
end end
...@@ -33,7 +33,7 @@ describe Resolvers::MergeRequestsResolver do ...@@ -33,7 +33,7 @@ describe Resolvers::MergeRequestsResolver do
end end
it 'can batch-resolve merge requests from different projects' do it 'can batch-resolve merge requests from different projects' do
result = batch(max_queries: 3) do result = batch_sync(max_queries: 3) do
resolve_mr(project, iid: iid_1) + resolve_mr(project, iid: iid_1) +
resolve_mr(project, iid: iid_2) + resolve_mr(project, iid: iid_2) +
resolve_mr(other_project, iid: other_iid) resolve_mr(other_project, iid: other_iid)
...@@ -43,13 +43,13 @@ describe Resolvers::MergeRequestsResolver do ...@@ -43,13 +43,13 @@ describe Resolvers::MergeRequestsResolver do
end end
it 'resolves an unknown iid to be empty' do it 'resolves an unknown iid to be empty' do
result = batch { resolve_mr(project, iid: -1) } result = batch_sync { resolve_mr(project, iid: -1) }
expect(result).to be_empty expect(result.compact).to be_empty
end end
it 'resolves empty iids to be empty' do it 'resolves empty iids to be empty' do
result = batch { resolve_mr(project, iids: []) } result = batch_sync { resolve_mr(project, iids: []) }
expect(result).to be_empty expect(result).to be_empty
end end
......
...@@ -46,7 +46,7 @@ describe Resolvers::NamespaceProjectsResolver do ...@@ -46,7 +46,7 @@ describe Resolvers::NamespaceProjectsResolver do
context "when passing a non existent, batch loaded namespace" do context "when passing a non existent, batch loaded namespace" do
let(:namespace) do let(:namespace) do
BatchLoader.for("non-existent-path").batch do |_fake_paths, loader, _| BatchLoader::GraphQL.for("non-existent-path").batch do |_fake_paths, loader, _|
loader.call("non-existent-path", nil) loader.call("non-existent-path", nil)
end end
end end
......
...@@ -12,7 +12,7 @@ describe Resolvers::ProjectResolver do ...@@ -12,7 +12,7 @@ describe Resolvers::ProjectResolver do
it 'batch-resolves projects by full path' do it 'batch-resolves projects by full path' do
paths = [project1.full_path, project2.full_path] paths = [project1.full_path, project2.full_path]
result = batch(max_queries: 1) do result = batch_sync(max_queries: 1) do
paths.map { |path| resolve_project(path) } paths.map { |path| resolve_project(path) }
end end
...@@ -20,7 +20,7 @@ describe Resolvers::ProjectResolver do ...@@ -20,7 +20,7 @@ describe Resolvers::ProjectResolver do
end end
it 'resolves an unknown full_path to nil' do it 'resolves an unknown full_path to nil' do
result = batch { resolve_project('unknown/project') } result = batch_sync { resolve_project('unknown/project') }
expect(result).to be_nil expect(result).to be_nil
end end
......
...@@ -46,9 +46,9 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do ...@@ -46,9 +46,9 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do
end end
end end
describe '#authorized?' do describe '#authorized_resource?' do
it 'is true' do it 'is true' do
expect(loading_resource.authorized?(project)).to be(true) expect(loading_resource.authorized_resource?(project)).to be(true)
end end
end end
end end
...@@ -72,9 +72,9 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do ...@@ -72,9 +72,9 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do
end end
end end
describe '#authorized?' do describe '#authorized_resource?' do
it 'is false' do it 'is false' do
expect(loading_resource.authorized?(project)).to be(false) expect(loading_resource.authorized_resource?(project)).to be(false)
end end
end end
end end
...@@ -121,9 +121,9 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do ...@@ -121,9 +121,9 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do
end end
end end
describe '#authorized?' do describe '#authorized_resource?' do
it 'raises a comprehensive error message' do it 'raises a comprehensive error message' do
expect { loading_resource.authorized?(project) }.to raise_error(error) expect { loading_resource.authorized_resource?(project) }.to raise_error(error)
end end
end end
end end
......
...@@ -12,7 +12,7 @@ describe Gitlab::Graphql::Loaders::BatchLfsOidLoader do ...@@ -12,7 +12,7 @@ describe Gitlab::Graphql::Loaders::BatchLfsOidLoader do
it 'batch-resolves LFS blob IDs' do it 'batch-resolves LFS blob IDs' do
expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).once.and_call_original expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).once.and_call_original
result = batch do result = batch_sync do
[blob, otherblob].map { |b| described_class.new(repository, b.id).find } [blob, otherblob].map { |b| described_class.new(repository, b.id).find }
end end
......
...@@ -9,8 +9,8 @@ describe Gitlab::Graphql::Loaders::BatchModelLoader do ...@@ -9,8 +9,8 @@ describe Gitlab::Graphql::Loaders::BatchModelLoader do
issue_result = described_class.new(Issue, issue.id).find issue_result = described_class.new(Issue, issue.id).find
user_result = described_class.new(User, user.id).find user_result = described_class.new(User, user.id).find
expect(issue_result.__sync).to eq(issue) expect(issue_result.sync).to eq(issue)
expect(user_result.__sync).to eq(user) expect(user_result.sync).to eq(user)
end end
it 'only queries once per model' do it 'only queries once per model' do
...@@ -21,7 +21,7 @@ describe Gitlab::Graphql::Loaders::BatchModelLoader do ...@@ -21,7 +21,7 @@ describe Gitlab::Graphql::Loaders::BatchModelLoader do
expect do expect do
[described_class.new(User, other_user.id).find, [described_class.new(User, other_user.id).find,
described_class.new(User, user.id).find, described_class.new(User, user.id).find,
described_class.new(Issue, issue.id).find].map(&:__sync) described_class.new(Issue, issue.id).find].map(&:sync)
end.not_to exceed_query_limit(2) end.not_to exceed_query_limit(2)
end end
end end
......
...@@ -10,7 +10,7 @@ describe Gitlab::Graphql::Loaders::PipelineForShaLoader do ...@@ -10,7 +10,7 @@ describe Gitlab::Graphql::Loaders::PipelineForShaLoader do
pipeline2 = create(:ci_pipeline, project: project, ref: project.default_branch, sha: project.commit.sha) pipeline2 = create(:ci_pipeline, project: project, ref: project.default_branch, sha: project.commit.sha)
pipeline3 = create(:ci_pipeline, project: project, ref: 'improve/awesome', sha: project.commit('improve/awesome').sha) pipeline3 = create(:ci_pipeline, project: project, ref: 'improve/awesome', sha: project.commit('improve/awesome').sha)
result = batch(max_queries: 1) do result = batch_sync(max_queries: 1) do
[pipeline1.sha, pipeline3.sha].map { |sha| described_class.new(project, sha).find_last } [pipeline1.sha, pipeline3.sha].map { |sha| described_class.new(project, sha).find_last }
end end
......
...@@ -13,7 +13,7 @@ describe 'Setting WIP status of a merge request' do ...@@ -13,7 +13,7 @@ describe 'Setting WIP status of a merge request' do
project_path: project.full_path, project_path: project.full_path,
iid: merge_request.iid.to_s iid: merge_request.iid.to_s
} }
graphql_mutation(:merge_request_set_wip, variables.merge(input)) graphql_mutation(:merge_request_set_wip, variables.merge(input), "clientMutationId\nerrors\nmergeRequest { id\ntitle }")
end end
def mutation_response def mutation_response
......
...@@ -34,6 +34,14 @@ module GraphqlHelpers ...@@ -34,6 +34,14 @@ module GraphqlHelpers
end end
end end
# BatchLoader::GraphQL returns a wrapper, so we need to :sync in order
# to get the actual values
def batch_sync(max_queries: nil, &blk)
result = batch(max_queries: nil, &blk)
result.is_a?(Array) ? result.map(&:sync) : result&.sync
end
def graphql_query_for(name, attributes = {}, fields = nil) def graphql_query_for(name, attributes = {}, fields = nil)
<<~QUERY <<~QUERY
{ {
...@@ -114,7 +122,11 @@ module GraphqlHelpers ...@@ -114,7 +122,11 @@ module GraphqlHelpers
FIELDS FIELDS
end end
def all_graphql_fields_for(class_name, parent_types = Set.new) def all_graphql_fields_for(class_name, parent_types = Set.new, max_depth: 3)
# pulling _all_ fields can generate a _huge_ query (like complexity 180,000),
# and significantly increase spec runtime. so limit the depth by default
return if max_depth <= 0
allow_unlimited_graphql_complexity allow_unlimited_graphql_complexity
allow_unlimited_graphql_depth allow_unlimited_graphql_depth
...@@ -133,9 +145,9 @@ module GraphqlHelpers ...@@ -133,9 +145,9 @@ module GraphqlHelpers
if nested_fields?(field) if nested_fields?(field)
fields = fields =
all_graphql_fields_for(singular_field_type, parent_types | [type]) all_graphql_fields_for(singular_field_type, parent_types | [type], max_depth: max_depth - 1)
"#{name} { #{fields} }" "#{name} { #{fields} }" unless fields.blank?
else else
name name
end end
......
...@@ -46,7 +46,7 @@ shared_context 'exposing regular notes on a noteable in GraphQL' do ...@@ -46,7 +46,7 @@ shared_context 'exposing regular notes on a noteable in GraphQL' do
discussions { discussions {
edges { edges {
node { node {
#{all_graphql_fields_for('Discussion')} #{all_graphql_fields_for('Discussion', max_depth: 4)}
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment