summaryrefslogtreecommitdiff
path: root/app/graphql/types
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-04-20 23:50:22 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-04-20 23:50:22 +0000
commit9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch)
tree70467ae3692a0e35e5ea56bcb803eb512a10bedb /app/graphql/types
parent4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff)
downloadgitlab-ce-9dc93a4519d9d5d7be48ff274127136236a3adb3.tar.gz
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'app/graphql/types')
-rw-r--r--app/graphql/types/base_argument.rb4
-rw-r--r--app/graphql/types/base_enum.rb27
-rw-r--r--app/graphql/types/base_field.rb41
-rw-r--r--app/graphql/types/base_interface.rb6
-rw-r--r--app/graphql/types/base_object.rb8
-rw-r--r--app/graphql/types/base_union.rb3
-rw-r--r--app/graphql/types/board_type.rb6
-rw-r--r--app/graphql/types/boards/assignee_wildcard_id_enum.rb13
-rw-r--r--app/graphql/types/boards/board_issuable_input_base_type.rb20
-rw-r--r--app/graphql/types/boards/board_issue_input_base_type.rb17
-rw-r--r--app/graphql/types/boards/board_issue_input_type.rb13
-rw-r--r--app/graphql/types/boards/negated_board_issue_input_type.rb10
-rw-r--r--app/graphql/types/ci/job_status_enum.rb15
-rw-r--r--app/graphql/types/ci/job_type.rb99
-rw-r--r--app/graphql/types/ci/pipeline_config_source_enum.rb3
-rw-r--r--app/graphql/types/ci/pipeline_status_enum.rb4
-rw-r--r--app/graphql/types/ci/pipeline_type.rb45
-rw-r--r--app/graphql/types/ci/recent_failures_type.rb20
-rw-r--r--app/graphql/types/ci/stage_type.rb35
-rw-r--r--app/graphql/types/ci/test_case_status_enum.rb15
-rw-r--r--app/graphql/types/ci/test_case_type.rb41
-rw-r--r--app/graphql/types/ci/test_report_summary_type.rb19
-rw-r--r--app/graphql/types/ci/test_report_total_type.rb33
-rw-r--r--app/graphql/types/ci/test_suite_summary_type.rb41
-rw-r--r--app/graphql/types/ci/test_suite_type.rb41
-rw-r--r--app/graphql/types/concerns/find_closest.rb11
-rw-r--r--app/graphql/types/concerns/gitlab_style_deprecations.rb18
-rw-r--r--app/graphql/types/global_id_type.rb13
-rw-r--r--app/graphql/types/group_type.rb69
-rw-r--r--app/graphql/types/issue_type.rb3
-rw-r--r--app/graphql/types/issues/negated_issue_filter_input_type.rb27
-rw-r--r--app/graphql/types/jira_users_mapping_input_type.rb12
-rw-r--r--app/graphql/types/merge_request_review_state_enum.rb11
-rw-r--r--app/graphql/types/merge_request_state_enum.rb2
-rw-r--r--app/graphql/types/merge_request_type.rb7
-rw-r--r--app/graphql/types/merge_requests/reviewer_type.rb26
-rw-r--r--app/graphql/types/milestone_type.rb3
-rw-r--r--app/graphql/types/mutation_operation_mode_enum.rb8
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/graphql/types/packages/conan/file_metadatum_type.rb22
-rw-r--r--app/graphql/types/packages/conan/metadatum_file_type_enum.rb16
-rw-r--r--app/graphql/types/packages/conan/metadatum_type.rb22
-rw-r--r--app/graphql/types/packages/file_metadata_type.rb27
-rw-r--r--app/graphql/types/packages/metadata_type.rb4
-rw-r--r--app/graphql/types/packages/package_details_type.rb20
-rw-r--r--app/graphql/types/packages/package_file_type.rb36
-rw-r--r--app/graphql/types/packages/package_type.rb47
-rw-r--r--app/graphql/types/packages/package_without_versions_type.rb44
-rw-r--r--app/graphql/types/project_type.rb6
-rw-r--r--app/graphql/types/query_type.rb34
-rw-r--r--app/graphql/types/repository/blob_type.rb40
-rw-r--r--app/graphql/types/repository_type.rb5
-rw-r--r--app/graphql/types/sort_enum.rb32
-rw-r--r--app/graphql/types/timelog_type.rb42
-rw-r--r--app/graphql/types/user_merge_request_interaction_type.rb47
-rw-r--r--app/graphql/types/user_type.rb75
56 files changed, 1142 insertions, 167 deletions
diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb
index 4ad9e8c0e40..ff9a5a0611d 100644
--- a/app/graphql/types/base_argument.rb
+++ b/app/graphql/types/base_argument.rb
@@ -4,8 +4,10 @@ module Types
class BaseArgument < GraphQL::Schema::Argument
include GitlabStyleDeprecations
+ attr_reader :deprecation
+
def initialize(*args, **kwargs, &block)
- kwargs = gitlab_deprecation(kwargs)
+ @deprecation = gitlab_deprecation(kwargs)
super(*args, **kwargs, &block)
end
diff --git a/app/graphql/types/base_enum.rb b/app/graphql/types/base_enum.rb
index 4d470aceca4..518a902a5d7 100644
--- a/app/graphql/types/base_enum.rb
+++ b/app/graphql/types/base_enum.rb
@@ -21,12 +21,23 @@ module Types
graphql_name(enum_mod.name) if use_name
description(enum_mod.description) if use_description
- enum_mod.definition.each { |key, content| value(key.to_s.upcase, **content) }
+ enum_mod.definition.each do |key, content|
+ value(key.to_s.upcase, **content)
+ end
+ end
+
+ # Helper to define an enum member for each element of a Rails AR enum
+ def from_rails_enum(enum, description:)
+ enum.each_key do |name|
+ value name.to_s.upcase,
+ value: name,
+ description: format(description, name: name)
+ end
end
def value(*args, **kwargs, &block)
enum[args[0].downcase] = kwargs[:value] || args[0]
- kwargs = gitlab_deprecation(kwargs)
+ gitlab_deprecation(kwargs)
super(*args, **kwargs, &block)
end
@@ -36,6 +47,18 @@ module Types
def enum
@enum_values ||= {}.with_indifferent_access
end
+
+ def authorization
+ @authorization ||= ::Gitlab::Graphql::Authorize::ObjectAuthorization.new(authorize)
+ end
+
+ def authorize(*abilities)
+ @abilities = abilities
+ end
+
+ def authorized?(object, context)
+ authorization.ok?(object, context[:current_user])
+ end
end
end
end
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 78ab6890923..7c939f94dde 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -2,28 +2,30 @@
module Types
class BaseField < GraphQL::Schema::Field
- prepend Gitlab::Graphql::Authorize
include GitlabStyleDeprecations
argument_class ::Types::BaseArgument
DEFAULT_COMPLEXITY = 1
+ attr_reader :deprecation
+
def initialize(**kwargs, &block)
@calls_gitaly = !!kwargs.delete(:calls_gitaly)
@constant_complexity = kwargs[:complexity].is_a?(Integer) && kwargs[:complexity] > 0
@requires_argument = !!kwargs.delete(:requires_argument)
+ @authorize = Array.wrap(kwargs.delete(:authorize))
kwargs[:complexity] = field_complexity(kwargs[:resolver_class], kwargs[:complexity])
@feature_flag = kwargs[:feature_flag]
kwargs = check_feature_flag(kwargs)
- kwargs = gitlab_deprecation(kwargs)
+ @deprecation = gitlab_deprecation(kwargs)
super(**kwargs, &block)
# We want to avoid the overhead of this in prod
extension ::Gitlab::Graphql::CallsGitaly::FieldExtension if Gitlab.dev_or_test_env?
-
extension ::Gitlab::Graphql::Present::FieldExtension
+ extension ::Gitlab::Graphql::Authorize::ConnectionFilterExtension
end
def may_call_gitaly?
@@ -34,6 +36,19 @@ module Types
@requires_argument || arguments.values.any? { |argument| argument.type.non_null? }
end
+ # By default fields authorize against the current object, but that is not how our
+ # resolvers work - they use declarative permissions to authorize fields
+ # manually (so we make them opt in).
+ # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/300922
+ # (separate out authorize into permissions on the object, and on the
+ # resolved values)
+ # We do not support argument authorization in our schema. If/when we do,
+ # we should call `super` here, to apply argument authorization checks.
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/324647
+ def authorized?(object, args, ctx)
+ field_authorized?(object, ctx) && resolver_authorized?(object, ctx)
+ end
+
def base_complexity
complexity = DEFAULT_COMPLEXITY
complexity += 1 if calls_gitaly?
@@ -58,6 +73,26 @@ module Types
attr_reader :feature_flag
+ def field_authorized?(object, ctx)
+ authorization.ok?(object, ctx[:current_user])
+ end
+
+ # Historically our resolvers have used declarative permission checks only
+ # for _what they resolved_, not the _object they resolved these things from_
+ # We preserve these semantics here, and only apply resolver authorization
+ # if the resolver has opted in.
+ def resolver_authorized?(object, ctx)
+ if @resolver_class && @resolver_class.try(:authorizes_object?)
+ @resolver_class.authorized?(object, ctx)
+ else
+ true
+ end
+ end
+
+ def authorization
+ @authorization ||= ::Gitlab::Graphql::Authorize::ObjectAuthorization.new(@authorize)
+ end
+
def feature_documentation_message(key, description)
"#{description} Available only when feature flag `#{key}` is enabled."
end
diff --git a/app/graphql/types/base_interface.rb b/app/graphql/types/base_interface.rb
index 4b1f3193136..c21c95876be 100644
--- a/app/graphql/types/base_interface.rb
+++ b/app/graphql/types/base_interface.rb
@@ -5,5 +5,11 @@ module Types
include GraphQL::Schema::Interface
field_class ::Types::BaseField
+
+ definition_methods do
+ def authorized?(object, context)
+ resolve_type(object, context).authorized?(object, context)
+ end
+ end
end
end
diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb
index 9c36c83d4a3..cd677e50d28 100644
--- a/app/graphql/types/base_object.rb
+++ b/app/graphql/types/base_object.rb
@@ -19,6 +19,14 @@ module Types
GitlabSchema.id_from_object(object)
end
+ def self.authorization
+ @authorization ||= ::Gitlab::Graphql::Authorize::ObjectAuthorization.new(authorize)
+ end
+
+ def self.authorized?(object, context)
+ authorization.ok?(object, context[:current_user])
+ end
+
def current_user
context[:current_user]
end
diff --git a/app/graphql/types/base_union.rb b/app/graphql/types/base_union.rb
index 30a5668c0bb..aeafbf85020 100644
--- a/app/graphql/types/base_union.rb
+++ b/app/graphql/types/base_union.rb
@@ -2,5 +2,8 @@
module Types
class BaseUnion < GraphQL::Schema::Union
+ def self.authorized?(object, context)
+ resolve_type(object, context).authorized?(object, context)
+ end
end
end
diff --git a/app/graphql/types/board_type.rb b/app/graphql/types/board_type.rb
index f33f3f5e537..42d8eecc366 100644
--- a/app/graphql/types/board_type.rb
+++ b/app/graphql/types/board_type.rb
@@ -20,6 +20,12 @@ module Types
field :hide_closed_list, type: GraphQL::BOOLEAN_TYPE, null: true,
description: 'Whether or not closed list is hidden.'
+ field :created_at, Types::TimeType, null: false,
+ description: 'Timestamp of when the board was created.'
+
+ field :updated_at, Types::TimeType, null: false,
+ description: 'Timestamp of when the board was last updated.'
+
field :lists,
Types::BoardListType.connection_type,
null: true,
diff --git a/app/graphql/types/boards/assignee_wildcard_id_enum.rb b/app/graphql/types/boards/assignee_wildcard_id_enum.rb
new file mode 100644
index 00000000000..ba9058a78d9
--- /dev/null
+++ b/app/graphql/types/boards/assignee_wildcard_id_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Boards
+ class AssigneeWildcardIdEnum < BaseEnum
+ graphql_name 'AssigneeWildcardId'
+ description 'Assignee ID wildcard values'
+
+ value 'NONE', 'No assignee is assigned.'
+ value 'ANY', 'An assignee is assigned.'
+ end
+ end
+end
diff --git a/app/graphql/types/boards/board_issuable_input_base_type.rb b/app/graphql/types/boards/board_issuable_input_base_type.rb
new file mode 100644
index 00000000000..2cd057347d6
--- /dev/null
+++ b/app/graphql/types/boards/board_issuable_input_base_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Boards
+ # Common arguments that we can be used to filter boards epics and issues
+ class BoardIssuableInputBaseType < BaseInputObject
+ argument :label_name, [GraphQL::STRING_TYPE, null: true],
+ required: false,
+ description: 'Filter by label name.'
+
+ argument :author_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Filter by author username.'
+
+ argument :my_reaction_emoji, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Filter by reaction emoji applied by the current user.'
+ end
+ end
+end
diff --git a/app/graphql/types/boards/board_issue_input_base_type.rb b/app/graphql/types/boards/board_issue_input_base_type.rb
index b762cef6e58..7cf2dcb9c82 100644
--- a/app/graphql/types/boards/board_issue_input_base_type.rb
+++ b/app/graphql/types/boards/board_issue_input_base_type.rb
@@ -2,30 +2,19 @@
module Types
module Boards
- class BoardIssueInputBaseType < BaseInputObject
- argument :label_name, GraphQL::STRING_TYPE.to_list_type,
- required: false,
- description: 'Filter by label name.'
-
+ # rubocop: disable Graphql/AuthorizeTypes
+ class BoardIssueInputBaseType < BoardIssuableInputBaseType
argument :milestone_title, GraphQL::STRING_TYPE,
required: false,
description: 'Filter by milestone title.'
- argument :assignee_username, GraphQL::STRING_TYPE.to_list_type,
+ argument :assignee_username, [GraphQL::STRING_TYPE, null: true],
required: false,
description: 'Filter by assignee username.'
- argument :author_username, GraphQL::STRING_TYPE,
- required: false,
- description: 'Filter by author username.'
-
argument :release_tag, GraphQL::STRING_TYPE,
required: false,
description: 'Filter by release tag.'
-
- argument :my_reaction_emoji, GraphQL::STRING_TYPE,
- required: false,
- description: 'Filter by reaction emoji.'
end
end
end
diff --git a/app/graphql/types/boards/board_issue_input_type.rb b/app/graphql/types/boards/board_issue_input_type.rb
index 9cc0f484a16..8c0e37e5cb7 100644
--- a/app/graphql/types/boards/board_issue_input_type.rb
+++ b/app/graphql/types/boards/board_issue_input_type.rb
@@ -2,19 +2,24 @@
module Types
module Boards
- class NegatedBoardIssueInputType < BoardIssueInputBaseType
- end
-
class BoardIssueInputType < BoardIssueInputBaseType
graphql_name 'BoardIssueInput'
argument :not, NegatedBoardIssueInputType,
required: false,
- description: 'List of negated params. Warning: this argument is experimental and a subject to change in future.'
+ prepare: ->(negated_args, ctx) { negated_args.to_h },
+ description: <<~MD
+ List of negated arguments.
+ Warning: this argument is experimental and a subject to change in future.
+ MD
argument :search, GraphQL::STRING_TYPE,
required: false,
description: 'Search query for issue title or description.'
+
+ argument :assignee_wildcard_id, ::Types::Boards::AssigneeWildcardIdEnum,
+ required: false,
+ description: 'Filter by assignee wildcard. Incompatible with assigneeUsername.'
end
end
end
diff --git a/app/graphql/types/boards/negated_board_issue_input_type.rb b/app/graphql/types/boards/negated_board_issue_input_type.rb
new file mode 100644
index 00000000000..a0fab2ae969
--- /dev/null
+++ b/app/graphql/types/boards/negated_board_issue_input_type.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Types
+ module Boards
+ class NegatedBoardIssueInputType < BoardIssueInputBaseType
+ end
+ end
+end
+
+Types::Boards::NegatedBoardIssueInputType.prepend_if_ee('::EE::Types::Boards::NegatedBoardIssueInputType')
diff --git a/app/graphql/types/ci/job_status_enum.rb b/app/graphql/types/ci/job_status_enum.rb
new file mode 100644
index 00000000000..ec80b1f4776
--- /dev/null
+++ b/app/graphql/types/ci/job_status_enum.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class JobStatusEnum < BaseEnum
+ graphql_name 'CiJobStatus'
+
+ ::Ci::HasStatus::AVAILABLE_STATUSES.each do |status|
+ value status.upcase,
+ description: "A job that is #{status.tr('_', ' ')}.",
+ value: status
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index c86337eea89..94a256fed3d 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -6,27 +6,74 @@ module Types
graphql_name 'CiJob'
authorize :read_commit_status
+ connection_type_class(Types::CountableConnectionType)
+
+ field :id, ::Types::GlobalIDType[::CommitStatus].as('JobID'), null: true,
+ description: 'ID of the job.'
field :pipeline, Types::Ci::PipelineType, null: true,
description: 'Pipeline the job belongs to.'
field :name, GraphQL::STRING_TYPE, null: true,
description: 'Name of the job.'
field :needs, BuildNeedType.connection_type, null: true,
description: 'References to builds that must complete before the jobs run.'
- field :detailed_status, Types::Ci::DetailedStatusType, null: true,
- description: 'Detailed status of the job.'
+ field :status,
+ type: ::Types::Ci::JobStatusEnum,
+ null: true,
+ description: "Status of the job."
+ field :stage, Types::Ci::StageType, null: true,
+ description: 'Stage of the job.'
+ field :allow_failure, ::GraphQL::BOOLEAN_TYPE, null: false,
+ description: 'Whether this job is allowed to fail.'
+ field :duration, GraphQL::INT_TYPE, null: true,
+ description: 'Duration of the job in seconds.'
+ field :tags, [GraphQL::STRING_TYPE], null: true,
+ description: 'Tags for the current job.'
+
+ # Life-cycle timestamps:
+ field :created_at, Types::TimeType, null: false,
+ description: "When the job was created."
+ field :queued_at, Types::TimeType, null: true,
+ description: 'When the job was enqueued and marked as pending.'
+ field :started_at, Types::TimeType, null: true,
+ description: 'When the job was started.'
+ field :finished_at, Types::TimeType, null: true,
+ description: 'When a job has finished running.'
field :scheduled_at, Types::TimeType, null: true,
description: 'Schedule for the build.'
+
+ field :detailed_status, Types::Ci::DetailedStatusType, null: true,
+ description: 'Detailed status of the job.'
field :artifacts, Types::Ci::JobArtifactType.connection_type, null: true,
description: 'Artifacts generated by the job.'
- field :finished_at, Types::TimeType, null: true,
- description: 'When a job has finished running.'
- field :duration, GraphQL::INT_TYPE, null: true,
- description: 'Duration of the job in seconds.'
+ field :short_sha, type: GraphQL::STRING_TYPE, null: false,
+ description: 'Short SHA1 ID of the commit.'
+ field :scheduling_type, GraphQL::STRING_TYPE, null: true,
+ description: 'Type of pipeline scheduling. Value is `dag` if the pipeline uses the `needs` keyword, and `stage` otherwise.'
+ field :commit_path, GraphQL::STRING_TYPE, null: true,
+ description: 'Path to the commit that triggered the job.'
+ field :ref_name, GraphQL::STRING_TYPE, null: true,
+ description: 'Ref name of the job.'
+ field :ref_path, GraphQL::STRING_TYPE, null: true,
+ description: 'Path to the ref.'
+ field :playable, GraphQL::BOOLEAN_TYPE, null: false, method: :playable?,
+ description: 'Indicates the job can be played.'
+ field :retryable, GraphQL::BOOLEAN_TYPE, null: false, method: :retryable?,
+ description: 'Indicates the job can be retried.'
+ field :cancelable, GraphQL::BOOLEAN_TYPE, null: false, method: :cancelable?,
+ description: 'Indicates the job can be canceled.'
+ field :active, GraphQL::BOOLEAN_TYPE, null: false, method: :active?,
+ description: 'Indicates the job is active.'
+ field :coverage, GraphQL::FLOAT_TYPE, null: true,
+ description: 'Coverage level of the job.'
def pipeline
Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find
end
+ def tags
+ object.tags.map(&:name) if object.is_a?(::Ci::Build)
+ end
+
def detailed_status
object.detailed_status(context[:current_user])
end
@@ -36,6 +83,46 @@ module Types
object.job_artifacts
end
end
+
+ def stage
+ ::Gitlab::Graphql::Lazy.with_value(pipeline) do |pl|
+ BatchLoader::GraphQL.for([pl, object.stage]).batch do |ids, loader|
+ by_pipeline = ids
+ .group_by(&:first)
+ .transform_values { |grp| grp.map(&:second) }
+
+ by_pipeline.each do |p, names|
+ p.stages.by_name(names).each { |s| loader.call([p, s.name], s) }
+ end
+ end
+ end
+ end
+
+ # This class is a secret union!
+ # TODO: turn this into an actual union, so that fields can be referenced safely!
+ def id
+ return unless object.id.present?
+
+ model_name = object.type || ::CommitStatus.name
+ id = object.id
+ Gitlab::GlobalId.build(model_name: model_name, id: id)
+ end
+
+ def commit_path
+ ::Gitlab::Routing.url_helpers.project_commit_path(object.project, object.sha)
+ end
+
+ def ref_name
+ object&.ref
+ end
+
+ def ref_path
+ ::Gitlab::Routing.url_helpers.project_commits_path(object.project, ref_name)
+ end
+
+ def coverage
+ object&.coverage
+ end
end
end
end
diff --git a/app/graphql/types/ci/pipeline_config_source_enum.rb b/app/graphql/types/ci/pipeline_config_source_enum.rb
index e1575cb2f99..96c8a5f2941 100644
--- a/app/graphql/types/ci/pipeline_config_source_enum.rb
+++ b/app/graphql/types/ci/pipeline_config_source_enum.rb
@@ -4,7 +4,8 @@ module Types
module Ci
class PipelineConfigSourceEnum < BaseEnum
::Enums::Ci::Pipeline.config_sources.keys.each do |state_symbol|
- value state_symbol.to_s.upcase, value: state_symbol.to_s
+ description = state_symbol == :auto_devops_source ? "Auto DevOps source." : "#{state_symbol.to_s.titleize.capitalize}." # This is needed to avoid failure in doc lint
+ value state_symbol.to_s.upcase, value: state_symbol.to_s, description: description
end
end
end
diff --git a/app/graphql/types/ci/pipeline_status_enum.rb b/app/graphql/types/ci/pipeline_status_enum.rb
index c19ddf5bb25..e0b2020dcc1 100644
--- a/app/graphql/types/ci/pipeline_status_enum.rb
+++ b/app/graphql/types/ci/pipeline_status_enum.rb
@@ -4,7 +4,9 @@ module Types
module Ci
class PipelineStatusEnum < BaseEnum
::Ci::Pipeline.all_state_names.each do |state_symbol|
- value state_symbol.to_s.upcase, value: state_symbol.to_s
+ value state_symbol.to_s.upcase,
+ description: ::Ci::Pipeline::STATUSES_DESCRIPTION[state_symbol],
+ value: state_symbol.to_s
end
end
end
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index 49be200a788..2e83f6c1f5a 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -81,6 +81,20 @@ module Types
description: 'Jobs belonging to the pipeline.',
resolver: ::Resolvers::Ci::JobsResolver
+ field :job,
+ type: ::Types::Ci::JobType,
+ null: true,
+ description: 'A specific job in this pipeline, either by name or ID.' do
+ argument :id,
+ type: ::Types::GlobalIDType[::CommitStatus],
+ required: false,
+ description: 'ID of the job.'
+ argument :name,
+ type: ::GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Name of the job.'
+ end
+
field :source_job, Types::Ci::JobType, null: true,
description: 'Job where pipeline was triggered from.'
@@ -104,8 +118,24 @@ module Types
field :active, GraphQL::BOOLEAN_TYPE, null: false, method: :active?,
description: 'Indicates if the pipeline is active.'
+ field :uses_needs, GraphQL::BOOLEAN_TYPE, null: true,
+ method: :uses_needs?,
+ description: 'Indicates if the pipeline has jobs with `needs` dependencies.'
+
+ field :test_report_summary,
+ Types::Ci::TestReportSummaryType,
+ null: false,
+ description: 'Summary of the test report generated by the pipeline.',
+ resolver: Resolvers::Ci::TestReportSummaryResolver
+
+ field :test_suite,
+ Types::Ci::TestSuiteType,
+ null: true,
+ description: 'A specific test suite in a pipeline test report.',
+ resolver: Resolvers::Ci::TestSuiteResolver
+
def detailed_status
- object.detailed_status(context[:current_user])
+ object.detailed_status(current_user)
end
def user
@@ -119,6 +149,19 @@ module Types
def path
::Gitlab::Routing.url_helpers.project_pipeline_path(object.project, object)
end
+
+ def job(id: nil, name: nil)
+ raise ::Gitlab::Graphql::Errors::ArgumentError, 'One of id or name is required' unless id || name
+
+ if id
+ id = ::Types::GlobalIDType[::CommitStatus].coerce_isolated_input(id) if id
+ pipeline.statuses.id_in(id.model_id)
+ else
+ pipeline.statuses.by_name(name)
+ end.take # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ alias_method :pipeline, :object
end
end
end
diff --git a/app/graphql/types/ci/recent_failures_type.rb b/app/graphql/types/ci/recent_failures_type.rb
new file mode 100644
index 00000000000..eeff7222762
--- /dev/null
+++ b/app/graphql/types/ci/recent_failures_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class RecentFailuresType < BaseObject
+ graphql_name 'RecentFailures'
+ description 'Recent failure history of a test case.'
+
+ connection_type_class(Types::CountableConnectionType)
+
+ field :count, GraphQL::INT_TYPE, null: true,
+ description: 'Number of times the test case has failed in the past 14 days.'
+
+ field :base_branch, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the base branch of the project.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb
index 836f2430890..56b4f248697 100644
--- a/app/graphql/types/ci/stage_type.rb
+++ b/app/graphql/types/ci/stage_type.rb
@@ -12,10 +12,13 @@ module Types
extras: [:lookahead],
description: 'Group of jobs for the stage.'
field :detailed_status, Types::Ci::DetailedStatusType, null: true,
- description: 'Detailed status of the stage.'
+ description: 'Detailed status of the stage.'
+ field :jobs, Ci::JobType.connection_type, null: true,
+ description: 'Jobs for the stage.',
+ method: 'latest_statuses'
def detailed_status
- object.detailed_status(context[:current_user])
+ object.detailed_status(current_user)
end
# Issues one query per pipeline
@@ -33,6 +36,34 @@ module Types
jobs_for_pipeline(pl, indexed.keys, include_needs).each do |stage_id, statuses|
key = indexed[stage_id]
groups = ::Ci::Group.fabricate(project, key.stage, statuses)
+
+ if Feature.enabled?(:ci_no_empty_groups, project)
+ groups.each do |group|
+ rejected = group.jobs.reject { |job| Ability.allowed?(current_user, :read_commit_status, job) }
+ group.jobs.select! { |job| Ability.allowed?(current_user, :read_commit_status, job) }
+ next unless group.jobs.empty?
+
+ exc = StandardError.new('Empty Ci::Group')
+ traces = rejected.map do |job|
+ trace = []
+ policy = Ability.policy_for(current_user, job)
+ policy.debug(:read_commit_status, trace)
+ trace
+ end
+ extra = {
+ current_user_id: current_user&.id,
+ project_id: project.id,
+ pipeline_id: pl.id,
+ stage_id: stage_id,
+ group_name: group.name,
+ rejected_job_ids: rejected.map(&:id),
+ rejected_traces: traces
+ }
+ Gitlab::ErrorTracking.track_exception(exc, extra)
+ end
+ groups.reject! { |group| group.jobs.empty? }
+ end
+
loader.call(key, groups)
end
end
diff --git a/app/graphql/types/ci/test_case_status_enum.rb b/app/graphql/types/ci/test_case_status_enum.rb
new file mode 100644
index 00000000000..6a5f8bc2a59
--- /dev/null
+++ b/app/graphql/types/ci/test_case_status_enum.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class TestCaseStatusEnum < BaseEnum
+ graphql_name 'TestCaseStatus'
+
+ ::Gitlab::Ci::Reports::TestCase::STATUS_TYPES.each do |status|
+ value status,
+ description: "Test case that has a status of #{status}.",
+ value: status
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/test_case_type.rb b/app/graphql/types/ci/test_case_type.rb
new file mode 100644
index 00000000000..9cc3f918125
--- /dev/null
+++ b/app/graphql/types/ci/test_case_type.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class TestCaseType < BaseObject
+ graphql_name 'TestCase'
+ description 'Test case in pipeline test report.'
+
+ connection_type_class(Types::CountableConnectionType)
+
+ field :status, Types::Ci::TestCaseStatusEnum, null: true,
+ description: "Status of the test case (#{::Gitlab::Ci::Reports::TestCase::STATUS_TYPES.join(', ')})."
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the test case.'
+
+ field :classname, GraphQL::STRING_TYPE, null: true,
+ description: 'Classname of the test case.'
+
+ field :execution_time, GraphQL::FLOAT_TYPE, null: true,
+ description: 'Test case execution time in seconds.'
+
+ field :file, GraphQL::STRING_TYPE, null: true,
+ description: 'Path to the file of the test case.'
+
+ field :attachment_url, GraphQL::STRING_TYPE, null: true,
+ description: 'URL of the test case attachment file.'
+
+ field :system_output, GraphQL::STRING_TYPE, null: true,
+ description: 'System output of the test case.'
+
+ field :stack_trace, GraphQL::STRING_TYPE, null: true,
+ description: 'Stack trace of the test case.'
+
+ field :recent_failures, Types::Ci::RecentFailuresType, null: true,
+ description: 'Recent failure history of the test case on the base branch.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ci/test_report_summary_type.rb b/app/graphql/types/ci/test_report_summary_type.rb
new file mode 100644
index 00000000000..87207c8a765
--- /dev/null
+++ b/app/graphql/types/ci/test_report_summary_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ # This is presented through `PipelineType` that has its own authorization
+ class TestReportSummaryType < BaseObject
+ graphql_name 'TestReportSummary'
+ description 'Test report for a pipeline'
+
+ field :total, Types::Ci::TestReportTotalType, null: false,
+ description: 'Total report statistics for a pipeline test report.'
+
+ field :test_suites, Types::Ci::TestSuiteSummaryType.connection_type, null: false,
+ description: 'Test suites belonging to a pipeline test report.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ci/test_report_total_type.rb b/app/graphql/types/ci/test_report_total_type.rb
new file mode 100644
index 00000000000..1123734adc3
--- /dev/null
+++ b/app/graphql/types/ci/test_report_total_type.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class TestReportTotalType < BaseObject
+ graphql_name 'TestReportTotal'
+ description 'Total test report statistics.'
+
+ field :time, GraphQL::FLOAT_TYPE, null: true,
+ description: 'Total duration of the tests.'
+
+ field :count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of the test cases.'
+
+ field :success, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that succeeded.'
+
+ field :failed, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that failed.'
+
+ field :skipped, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that were skipped.'
+
+ field :error, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that had an error.'
+
+ field :suite_error, GraphQL::STRING_TYPE, null: true,
+ description: 'Test suite error message.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ci/test_suite_summary_type.rb b/app/graphql/types/ci/test_suite_summary_type.rb
new file mode 100644
index 00000000000..a80a9179cb4
--- /dev/null
+++ b/app/graphql/types/ci/test_suite_summary_type.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class TestSuiteSummaryType < BaseObject
+ graphql_name 'TestSuiteSummary'
+ description 'Test suite summary in a pipeline test report.'
+
+ connection_type_class(Types::CountableConnectionType)
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the test suite.'
+
+ field :total_time, GraphQL::FLOAT_TYPE, null: true,
+ description: 'Total duration of the tests in the test suite.'
+
+ field :total_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of the test cases in the test suite.'
+
+ field :success_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that succeeded in the test suite.'
+
+ field :failed_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that failed in the test suite.'
+
+ field :skipped_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that were skipped in the test suite.'
+
+ field :error_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that had an error.'
+
+ field :suite_error, GraphQL::STRING_TYPE, null: true,
+ description: 'Test suite error message.'
+
+ field :build_ids, [GraphQL::ID_TYPE], null: true,
+ description: 'IDs of the builds used to run the test suite.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ci/test_suite_type.rb b/app/graphql/types/ci/test_suite_type.rb
new file mode 100644
index 00000000000..7d4c01da81b
--- /dev/null
+++ b/app/graphql/types/ci/test_suite_type.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class TestSuiteType < BaseObject
+ graphql_name 'TestSuite'
+ description 'Test suite in a pipeline test report.'
+
+ connection_type_class(Types::CountableConnectionType)
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the test suite.'
+
+ field :total_time, GraphQL::FLOAT_TYPE, null: true,
+ description: 'Total duration of the tests in the test suite.'
+
+ field :total_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of the test cases in the test suite.'
+
+ field :success_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that succeeded in the test suite.'
+
+ field :failed_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that failed in the test suite.'
+
+ field :skipped_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that were skipped in the test suite.'
+
+ field :error_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of test cases that had an error.'
+
+ field :suite_error, GraphQL::STRING_TYPE, null: true,
+ description: 'Test suite error message.'
+
+ field :test_cases, Types::Ci::TestCaseType.connection_type, null: true,
+ description: 'Test cases in the test suite.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/concerns/find_closest.rb b/app/graphql/types/concerns/find_closest.rb
new file mode 100644
index 00000000000..1d76e872364
--- /dev/null
+++ b/app/graphql/types/concerns/find_closest.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module FindClosest
+ # Find the closest node of a given type above this node, and return the domain object
+ def closest_parent(type, parent)
+ parent = parent.try(:parent) while parent && parent.object.class != type
+ return unless parent
+
+ parent.object.object
+ end
+end
diff --git a/app/graphql/types/concerns/gitlab_style_deprecations.rb b/app/graphql/types/concerns/gitlab_style_deprecations.rb
index ad195354930..802562ed958 100644
--- a/app/graphql/types/concerns/gitlab_style_deprecations.rb
+++ b/app/graphql/types/concerns/gitlab_style_deprecations.rb
@@ -7,25 +7,21 @@ module GitlabStyleDeprecations
private
+ # Mutate the arguments, returns the deprecation
def gitlab_deprecation(kwargs)
if kwargs[:deprecation_reason].present?
raise ArgumentError, 'Use `deprecated` property instead of `deprecation_reason`. ' \
'See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-fields-arguments-and-enum-values'
end
- deprecation = kwargs.delete(:deprecated)
- return kwargs unless deprecation
+ deprecation = ::Gitlab::Graphql::Deprecation.parse(kwargs.delete(:deprecated))
+ return unless deprecation
- milestone, reason = deprecation.values_at(:milestone, :reason).map(&:presence)
+ raise ArgumentError, "Bad deprecation. #{deprecation.errors.full_messages.to_sentence}" unless deprecation.valid?
- raise ArgumentError, 'Please provide a `milestone` within `deprecated`' unless milestone
- raise ArgumentError, 'Please provide a `reason` within `deprecated`' unless reason
- raise ArgumentError, '`milestone` must be a `String`' unless milestone.is_a?(String)
+ kwargs[:deprecation_reason] = deprecation.deprecation_reason
+ kwargs[:description] = deprecation.edit_description(kwargs[:description])
- deprecated_in = "Deprecated in #{milestone}"
- kwargs[:deprecation_reason] = "#{reason}. #{deprecated_in}."
- kwargs[:description] += " #{deprecated_in}: #{reason}." if kwargs[:description]
-
- kwargs
+ deprecation
end
end
diff --git a/app/graphql/types/global_id_type.rb b/app/graphql/types/global_id_type.rb
index 750bd1bfe8d..79061df7282 100644
--- a/app/graphql/types/global_id_type.rb
+++ b/app/graphql/types/global_id_type.rb
@@ -23,7 +23,7 @@ module Types
A global identifier.
A global identifier represents an object uniquely across the application.
- An example of such an identifier is "gid://gitlab/User/1".
+ An example of such an identifier is `"gid://gitlab/User/1"`.
Global identifiers are encoded as strings.
DESC
@@ -67,6 +67,17 @@ module Types
graphql_name
end
+ define_singleton_method(:as) do |new_name|
+ if @renamed && graphql_name != new_name
+ raise "Conflicting names for ID of #{model_class.name}: " \
+ "#{graphql_name} and #{new_name}"
+ end
+
+ @renamed = true
+ graphql_name(new_name)
+ self
+ end
+
define_singleton_method(:coerce_result) do |gid, ctx|
global_id = ::Gitlab::GlobalId.as_global_id(gid, model_name: model_class.name)
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index 7a84e76657b..a44281b2bdf 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -8,39 +8,65 @@ module Types
expose_permissions Types::PermissionTypes::Group
- field :web_url, GraphQL::STRING_TYPE, null: false,
+ field :web_url,
+ type: GraphQL::STRING_TYPE,
+ null: false,
description: 'Web URL of the group.'
- field :avatar_url, GraphQL::STRING_TYPE, null: true,
+ field :avatar_url,
+ type: GraphQL::STRING_TYPE,
+ null: true,
description: 'Avatar URL of the group.'
- field :custom_emoji, Types::CustomEmojiType.connection_type, null: true,
+ field :custom_emoji,
+ type: Types::CustomEmojiType.connection_type,
+ null: true,
description: 'Custom emoji within this namespace.',
feature_flag: :custom_emoji
- field :share_with_group_lock, GraphQL::BOOLEAN_TYPE, null: true,
+ field :share_with_group_lock,
+ type: GraphQL::BOOLEAN_TYPE,
+ null: true,
description: 'Indicates if sharing a project with another group within this group is prevented.'
- field :project_creation_level, GraphQL::STRING_TYPE, null: true, method: :project_creation_level_str,
+ field :project_creation_level,
+ type: GraphQL::STRING_TYPE,
+ null: true,
+ method: :project_creation_level_str,
description: 'The permission level required to create projects in the group.'
- field :subgroup_creation_level, GraphQL::STRING_TYPE, null: true, method: :subgroup_creation_level_str,
+ field :subgroup_creation_level,
+ type: GraphQL::STRING_TYPE,
+ null: true,
+ method: :subgroup_creation_level_str,
description: 'The permission level required to create subgroups within the group.'
- field :require_two_factor_authentication, GraphQL::BOOLEAN_TYPE, null: true,
+ field :require_two_factor_authentication,
+ type: GraphQL::BOOLEAN_TYPE,
+ null: true,
description: 'Indicates if all users in this group are required to set up two-factor authentication.'
- field :two_factor_grace_period, GraphQL::INT_TYPE, null: true,
+ field :two_factor_grace_period,
+ type: GraphQL::INT_TYPE,
+ null: true,
description: 'Time before two-factor authentication is enforced.'
- field :auto_devops_enabled, GraphQL::BOOLEAN_TYPE, null: true,
+ field :auto_devops_enabled,
+ type: GraphQL::BOOLEAN_TYPE,
+ null: true,
description: 'Indicates whether Auto DevOps is enabled for all projects within this group.'
- field :emails_disabled, GraphQL::BOOLEAN_TYPE, null: true,
+ field :emails_disabled,
+ type: GraphQL::BOOLEAN_TYPE,
+ null: true,
description: 'Indicates if a group has email notifications disabled.'
- field :mentions_disabled, GraphQL::BOOLEAN_TYPE, null: true,
+ field :mentions_disabled,
+ type: GraphQL::BOOLEAN_TYPE,
+ null: true,
description: 'Indicates if a group is disabled from getting mentioned.'
- field :parent, GroupType, null: true,
+ field :parent,
+ type: GroupType,
+ null: true,
description: 'Parent group.'
field :issues,
@@ -55,7 +81,7 @@ module Types
description: 'Merge requests for projects in this group.',
resolver: Resolvers::GroupMergeRequestsResolver
- field :milestones, Types::MilestoneType.connection_type, null: true,
+ field :milestones,
description: 'Milestones of the group.',
resolver: Resolvers::GroupMilestonesResolver
@@ -76,9 +102,10 @@ module Types
Types::LabelType,
null: true,
description: 'A label available on this group.' do
- argument :title, GraphQL::STRING_TYPE,
- required: true,
- description: 'Title of the label.'
+ argument :title,
+ type: GraphQL::STRING_TYPE,
+ required: true,
+ description: 'Title of the label.'
end
field :group_members,
@@ -92,7 +119,9 @@ module Types
resolver: Resolvers::ContainerRepositoriesResolver,
authorize: :read_container_image
- field :container_repositories_count, GraphQL::INT_TYPE, null: false,
+ field :container_repositories_count,
+ type: GraphQL::INT_TYPE,
+ null: false,
description: 'Number of container repositories in the group.'
field :packages,
@@ -114,6 +143,12 @@ module Types
description: 'Labels available on this group.',
resolver: Resolvers::GroupLabelsResolver
+ field :timelogs, ::Types::TimelogType.connection_type, null: false,
+ description: 'Time logged on issues in the group and its subgroups.',
+ extras: [:lookahead],
+ complexity: 5,
+ resolver: ::Resolvers::TimelogResolver
+
def avatar_url
object.avatar_url(only_path: false)
end
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index f15ab69f2d4..34c824fe9fb 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -124,6 +124,9 @@ module Types
field :create_note_email, GraphQL::STRING_TYPE, null: true,
description: 'User specific email address for the issue.'
+ field :timelogs, Types::TimelogType.connection_type, null: false,
+ description: 'Timelogs on the issue.'
+
def author
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
end
diff --git a/app/graphql/types/issues/negated_issue_filter_input_type.rb b/app/graphql/types/issues/negated_issue_filter_input_type.rb
new file mode 100644
index 00000000000..10bf6f21792
--- /dev/null
+++ b/app/graphql/types/issues/negated_issue_filter_input_type.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Types
+ module Issues
+ class NegatedIssueFilterInputType < BaseInputObject
+ graphql_name 'NegatedIssueFilterInput'
+
+ argument :iids, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'List of IIDs of issues to exclude. For example, [1, 2].'
+ argument :label_name, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Labels not applied to this issue.'
+ argument :milestone_title, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Milestone not applied to this issue.'
+ argument :assignee_usernames, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Usernames of users not assigned to the issue.'
+ argument :assignee_id, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'ID of a user not assigned to the issues.'
+ end
+ end
+end
+
+Types::Issues::NegatedIssueFilterInputType.prepend_if_ee('::EE::Types::Issues::NegatedIssueFilterInputType')
diff --git a/app/graphql/types/jira_users_mapping_input_type.rb b/app/graphql/types/jira_users_mapping_input_type.rb
index 61e3240ecf3..32640b9cb17 100644
--- a/app/graphql/types/jira_users_mapping_input_type.rb
+++ b/app/graphql/types/jira_users_mapping_input_type.rb
@@ -5,12 +5,12 @@ module Types
graphql_name 'JiraUsersMappingInputType'
argument :jira_account_id,
- GraphQL::STRING_TYPE,
- required: true,
- description: 'Jira account ID of the user.'
+ GraphQL::STRING_TYPE,
+ required: true,
+ description: 'Jira account ID of the user.'
argument :gitlab_id,
- GraphQL::INT_TYPE,
- required: false,
- description: 'Id of the GitLab user.'
+ GraphQL::INT_TYPE,
+ required: false,
+ description: 'ID of the GitLab user.'
end
end
diff --git a/app/graphql/types/merge_request_review_state_enum.rb b/app/graphql/types/merge_request_review_state_enum.rb
new file mode 100644
index 00000000000..45f97758425
--- /dev/null
+++ b/app/graphql/types/merge_request_review_state_enum.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ class MergeRequestReviewStateEnum < BaseEnum
+ graphql_name 'MergeRequestReviewState'
+ description 'State of a review of a GitLab merge request.'
+
+ from_rails_enum(::MergeRequestReviewer.states,
+ description: "The merge request is %{name}.")
+ end
+end
diff --git a/app/graphql/types/merge_request_state_enum.rb b/app/graphql/types/merge_request_state_enum.rb
index a2d7bd0306c..bcf18b836de 100644
--- a/app/graphql/types/merge_request_state_enum.rb
+++ b/app/graphql/types/merge_request_state_enum.rb
@@ -5,6 +5,6 @@ module Types
graphql_name 'MergeRequestState'
description 'State of a GitLab merge request'
- value 'merged', description: "Merge Request has been merged."
+ value 'merged', description: "Merge request has been merged."
end
end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 449286915f2..c8ccf9d8aff 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -132,7 +132,10 @@ module Types
description: 'The milestone of the merge request.'
field :assignees, Types::UserType.connection_type, null: true, complexity: 5,
description: 'Assignees of the merge request.'
- field :reviewers, Types::UserType.connection_type, null: true, complexity: 5,
+ field :reviewers,
+ type: Types::MergeRequests::ReviewerType.connection_type,
+ null: true,
+ complexity: 5,
description: 'Users from whom a review has been requested.'
field :author, Types::UserType, null: true,
description: 'User who created this merge request.'
@@ -183,6 +186,8 @@ module Types
description: 'Selected auto merge strategy.'
field :merge_user, Types::UserType, null: true,
description: 'User who merged this merge request.'
+ field :timelogs, Types::TimelogType.connection_type, null: false,
+ description: 'Timelogs on the merge request.'
def approved_by
object.approved_by_users
diff --git a/app/graphql/types/merge_requests/reviewer_type.rb b/app/graphql/types/merge_requests/reviewer_type.rb
new file mode 100644
index 00000000000..09ced39844a
--- /dev/null
+++ b/app/graphql/types/merge_requests/reviewer_type.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Types
+ module MergeRequests
+ class ReviewerType < ::Types::UserType
+ include FindClosest
+
+ graphql_name 'MergeRequestReviewer'
+ description 'A user from whom a merge request review has been requested.'
+ authorize :read_user
+
+ field :merge_request_interaction,
+ type: ::Types::UserMergeRequestInteractionType,
+ null: true,
+ extras: [:parent],
+ description: "Details of this user's interactions with the merge request."
+
+ def merge_request_interaction(parent:)
+ merge_request = closest_parent(::Types::MergeRequestType, parent)
+ return unless merge_request
+
+ Users::MergeRequestInteraction.new(user: object, merge_request: merge_request)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/milestone_type.rb b/app/graphql/types/milestone_type.rb
index c3816116e2b..91a5109c748 100644
--- a/app/graphql/types/milestone_type.rb
+++ b/app/graphql/types/milestone_type.rb
@@ -14,6 +14,9 @@ module Types
field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the milestone.'
+ field :iid, GraphQL::ID_TYPE, null: false,
+ description: "Internal ID of the milestone."
+
field :title, GraphQL::STRING_TYPE, null: false,
description: 'Title of the milestone.'
diff --git a/app/graphql/types/mutation_operation_mode_enum.rb b/app/graphql/types/mutation_operation_mode_enum.rb
index 75c1d7cd4a6..08214eebc7e 100644
--- a/app/graphql/types/mutation_operation_mode_enum.rb
+++ b/app/graphql/types/mutation_operation_mode_enum.rb
@@ -10,5 +10,13 @@ module Types
value 'REPLACE', 'Performs a replace operation.'
value 'APPEND', 'Performs an append operation.'
value 'REMOVE', 'Performs a removal operation.'
+
+ def self.default_mode
+ enum[:replace]
+ end
+
+ def self.transform_modes
+ enum.values_at(:remove, :append)
+ end
end
end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 76ffddf416f..5a9c7b32deb 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -68,6 +68,7 @@ module Types
mount_mutation Mutations::Releases::Delete
mount_mutation Mutations::ReleaseAssetLinks::Create
mount_mutation Mutations::ReleaseAssetLinks::Update
+ mount_mutation Mutations::ReleaseAssetLinks::Delete
mount_mutation Mutations::Terraform::State::Delete
mount_mutation Mutations::Terraform::State::Lock
mount_mutation Mutations::Terraform::State::Unlock
diff --git a/app/graphql/types/packages/conan/file_metadatum_type.rb b/app/graphql/types/packages/conan/file_metadatum_type.rb
new file mode 100644
index 00000000000..97d5abe6ba4
--- /dev/null
+++ b/app/graphql/types/packages/conan/file_metadatum_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ module Conan
+ class FileMetadatumType < BaseObject
+ graphql_name 'ConanFileMetadata'
+ description 'Conan file metadata'
+
+ implements Types::Packages::FileMetadataType
+
+ authorize :read_package
+
+ field :id, ::Types::GlobalIDType[::Packages::Conan::FileMetadatum], null: false, description: 'ID of the metadatum.'
+ field :recipe_revision, GraphQL::STRING_TYPE, null: false, description: 'Revision of the Conan recipe.'
+ field :package_revision, GraphQL::STRING_TYPE, null: true, description: 'Revision of the package.'
+ field :conan_package_reference, GraphQL::STRING_TYPE, null: true, description: 'Reference of the Conan package.'
+ field :conan_file_type, ::Types::Packages::Conan::MetadatumFileTypeEnum, null: false, description: 'Type of the Conan file.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/packages/conan/metadatum_file_type_enum.rb b/app/graphql/types/packages/conan/metadatum_file_type_enum.rb
new file mode 100644
index 00000000000..d8ec3a44d4d
--- /dev/null
+++ b/app/graphql/types/packages/conan/metadatum_file_type_enum.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ module Conan
+ class MetadatumFileTypeEnum < BaseEnum
+ graphql_name 'ConanMetadatumFileTypeEnum'
+ description 'Conan file types'
+
+ ::Packages::Conan::FileMetadatum.conan_file_types.keys.each do |file|
+ value file.upcase, value: file, description: "A #{file.humanize(capitalize: false)} type."
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/packages/conan/metadatum_type.rb b/app/graphql/types/packages/conan/metadatum_type.rb
new file mode 100644
index 00000000000..00b84235d27
--- /dev/null
+++ b/app/graphql/types/packages/conan/metadatum_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ module Conan
+ class MetadatumType < BaseObject
+ graphql_name 'ConanMetadata'
+ description 'Conan metadata'
+
+ authorize :read_package
+
+ field :id, ::Types::GlobalIDType[::Packages::Conan::Metadatum], null: false, description: 'ID of the metadatum.'
+ field :created_at, Types::TimeType, null: false, description: 'Date of creation.'
+ field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.'
+ field :package_username, GraphQL::STRING_TYPE, null: false, description: 'Username of the Conan package.'
+ field :package_channel, GraphQL::STRING_TYPE, null: false, description: 'Channel of the Conan package.'
+ field :recipe, GraphQL::STRING_TYPE, null: false, description: 'Recipe of the Conan package.'
+ field :recipe_path, GraphQL::STRING_TYPE, null: false, description: 'Recipe path of the Conan package.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/packages/file_metadata_type.rb b/app/graphql/types/packages/file_metadata_type.rb
new file mode 100644
index 00000000000..46ccb424218
--- /dev/null
+++ b/app/graphql/types/packages/file_metadata_type.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ module FileMetadataType
+ include ::Types::BaseInterface
+ graphql_name 'PackageFileMetadata'
+ description 'Represents metadata associated with a Package file'
+
+ field :created_at, ::Types::TimeType, null: false, description: 'Date of creation.'
+ field :updated_at, ::Types::TimeType, null: false, description: 'Date of most recent update.'
+
+ def self.resolve_type(object, context)
+ case object
+ when ::Packages::Conan::FileMetadatum
+ ::Types::Packages::Conan::FileMetadatumType
+ else
+ # NOTE: This method must be kept in sync with `PackageFileType#file_metadata`,
+ # which must never produce data that this discriminator cannot handle.
+ raise 'Unsupported file metadata type'
+ end
+ end
+
+ orphan_types Types::Packages::Conan::FileMetadatumType
+ end
+ end
+end
diff --git a/app/graphql/types/packages/metadata_type.rb b/app/graphql/types/packages/metadata_type.rb
index 26c43b51a69..4ab6707df88 100644
--- a/app/graphql/types/packages/metadata_type.rb
+++ b/app/graphql/types/packages/metadata_type.rb
@@ -6,12 +6,14 @@ module Types
graphql_name 'PackageMetadata'
description 'Represents metadata associated with a Package'
- possible_types ::Types::Packages::Composer::MetadatumType
+ possible_types ::Types::Packages::Composer::MetadatumType, ::Types::Packages::Conan::MetadatumType
def self.resolve_type(object, context)
case object
when ::Packages::Composer::Metadatum
::Types::Packages::Composer::MetadatumType
+ when ::Packages::Conan::Metadatum
+ ::Types::Packages::Conan::MetadatumType
else
# NOTE: This method must be kept in sync with `PackageWithoutVersionsType#metadata`,
# which must never produce data that this discriminator cannot handle.
diff --git a/app/graphql/types/packages/package_details_type.rb b/app/graphql/types/packages/package_details_type.rb
new file mode 100644
index 00000000000..510b7e2ba41
--- /dev/null
+++ b/app/graphql/types/packages/package_details_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ class PackageDetailsType < PackageType
+ graphql_name 'PackageDetailsType'
+ description 'Represents a package details in the Package Registry. Note that this type is in beta and susceptible to changes'
+ authorize :read_package
+
+ field :versions, ::Types::Packages::PackageType.connection_type, null: true,
+ description: 'The other versions of the package.'
+
+ field :package_files, Types::Packages::PackageFileType.connection_type, null: true, description: 'Package files.'
+
+ def versions
+ object.versions
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/packages/package_file_type.rb b/app/graphql/types/packages/package_file_type.rb
new file mode 100644
index 00000000000..e9e38559626
--- /dev/null
+++ b/app/graphql/types/packages/package_file_type.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ class PackageFileType < BaseObject
+ graphql_name 'PackageFile'
+ description 'Represents a package file'
+ authorize :read_package
+
+ field :id, ::Types::GlobalIDType[::Packages::PackageFile], null: false, description: 'ID of the file.'
+ field :created_at, Types::TimeType, null: false, description: 'The created date.'
+ field :updated_at, Types::TimeType, null: false, description: 'The updated date.'
+ field :size, GraphQL::STRING_TYPE, null: false, description: 'Size of the package file.'
+ field :file_name, GraphQL::STRING_TYPE, null: false, description: 'Name of the package file.'
+ field :download_path, GraphQL::STRING_TYPE, null: false, description: 'Download path of the package file.'
+ field :file_md5, GraphQL::STRING_TYPE, null: true, description: 'Md5 of the package file.'
+ field :file_sha1, GraphQL::STRING_TYPE, null: true, description: 'Sha1 of the package file.'
+ field :file_sha256, GraphQL::STRING_TYPE, null: true, description: 'Sha256 of the package file.'
+ field :file_metadata, Types::Packages::FileMetadataType, null: true,
+ description: 'File metadata.'
+
+ # NOTE: This method must be kept in sync with the union
+ # type: `Types::Packages::FileMetadataType`.
+ #
+ # `Types::Packages::FileMetadataType.resolve_type(metadata, ctx)` must never raise.
+ def file_metadata
+ case object.package.package_type
+ when 'conan'
+ object.conan_file_metadatum
+ else
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/packages/package_type.rb b/app/graphql/types/packages/package_type.rb
index 331898a1e84..a263ca1577a 100644
--- a/app/graphql/types/packages/package_type.rb
+++ b/app/graphql/types/packages/package_type.rb
@@ -2,13 +2,52 @@
module Types
module Packages
- class PackageType < PackageWithoutVersionsType
+ class PackageType < ::Types::BaseObject
graphql_name 'Package'
- description 'Represents a package in the Package Registry'
+ description 'Represents a package in the Package Registry. Note that this type is in beta and susceptible to changes'
+
authorize :read_package
- field :versions, ::Types::Packages::PackageWithoutVersionsType.connection_type, null: true,
- description: 'The other versions of the package.'
+ field :id, ::Types::GlobalIDType[::Packages::Package], null: false,
+ description: 'ID of the package.'
+
+ field :name, GraphQL::STRING_TYPE, null: false, description: 'Name of the package.'
+ field :created_at, Types::TimeType, null: false, description: 'Date of creation.'
+ field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.'
+ field :version, GraphQL::STRING_TYPE, null: true, description: 'Version string.'
+ field :package_type, Types::Packages::PackageTypeEnum, null: false, description: 'Package type.'
+ field :tags, Types::Packages::PackageTagType.connection_type, null: true, description: 'Package tags.'
+ field :project, Types::ProjectType, null: false, description: 'Project where the package is stored.'
+ field :pipelines, Types::Ci::PipelineType.connection_type, null: true,
+ description: 'Pipelines that built the package.'
+ field :metadata, Types::Packages::MetadataType, null: true,
+ description: 'Package metadata.'
+ field :versions, ::Types::Packages::PackageType.connection_type, null: true,
+ description: 'The other versions of the package.',
+ deprecated: { reason: 'This field is now only returned in the PackageDetailsType', milestone: '13.11' }
+
+ def project
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
+ end
+
+ def versions
+ []
+ end
+
+ # NOTE: This method must be kept in sync with the union
+ # type: `Types::Packages::MetadataType`.
+ #
+ # `Types::Packages::MetadataType.resolve_type(metadata, ctx)` must never raise.
+ def metadata
+ case object.package_type
+ when 'composer'
+ object.composer_metadatum
+ when 'conan'
+ object.conan_metadatum
+ else
+ nil
+ end
+ end
end
end
end
diff --git a/app/graphql/types/packages/package_without_versions_type.rb b/app/graphql/types/packages/package_without_versions_type.rb
deleted file mode 100644
index 9c6bb37e6cc..00000000000
--- a/app/graphql/types/packages/package_without_versions_type.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module Packages
- class PackageWithoutVersionsType < ::Types::BaseObject
- graphql_name 'PackageWithoutVersions'
- description 'Represents a version of a package in the Package Registry'
-
- authorize :read_package
-
- field :id, ::Types::GlobalIDType[::Packages::Package], null: false,
- description: 'ID of the package.'
-
- field :name, GraphQL::STRING_TYPE, null: false, description: 'Name of the package.'
- field :created_at, Types::TimeType, null: false, description: 'Date of creation.'
- field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.'
- field :version, GraphQL::STRING_TYPE, null: true, description: 'Version string.'
- field :package_type, Types::Packages::PackageTypeEnum, null: false, description: 'Package type.'
- field :tags, Types::Packages::PackageTagType.connection_type, null: true, description: 'Package tags.'
- field :project, Types::ProjectType, null: false, description: 'Project where the package is stored.'
- field :pipelines, Types::Ci::PipelineType.connection_type, null: true,
- description: 'Pipelines that built the package.'
- field :metadata, Types::Packages::MetadataType, null: true,
- description: 'Package metadata.'
-
- def project
- Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
- end
-
- # NOTE: This method must be kept in sync with the union
- # type: `Types::Packages::MetadataType`.
- #
- # `Types::Packages::MetadataType.resolve_type(metadata, ctx)` must never raise.
- def metadata
- case object.package_type
- when 'composer'
- object.composer_metadatum
- else
- nil
- end
- end
- end
- end
-end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 9a3f2e311e6..21534f40499 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -183,6 +183,12 @@ module Types
description: 'Packages of the project.',
resolver: Resolvers::ProjectPackagesResolver
+ field :jobs,
+ Types::Ci::JobType.connection_type,
+ null: true,
+ description: 'Jobs of a project. This field can only be resolved for one project in any single request.',
+ resolver: Resolvers::ProjectJobsResolver
+
field :pipelines,
null: true,
description: 'Build pipelines of the project.',
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 74818bfcd42..8af0db644dd 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -55,7 +55,10 @@ module Types
field :container_repository, Types::ContainerRepositoryDetailsType,
null: true,
description: 'Find a container repository.' do
- argument :id, ::Types::GlobalIDType[::ContainerRepository], required: true, description: 'The global ID of the container repository.'
+ argument :id,
+ type: ::Types::GlobalIDType[::ContainerRepository],
+ required: true,
+ description: 'The global ID of the container repository.'
end
field :package,
@@ -72,9 +75,7 @@ module Types
description: 'Find users.',
resolver: Resolvers::UsersResolver
- field :echo, GraphQL::STRING_TYPE, null: false,
- description: 'Text to echo back.',
- resolver: Resolvers::EchoResolver
+ field :echo, resolver: Resolvers::EchoResolver
field :issue, Types::IssueType,
null: true,
@@ -82,11 +83,16 @@ module Types
argument :id, ::Types::GlobalIDType[::Issue], required: true, description: 'The global ID of the Issue.'
end
- field :instance_statistics_measurements, Types::Admin::Analytics::UsageTrends::MeasurementType.connection_type,
+ field :instance_statistics_measurements,
+ type: Types::Admin::Analytics::UsageTrends::MeasurementType.connection_type,
null: true,
description: 'Get statistics on the instance.',
- deprecated: { reason: 'This field was renamed. Use the `usageTrendsMeasurements` field instead', milestone: '13.10' },
- resolver: Resolvers::Admin::Analytics::UsageTrends::MeasurementsResolver
+ resolver: Resolvers::Admin::Analytics::UsageTrends::MeasurementsResolver,
+ deprecated: {
+ reason: :renamed,
+ replacement: 'Query.usageTrendsMeasurements',
+ milestone: '13.10'
+ }
field :usage_trends_measurements, Types::Admin::Analytics::UsageTrends::MeasurementType.connection_type,
null: true,
@@ -97,18 +103,10 @@ module Types
null: true,
description: 'CI related settings that apply to the entire instance.'
- field :runner_platforms, Types::Ci::RunnerPlatformType.connection_type,
- null: true, description: 'Supported runner platforms.',
- resolver: Resolvers::Ci::RunnerPlatformsResolver
-
- field :runner_setup, Types::Ci::RunnerSetupType, null: true,
- description: 'Get runner setup instructions.',
- resolver: Resolvers::Ci::RunnerSetupResolver
+ field :runner_platforms, resolver: Resolvers::Ci::RunnerPlatformsResolver
+ field :runner_setup, resolver: Resolvers::Ci::RunnerSetupResolver
- field :ci_config, Types::Ci::Config::ConfigType, null: true,
- description: 'Get linted and processed contents of a CI config. Should not be requested more than once per request.',
- resolver: Resolvers::Ci::ConfigResolver,
- complexity: 126 # AUTHENTICATED_COMPLEXITY / 2 + 1
+ field :ci_config, resolver: Resolvers::Ci::ConfigResolver, complexity: 126 # AUTHENTICATED_COMPLEXITY / 2 + 1
def design_management
DesignManagementObject.new(nil)
diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb
new file mode 100644
index 00000000000..912fc5f643a
--- /dev/null
+++ b/app/graphql/types/repository/blob_type.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+module Types
+ module Repository
+ # rubocop: disable Graphql/AuthorizeTypes
+ # This is presented through `Repository` that has its own authorization
+ class BlobType < BaseObject
+ present_using BlobPresenter
+
+ graphql_name 'RepositoryBlob'
+
+ field :id, GraphQL::ID_TYPE, null: false,
+ description: 'ID of the blob.'
+
+ field :oid, GraphQL::STRING_TYPE, null: false, method: :id,
+ description: 'OID of the blob.'
+
+ field :path, GraphQL::STRING_TYPE, null: false,
+ description: 'Path of the blob.'
+
+ field :name, GraphQL::STRING_TYPE,
+ description: 'Blob name.',
+ null: true
+
+ field :mode, type: GraphQL::STRING_TYPE,
+ description: 'Blob mode.',
+ null: true
+
+ field :lfs_oid, GraphQL::STRING_TYPE, null: true,
+ calls_gitaly: true,
+ description: 'LFS OID of the blob.'
+
+ field :web_path, GraphQL::STRING_TYPE, null: true,
+ description: 'Web path of the blob.'
+
+ def lfs_oid
+ Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(object.repository, object.id).find
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/repository_type.rb b/app/graphql/types/repository_type.rb
index e319a5f3124..963a4296c4f 100644
--- a/app/graphql/types/repository_type.rb
+++ b/app/graphql/types/repository_type.rb
@@ -14,5 +14,10 @@ module Types
description: 'Indicates a corresponding Git repository exists on disk.'
field :tree, Types::Tree::TreeType, null: true, resolver: Resolvers::TreeResolver, calls_gitaly: true,
description: 'Tree of the repository.'
+ field :blobs, Types::Repository::BlobType.connection_type, null: true, resolver: Resolvers::BlobsResolver, calls_gitaly: true,
+ description: 'Blobs contained within the repository'
+ field :branch_names, [GraphQL::STRING_TYPE], null: true, calls_gitaly: true,
+ complexity: 170, description: 'Names of branches available in this repository that match the search pattern.',
+ resolver: Resolvers::RepositoryBranchNamesResolver
end
end
diff --git a/app/graphql/types/sort_enum.rb b/app/graphql/types/sort_enum.rb
index ff994039b6d..cc04394004d 100644
--- a/app/graphql/types/sort_enum.rb
+++ b/app/graphql/types/sort_enum.rb
@@ -7,10 +7,34 @@ module Types
# Deprecated, as we prefer uppercase enums
# https://gitlab.com/groups/gitlab-org/-/epics/1838
- value 'updated_desc', 'Updated at descending order.', value: :updated_desc, deprecated: { reason: 'Use UPDATED_DESC', milestone: '13.5' }
- value 'updated_asc', 'Updated at ascending order.', value: :updated_asc, deprecated: { reason: 'Use UPDATED_ASC', milestone: '13.5' }
- value 'created_desc', 'Created at descending order.', value: :created_desc, deprecated: { reason: 'Use CREATED_DESC', milestone: '13.5' }
- value 'created_asc', 'Created at ascending order.', value: :created_asc, deprecated: { reason: 'Use CREATED_ASC', milestone: '13.5' }
+ value 'updated_desc', 'Updated at descending order.',
+ value: :updated_desc,
+ deprecated: {
+ reason: :renamed,
+ replacement: 'UPDATED_DESC',
+ milestone: '13.5'
+ }
+ value 'updated_asc', 'Updated at ascending order.',
+ value: :updated_asc,
+ deprecated: {
+ reason: :renamed,
+ replacement: 'UPDATED_ASC',
+ milestone: '13.5'
+ }
+ value 'created_desc', 'Created at descending order.',
+ value: :created_desc,
+ deprecated: {
+ reason: :renamed,
+ replacement: 'CREATED_DESC',
+ milestone: '13.5'
+ }
+ value 'created_asc', 'Created at ascending order.',
+ value: :created_asc,
+ deprecated: {
+ reason: :renamed,
+ replacement: 'CREATED_ASC',
+ milestone: '13.5'
+ }
value 'UPDATED_DESC', 'Updated at descending order.', value: :updated_desc
value 'UPDATED_ASC', 'Updated at ascending order.', value: :updated_asc
diff --git a/app/graphql/types/timelog_type.rb b/app/graphql/types/timelog_type.rb
new file mode 100644
index 00000000000..465e3c492bc
--- /dev/null
+++ b/app/graphql/types/timelog_type.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Types
+ class TimelogType < BaseObject
+ graphql_name 'Timelog'
+
+ authorize :read_group_timelogs
+
+ field :spent_at,
+ Types::TimeType,
+ null: true,
+ description: 'Timestamp of when the time tracked was spent at.'
+
+ field :time_spent,
+ GraphQL::INT_TYPE,
+ null: false,
+ description: 'The time spent displayed in seconds.'
+
+ field :user,
+ Types::UserType,
+ null: false,
+ description: 'The user that logged the time.'
+
+ field :issue,
+ Types::IssueType,
+ null: true,
+ description: 'The issue that logged time was added to.'
+
+ field :note,
+ Types::Notes::NoteType,
+ null: true,
+ description: 'The note where the quick action to add the logged time was executed.'
+
+ def user
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.user_id).find
+ end
+
+ def issue
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Issue, object.issue_id).find
+ end
+ end
+end
diff --git a/app/graphql/types/user_merge_request_interaction_type.rb b/app/graphql/types/user_merge_request_interaction_type.rb
new file mode 100644
index 00000000000..5ff0d79f13e
--- /dev/null
+++ b/app/graphql/types/user_merge_request_interaction_type.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Types
+ class UserMergeRequestInteractionType < BaseObject
+ graphql_name 'UserMergeRequestInteraction'
+ description <<~MD
+ Information about a merge request given a specific user.
+
+ This object has two parts to its state: a `User` and a `MergeRequest`. All
+ fields relate to interactions between the two entities.
+ MD
+
+ authorize :read_merge_request
+
+ field :can_merge,
+ type: ::GraphQL::BOOLEAN_TYPE,
+ null: false,
+ calls_gitaly: true,
+ method: :can_merge?,
+ description: 'Whether this user can merge this merge request.'
+
+ field :can_update,
+ type: ::GraphQL::BOOLEAN_TYPE,
+ null: false,
+ method: :can_update?,
+ description: 'Whether this user can update this merge request.'
+
+ field :review_state,
+ ::Types::MergeRequestReviewStateEnum,
+ null: true,
+ description: 'The state of the review by this user.'
+
+ field :reviewed,
+ type: ::GraphQL::BOOLEAN_TYPE,
+ null: false,
+ method: :reviewed?,
+ description: 'Whether this user has provided a review for this merge request.'
+
+ field :approved,
+ type: ::GraphQL::BOOLEAN_TYPE,
+ null: false,
+ method: :approved?,
+ description: 'Whether this user has approved this merge request.'
+ end
+end
+
+::Types::UserMergeRequestInteractionType.prepend_if_ee('EE::Types::UserMergeRequestInteractionType')
diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb
index 2cc7d379240..3d7db80ae11 100644
--- a/app/graphql/types/user_type.rb
+++ b/app/graphql/types/user_type.rb
@@ -3,6 +3,7 @@
module Types
class UserType < BaseObject
graphql_name 'User'
+ description 'Representation of a GitLab user.'
authorize :read_user
@@ -10,61 +11,87 @@ module Types
expose_permissions Types::PermissionTypes::User
- field :id, GraphQL::ID_TYPE, null: false,
+ field :id,
+ type: GraphQL::ID_TYPE,
+ null: false,
description: 'ID of the user.'
- field :bot, GraphQL::BOOLEAN_TYPE, null: false,
+ field :bot,
+ type: GraphQL::BOOLEAN_TYPE,
+ null: false,
description: 'Indicates if the user is a bot.',
method: :bot?
- field :username, GraphQL::STRING_TYPE, null: false,
+ field :username,
+ type: GraphQL::STRING_TYPE,
+ null: false,
description: 'Username of the user. Unique within this instance of GitLab.'
- field :name, GraphQL::STRING_TYPE, null: false,
+ field :name,
+ type: GraphQL::STRING_TYPE,
+ null: false,
description: 'Human-readable name of the user.'
- field :state, Types::UserStateEnum, null: false,
+ field :state,
+ type: Types::UserStateEnum,
+ null: false,
description: 'State of the user.'
- field :email, GraphQL::STRING_TYPE, null: true,
+ field :email,
+ type: GraphQL::STRING_TYPE,
+ null: true,
description: 'User email.', method: :public_email,
- deprecated: { reason: 'Use public_email', milestone: '13.7' }
- field :public_email, GraphQL::STRING_TYPE, null: true,
+ deprecated: { reason: :renamed, replacement: 'User.publicEmail', milestone: '13.7' }
+ field :public_email,
+ type: GraphQL::STRING_TYPE,
+ null: true,
description: "User's public email."
- field :avatar_url, GraphQL::STRING_TYPE, null: true,
+ field :avatar_url,
+ type: GraphQL::STRING_TYPE,
+ null: true,
description: "URL of the user's avatar."
- field :web_url, GraphQL::STRING_TYPE, null: false,
+ field :web_url,
+ type: GraphQL::STRING_TYPE,
+ null: false,
description: 'Web URL of the user.'
- field :web_path, GraphQL::STRING_TYPE, null: false,
+ field :web_path,
+ type: GraphQL::STRING_TYPE,
+ null: false,
description: 'Web path of the user.'
- field :todos, Types::TodoType.connection_type, null: false,
+ field :todos,
resolver: Resolvers::TodoResolver,
description: 'To-do items of the user.'
- field :group_memberships, Types::GroupMemberType.connection_type, null: true,
+ field :group_memberships,
+ type: Types::GroupMemberType.connection_type,
+ null: true,
description: 'Group memberships of the user.'
- field :group_count, GraphQL::INT_TYPE, null: true,
+ field :group_count,
resolver: Resolvers::Users::GroupCountResolver,
description: 'Group count for the user.',
feature_flag: :user_group_counts
- field :status, Types::UserStatusType, null: true,
- description: 'User status.'
- field :location, ::GraphQL::STRING_TYPE, null: true,
+ field :status,
+ type: Types::UserStatusType,
+ null: true,
+ description: 'User status.'
+ field :location,
+ type: ::GraphQL::STRING_TYPE,
+ null: true,
description: 'The location of the user.'
- field :project_memberships, Types::ProjectMemberType.connection_type, null: true,
+ field :project_memberships,
+ type: Types::ProjectMemberType.connection_type,
+ null: true,
description: 'Project memberships of the user.'
- field :starred_projects, Types::ProjectType.connection_type, null: true,
+ field :starred_projects,
description: 'Projects starred by the user.',
resolver: Resolvers::UserStarredProjectsResolver
# Merge request field: MRs can be authored, assigned, or assigned-for-review:
field :authored_merge_requests,
resolver: Resolvers::AuthoredMergeRequestsResolver,
- description: 'Merge Requests authored by the user.'
+ description: 'Merge requests authored by the user.'
field :assigned_merge_requests,
resolver: Resolvers::AssignedMergeRequestsResolver,
- description: 'Merge Requests assigned to the user.'
+ description: 'Merge requests assigned to the user.'
field :review_requested_merge_requests,
resolver: Resolvers::ReviewRequestedMergeRequestsResolver,
- description: 'Merge Requests assigned to the user for review.'
+ description: 'Merge requests assigned to the user for review.'
field :snippets,
- Types::SnippetType.connection_type,
- null: true,
description: 'Snippets authored by the user.',
resolver: Resolvers::Users::SnippetsResolver
field :callouts,