Skip to content
Snippets Groups Projects
Commit 9b65d4bb authored by Bob Van Landuyt's avatar Bob Van Landuyt
Browse files

Initial setup GraphQL using graphql-ruby 1.8

- All definitions have been replaced by classes:
  http://graphql-ruby.org/schema/class_based_api.html
- Authorization & Presentation have been refactored to work in the
  class based system
- Loaders have been replaced by resolvers
- Times are now coersed as ISO 8601
parent c443133e
No related branches found
No related tags found
No related merge requests found
Showing
with 144 additions and 113 deletions
Loading
Loading
@@ -43,7 +43,6 @@ Naming/FileName:
- 'config/**/*'
- 'lib/generators/**/*'
- 'ee/lib/generators/**/*'
- 'app/graphql/**/*'
IgnoreExecutableScripts: true
AllowedAcronyms:
- EE
Loading
Loading
Loading
Loading
@@ -94,8 +94,7 @@ gem 'grape-entity', '~> 0.7.1'
gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'
 
# GraphQL API
gem 'graphql', '~> 1.7.14'
gem 'graphql-preload', '~> 2.0.0'
gem 'graphql', '~> 1.8.0'
gem 'graphiql-rails', '~> 1.4.10'
 
# Disable strong_params so that Mash does not respond to :permitted?
Loading
Loading
Loading
Loading
@@ -368,15 +368,7 @@ GEM
graphiql-rails (1.4.10)
railties
sprockets-rails
graphql (1.7.14)
graphql-batch (0.3.9)
graphql (>= 0.8, < 2)
promise.rb (~> 0.7.2)
graphql-preload (2.0.1)
activerecord (>= 4.1, < 6)
graphql (>= 1.5, < 2)
graphql-batch (~> 0.3)
promise.rb (~> 0.7)
graphql (1.8.1)
grpc (1.11.0)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
Loading
Loading
@@ -639,7 +631,6 @@ GEM
unparser
procto (0.0.3)
prometheus-client-mmap (0.9.3)
promise.rb (0.7.4)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
Loading
Loading
@@ -1067,8 +1058,7 @@ DEPENDENCIES
grape-path-helpers (~> 1.0)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.7.14)
graphql-preload (~> 2.0.0)
graphql (~> 1.8.0)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.6.1)
Loading
Loading
module Functions
class BaseFunction < GraphQL::Function
end
end
module Functions
class Echo < BaseFunction
argument :text, GraphQL::STRING_TYPE
description "Testing endpoint to validate the API with"
def call(obj, args, ctx)
username = ctx[:current_user]&.username
"#{username.inspect} says: #{args[:text]}"
end
end
end
Gitlab::Graphql::Authorize.register!
Gitlab::Graphql::Present.register!
GitlabSchema = GraphQL::Schema.define do
class GitlabSchema < GraphQL::Schema
use BatchLoader::GraphQL
enable_preloading
enable_authorization
enable_presenting
use Gitlab::Graphql::Authorize
use Gitlab::Graphql::Present
 
query(Types::QueryType)
# mutation(Types::MutationType)
end
# Helper methods for all loaders
module Loaders::BaseLoader
extend ActiveSupport::Concern
class_methods do
# Convert a class method into a resolver proc. The method should follow the
# (obj, args, ctx) calling convention
def [](sym)
resolver = method(sym)
raise ArgumentError.new("#{self}.#{sym} is not a resolver") unless resolver.arity == 3
resolver
end
end
end
class Loaders::IidLoader
include Loaders::BaseLoader
class << self
def merge_request(obj, args, ctx)
iid = args[:iid]
project = Loaders::FullPathLoader.project_by_full_path(args[:project])
merge_request_by_project_and_iid(project, iid)
end
def merge_request_by_project_and_iid(project_loader, iid)
project_id = project_loader.__sync&.id
# IIDs are represented as the GraphQL `id` type, which is a string
BatchLoader.for(iid.to_s).batch(key: "merge_request:target_project:#{project_id}:iid") do |iids, loader|
if project_id
results = MergeRequest.where(target_project_id: project_id, iid: iids)
results.each { |mr| loader.call(mr.iid.to_s, mr) }
end
end
end
end
end
module Resolvers
class BaseResolver < GraphQL::Schema::Resolver
end
end
module Loaders::FullPathLoader
include Loaders::BaseLoader
module Resolvers
module FullPathResolver
extend ActiveSupport::Concern
 
class << self
def project(obj, args, ctx)
project_by_full_path(args[:full_path])
end
def project_by_full_path(full_path)
model_by_full_path(Project, full_path)
prepended do
argument :full_path, GraphQL::ID_TYPE,
required: true,
description: 'The full path of the project or namespace, e.g., "gitlab-org/gitlab-ce"'
end
 
def model_by_full_path(model, full_path)
Loading
Loading
module Resolvers
class MergeRequestResolver < BaseResolver
prepend FullPathResolver
type Types::ProjectType, null: true
argument :iid, GraphQL::ID_TYPE,
required: true,
description: 'The IID of the merge request, e.g., "1"'
def resolve(full_path:, iid:)
project = model_by_full_path(Project, full_path)
return unless project.present?
BatchLoader.for(iid.to_s).batch(key: project.id) do |iids, loader|
results = project.merge_requests.where(iid: iids)
results.each { |mr| loader.call(mr.iid.to_s, mr) }
end
end
end
end
module Resolvers
class ProjectResolver < BaseResolver
prepend FullPathResolver
type Types::ProjectType, null: true
def resolve(full_path:)
model_by_full_path(Project, full_path)
end
end
end
module Types
class BaseEnum < GraphQL::Schema::Enum
end
end
module Types
class BaseField < GraphQL::Schema::Field
prepend Gitlab::Graphql::Authorize
end
end
module Types
class BaseInputObject < GraphQL::Schema::InputObject
end
end
module Types
module BaseInterface
include GraphQL::Schema::Interface
end
end
module Types
class BaseObject < GraphQL::Schema::Object
prepend Gitlab::Graphql::Present
field_class Types::BaseField
end
end
module Types
class BaseScalar < GraphQL::Schema::Scalar
end
end
module Types
class BaseUnion < GraphQL::Schema::Union
end
end
Types::MergeRequestType = GraphQL::ObjectType.define do
present_using MergeRequestPresenter
module Types
class MergeRequestType < BaseObject
present_using MergeRequestPresenter
 
name 'MergeRequest'
graphql_name 'MergeRequest'
 
field :id, !types.ID
field :iid, !types.ID
field :title, !types.String
field :description, types.String
field :state, types.String
field :created_at, !Types::TimeType
field :updated_at, !Types::TimeType
field :source_project, Types::ProjectType
field :target_project, !Types::ProjectType
# Alias for target_project
field :project, !Types::ProjectType
field :project_id, !types.Int, property: :target_project_id
field :source_project_id, types.Int
field :target_project_id, !types.Int
field :source_branch, !types.String
field :target_branch, !types.String
field :work_in_progress, types.Boolean, property: :work_in_progress?
field :merge_when_pipeline_succeeds, types.Boolean
field :sha, types.String, property: :diff_head_sha
field :merge_commit_sha, types.String
field :user_notes_count, types.Int
field :should_remove_source_branch, types.Boolean, property: :should_remove_source_branch?
field :force_remove_source_branch, types.Boolean, property: :force_remove_source_branch?
field :merge_status, types.String
field :in_progress_merge_commit_sha, types.String
field :merge_error, types.String
field :allow_maintainer_to_push, types.Boolean
field :should_be_rebased, types.Boolean, property: :should_be_rebased?
field :rebase_commit_sha, types.String
field :rebase_in_progress, types.Boolean, property: :rebase_in_progress?
field :diff_head_sha, types.String
field :merge_commit_message, types.String
field :merge_ongoing, types.Boolean, property: :merge_ongoing?
field :work_in_progress, types.Boolean, property: :work_in_progress?
field :source_branch_exists, types.Boolean, property: :source_branch_exists?
field :mergeable_discussions_state, types.Boolean
field :web_url, types.String, property: :web_url
field :upvotes, types.Int
field :downvotes, types.Int
field :subscribed, types.Boolean, property: :subscribed?
field :id, GraphQL::ID_TYPE, null: false
field :iid, GraphQL::ID_TYPE, null: false
field :title, GraphQL::STRING_TYPE, null: false
field :description, GraphQL::STRING_TYPE, null: true
field :state, GraphQL::STRING_TYPE, null: true
field :created_at, Types::TimeType, null: false
field :updated_at, Types::TimeType, null: false
field :source_project, Types::ProjectType, null: true
field :target_project, Types::ProjectType, null: false
# Alias for target_project
field :project, Types::ProjectType, null: false
field :project_id, GraphQL::INT_TYPE, null: false, method: :target_project_id
field :source_project_id, GraphQL::INT_TYPE, null: true
field :target_project_id, GraphQL::INT_TYPE, null: false
field :source_branch, GraphQL::STRING_TYPE, null: false
field :target_branch, GraphQL::STRING_TYPE, null: false
field :work_in_progress, GraphQL::BOOLEAN_TYPE, method: :work_in_progress?, null: false
field :merge_when_pipeline_succeeds, GraphQL::BOOLEAN_TYPE, null: true
field :diff_head_sha, GraphQL::STRING_TYPE, null: true
field :merge_commit_sha, GraphQL::STRING_TYPE, null: true
field :user_notes_count, GraphQL::INT_TYPE, null: true
field :should_remove_source_branch, GraphQL::BOOLEAN_TYPE, method: :should_remove_source_branch?, null: true
field :force_remove_source_branch, GraphQL::BOOLEAN_TYPE, method: :force_remove_source_branch?, null: true
field :merge_status, GraphQL::STRING_TYPE, null: true
field :in_progress_merge_commit_sha, GraphQL::STRING_TYPE, null: true
field :merge_error, GraphQL::STRING_TYPE, null: true
field :allow_collaboration, GraphQL::BOOLEAN_TYPE, null: true
field :should_be_rebased, GraphQL::BOOLEAN_TYPE, method: :should_be_rebased?, null: false
field :rebase_commit_sha, GraphQL::STRING_TYPE, null: true
field :rebase_in_progress, GraphQL::BOOLEAN_TYPE, method: :rebase_in_progress?, null: false
field :diff_head_sha, GraphQL::STRING_TYPE, null: true
field :merge_commit_message, GraphQL::STRING_TYPE, null: true
field :merge_ongoing, GraphQL::BOOLEAN_TYPE, method: :merge_ongoing?, null: false
field :source_branch_exists, GraphQL::BOOLEAN_TYPE, method: :source_branch_exists?, null: false
field :mergeable_discussions_state, GraphQL::BOOLEAN_TYPE, null: true
field :web_url, GraphQL::STRING_TYPE, null: true
field :upvotes, GraphQL::INT_TYPE, null: false
field :downvotes, GraphQL::INT_TYPE, null: false
field :subscribed, GraphQL::BOOLEAN_TYPE, method: :subscribed?, null: false
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment