Skip to content

Commit

Permalink
Upgrade GraphQL gem to 1.8.17
Browse files Browse the repository at this point in the history
- Due to exAspArk/batch-loader#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
  • Loading branch information
Brett Walker committed Aug 27, 2019
1 parent 4738ef6 commit 59c9de4
Show file tree
Hide file tree
Showing 27 changed files with 67 additions and 54 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ gem 'grape-entity', '~> 0.7.1'
gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'

# GraphQL API
gem 'graphql', '= 1.8.4'
gem 'graphql', '= 1.8.17'
gem 'graphiql-rails', '~> 1.4.10'
gem 'apollo_upload_server', '~> 2.0.0.beta3'
gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ GEM
graphiql-rails (1.4.10)
railties
sprockets-rails
graphql (1.8.4)
graphql (1.8.17)
graphql-docs (1.6.0)
commonmarker (~> 0.16)
escape_utils (~> 1.2)
Expand Down Expand Up @@ -1109,7 +1109,7 @@ DEPENDENCIES
grape-path-helpers (~> 1.1)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (= 1.8.4)
graphql (= 1.8.17)
graphql-docs (~> 1.6.0)
grpc (~> 1.19.0)
haml_lint (~> 0.31.0)
Expand Down
4 changes: 2 additions & 2 deletions app/graphql/mutations/notes/create/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ class Base < Mutations::Notes::Base
required: true,
description: copy_field_description(Types::Notes::NoteType, :body)

private

def resolve(args)
noteable = authorized_find!(id: args[:noteable_id])

Expand All @@ -37,6 +35,8 @@ def resolve(args)
}
end

private

def create_note_params(noteable, args)
{
noteable: noteable,
Expand Down
2 changes: 1 addition & 1 deletion app/graphql/resolvers/full_path_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module FullPathResolver
end

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
args[:key].where_full_path_in(full_paths).with_route.each do |model_instance|
loader.call(model_instance.full_path, model_instance)
Expand Down
4 changes: 1 addition & 3 deletions app/graphql/resolvers/issues_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,11 @@ class IssuesResolver < BaseResolver

type Types::IssueType, null: true

alias_method :project, :object

def resolve(**args)
# 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
# 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?

# Will need to be be made group & namespace aware with
Expand Down
6 changes: 4 additions & 2 deletions app/graphql/resolvers/merge_requests_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ def resolve(**args)

# rubocop: disable CodeReuse/ActiveRecord
def batch_load(iid)
BatchLoader.for(iid.to_s).batch(key: project) do |iids, loader, args|
args[:key].merge_requests.where(iid: iids).each do |mr|
BatchLoader::GraphQL.for(iid.to_s).batch(key: project) do |iids, loader, args|
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)
end
end
Expand Down
4 changes: 1 addition & 3 deletions app/graphql/resolvers/namespace_projects_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ class NamespaceProjectsResolver < BaseResolver

type Types::ProjectType, null: true

alias_method :namespace, :object

def resolve(include_subgroups:)
# The namespace could have been loaded in batch by `BatchLoader`.
# 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.
namespace.sync if namespace.respond_to?(:sync)
namespace = object.respond_to?(:sync) ? object.sync : object
return Project.none if namespace.nil?

if include_subgroups
Expand Down
8 changes: 4 additions & 4 deletions lib/gitlab/graphql/authorize/authorize_field_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ def authorize_against(parent_typed_object, resolved_type)
# The field is a built-in/scalar type, or a list of scalars
# authorize using the parent's 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)
# The field is a connection or a list of non-built-in types, we'll
# authorize each element when rendering
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
# Resolved type is a single object that might not be loaded yet by
# the batchloader, we'll authorize that
Expand Down
11 changes: 9 additions & 2 deletions lib/gitlab/graphql/authorize/authorize_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,32 @@ def find_object(*args)

def authorized_find!(*args)
object = find_object(*args)
object = object.sync if object.respond_to?(:sync)

authorize!(object)

object
end

def authorize!(object)
unless authorized?(object)
unless authorized_resource?(object)
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"
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
def authorized_resource?(object)
# Sanity check. We don't want to accidentally allow a developer to authorize
# without first adding permissions to authorize against
if self.class.required_permissions.empty?
raise Gitlab::Graphql::Errors::ArgumentError, "#{self.class.name} has no authorizations"
end

object = object.sync if object.respond_to?(:sync)

self.class.required_permissions.all? do |ability|
# The actions could be performed across multiple objects. In which
# case the current user is common, and we could benefit from the
Expand Down
2 changes: 1 addition & 1 deletion lib/gitlab/graphql/loaders/batch_lfs_oid_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def initialize(repository, blob_id)
end

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|
loader.call(loaded_blob.id, loaded_blob.lfs_oid)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/gitlab/graphql/loaders/batch_model_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def initialize(model_class, model_id)

# rubocop: disable CodeReuse/ActiveRecord
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.each do |model, info|
ids = info.map { |i| i[:id] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(project_id)
end

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|
loader.call(statistics.project_id, statistics)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(namespace_id)
end

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|
loader.call(statistics.namespace_id, statistics)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/gitlab/graphql/loaders/pipeline_for_sha_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(project, sha)
end

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.each do |pipeline|
Expand Down
8 changes: 4 additions & 4 deletions spec/graphql/gitlab_schema_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@

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

it 'batchloads the queries' do
Expand All @@ -138,7 +138,7 @@

expect do
[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
end
Expand All @@ -149,7 +149,7 @@

result = described_class.object_from_id(note.to_global_id)

expect(result.__sync).to eq(note)
expect(result.sync).to eq(note)
end

it 'batchloads the queries' do
Expand All @@ -158,7 +158,7 @@

expect do
[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
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
project = create(:project)

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
4 changes: 2 additions & 2 deletions spec/graphql/resolvers/group_resolver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
it 'batch-resolves groups by full path' do
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) }
end

expect(result).to contain_exactly(group1, group2)
end

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
end
Expand Down
2 changes: 1 addition & 1 deletion spec/graphql/resolvers/issues_resolver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@

context "when passing a non existent, batch loaded 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)
end
end
Expand Down
12 changes: 6 additions & 6 deletions spec/graphql/resolvers/merge_requests_resolver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,23 @@

describe '#resolve' 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)
end

expect(result).to contain_exactly(merge_request_1, merge_request_2)
end

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])
end

expect(result).to contain_exactly(merge_request_1, merge_request_2)
end

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_2) +
resolve_mr(other_project, iid: other_iid)
Expand All @@ -43,13 +43,13 @@
end

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

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
end
Expand Down
2 changes: 1 addition & 1 deletion spec/graphql/resolvers/namespace_projects_resolver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

context "when passing a non existent, batch loaded 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)
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/graphql/resolvers/project_resolver_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
it 'batch-resolves projects by full path' do
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) }
end

expect(result).to contain_exactly(project1, project2)
end

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
end
Expand Down
12 changes: 6 additions & 6 deletions spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ def current_user
end
end

describe '#authorized?' do
describe '#authorized_resource?' 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
Expand All @@ -72,9 +72,9 @@ def current_user
end
end

describe '#authorized?' do
describe '#authorized_resource?' 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
Expand Down Expand Up @@ -121,9 +121,9 @@ def self.name
end
end

describe '#authorized?' do
describe '#authorized_resource?' 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
it 'batch-resolves LFS blob IDs' do
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 }
end

Expand Down
Loading

0 comments on commit 59c9de4

Please sign in to comment.