summaryrefslogtreecommitdiff
path: root/app/graphql
diff options
context:
space:
mode:
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/mutations/achievements/create.rb54
-rw-r--r--app/graphql/mutations/boards/lists/base_update.rb2
-rw-r--r--app/graphql/mutations/ci/job/play.rb12
-rw-r--r--app/graphql/mutations/ci/project_ci_cd_settings_update.rb2
-rw-r--r--app/graphql/mutations/clusters/agents/create.rb2
-rw-r--r--app/graphql/mutations/commits/create.rb2
-rw-r--r--app/graphql/mutations/concerns/mutations/work_items/widgetable.rb2
-rw-r--r--app/graphql/mutations/jira_import/start.rb2
-rw-r--r--app/graphql/mutations/members/groups/bulk_update.rb85
-rw-r--r--app/graphql/mutations/merge_requests/accept.rb4
-rw-r--r--app/graphql/mutations/notes/create/note.rb2
-rw-r--r--app/graphql/mutations/packages/bulk_destroy.rb5
-rw-r--r--app/graphql/mutations/releases/create.rb2
-rw-r--r--app/graphql/resolvers/ci/jobs_resolver.rb7
-rw-r--r--app/graphql/resolvers/ci/runner_groups_resolver.rb2
-rw-r--r--app/graphql/resolvers/concerns/board_item_filterable.rb1
-rw-r--r--app/graphql/resolvers/concerns/caching_array_resolver.rb1
-rw-r--r--app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb5
-rw-r--r--app/graphql/resolvers/concerns/resolves_merge_requests.rb2
-rw-r--r--app/graphql/resolvers/concerns/search_arguments.rb23
-rw-r--r--app/graphql/resolvers/issues/base_resolver.rb21
-rw-r--r--app/graphql/resolvers/issues_resolver.rb16
-rw-r--r--app/graphql/resolvers/projects/branch_rules_resolver.rb10
-rw-r--r--app/graphql/resolvers/timelog_resolver.rb36
-rw-r--r--app/graphql/resolvers/work_items_resolver.rb2
-rw-r--r--app/graphql/types/access_level_enum.rb2
-rw-r--r--app/graphql/types/achievements/achievement_type.rb55
-rw-r--r--app/graphql/types/ci/runner_countable_connection_type.rb12
-rw-r--r--app/graphql/types/ci/runner_type.rb9
-rw-r--r--app/graphql/types/description_version_type.rb19
-rw-r--r--app/graphql/types/issue_type.rb9
-rw-r--r--app/graphql/types/issues/unioned_issue_filter_input_type.rb3
-rw-r--r--app/graphql/types/member_access_level_enum.rb16
-rw-r--r--app/graphql/types/member_interface.rb2
-rw-r--r--app/graphql/types/merge_requests/interacts_with_merge_request.rb2
-rw-r--r--app/graphql/types/mutation_type.rb2
-rw-r--r--app/graphql/types/namespace/shared_runners_setting_enum.rb13
-rw-r--r--app/graphql/types/namespace_type.rb11
-rw-r--r--app/graphql/types/notes/note_type.rb94
-rw-r--r--app/graphql/types/notes/noteable_interface.rb5
-rw-r--r--app/graphql/types/notes/system_note_metadata_type.rb22
-rw-r--r--app/graphql/types/projects/branch_rule_type.rb16
-rw-r--r--app/graphql/types/query_type.rb184
-rw-r--r--app/graphql/types/repository/blob_type.rb8
-rw-r--r--app/graphql/types/time_tracking/timelog_connection_type.rb25
-rw-r--r--app/graphql/types/time_tracking/timelog_sort_enum.rb21
-rw-r--r--app/graphql/types/timelog_type.rb2
-rw-r--r--app/graphql/types/todo_action_enum.rb2
-rw-r--r--app/graphql/types/user_interface.rb13
-rw-r--r--app/graphql/types/users/email_type.rb36
-rw-r--r--app/graphql/types/users/namespace_commit_email_type.rb36
-rw-r--r--app/graphql/types/work_items/widgets/description_type.rb4
52 files changed, 712 insertions, 213 deletions
diff --git a/app/graphql/mutations/achievements/create.rb b/app/graphql/mutations/achievements/create.rb
new file mode 100644
index 00000000000..6cfe6c0e643
--- /dev/null
+++ b/app/graphql/mutations/achievements/create.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Achievements
+ class Create < BaseMutation
+ graphql_name 'AchievementsCreate'
+
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ field :achievement,
+ ::Types::Achievements::AchievementType,
+ null: true,
+ description: 'Achievement created.'
+
+ argument :namespace_id, ::Types::GlobalIDType[::Namespace],
+ required: true,
+ description: 'Namespace for the achievement.'
+
+ argument :name, GraphQL::Types::String,
+ required: true,
+ description: 'Name for the achievement.'
+
+ argument :avatar, ApolloUploadServer::Upload,
+ required: false,
+ description: 'Avatar for the achievement.'
+
+ argument :description, GraphQL::Types::String,
+ required: false,
+ description: 'Description of or notes for the achievement.'
+
+ argument :revokeable, GraphQL::Types::Boolean,
+ required: true,
+ description: 'Revokeability for the achievement.'
+
+ authorize :admin_achievement
+
+ def resolve(args)
+ namespace = authorized_find!(id: args[:namespace_id])
+
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable, '`achievements` feature flag is disabled.' \
+ if Feature.disabled?(:achievements, namespace)
+
+ result = ::Achievements::CreateService.new(namespace: namespace,
+ current_user: current_user,
+ params: args).execute
+ { achievement: result.payload, errors: result.errors }
+ end
+
+ def find_object(id:)
+ GitlabSchema.object_from_id(id, expected_type: ::Namespace)
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/boards/lists/base_update.rb b/app/graphql/mutations/boards/lists/base_update.rb
index 7962d9c85d4..c6e6b1c9bfe 100644
--- a/app/graphql/mutations/boards/lists/base_update.rb
+++ b/app/graphql/mutations/boards/lists/base_update.rb
@@ -10,7 +10,7 @@ module Mutations
argument :collapsed, GraphQL::Types::Boolean,
required: false,
- description: 'Indicates if the list is collapsed for this user.'
+ description: 'Indicates if the list is collapsed for the user.'
def resolve(list: nil, **args)
if list.nil? || !can_read_list?(list)
diff --git a/app/graphql/mutations/ci/job/play.rb b/app/graphql/mutations/ci/job/play.rb
index 99f62ea3e70..8bb69119a44 100644
--- a/app/graphql/mutations/ci/job/play.rb
+++ b/app/graphql/mutations/ci/job/play.rb
@@ -11,13 +11,21 @@ module Mutations
null: true,
description: 'Job after the mutation.'
+ argument :variables, [::Types::Ci::VariableInputType],
+ required: false,
+ default_value: [],
+ replace_null_with_default: true,
+ description: 'Variables to use when playing a manual job.'
+
authorize :update_build
- def resolve(id:)
+ def resolve(id:, variables:)
job = authorized_find!(id: id)
project = job.project
+ variables = variables.map(&:to_h)
+
+ ::Ci::PlayBuildService.new(project, current_user).execute(job, variables)
- ::Ci::PlayBuildService.new(project, current_user).execute(job)
{
job: job,
errors: errors_on_object(job)
diff --git a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
index 27b066ffcf6..934d62e92cf 100644
--- a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
+++ b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
@@ -15,7 +15,7 @@ module Mutations
argument :keep_latest_artifact, GraphQL::Types::Boolean,
required: false,
- description: 'Indicates if the latest artifact should be kept for this project.'
+ description: 'Indicates if the latest artifact should be kept for the project.'
argument :job_token_scope_enabled, GraphQL::Types::Boolean,
required: false,
diff --git a/app/graphql/mutations/clusters/agents/create.rb b/app/graphql/mutations/clusters/agents/create.rb
index deaa9c2d656..8be1e0e524a 100644
--- a/app/graphql/mutations/clusters/agents/create.rb
+++ b/app/graphql/mutations/clusters/agents/create.rb
@@ -12,7 +12,7 @@ module Mutations
argument :project_path, GraphQL::Types::ID,
required: true,
- description: 'Full path of the associated project for this cluster agent.'
+ description: 'Full path of the associated project for the cluster agent.'
argument :name, GraphQL::Types::String,
required: true,
diff --git a/app/graphql/mutations/commits/create.rb b/app/graphql/mutations/commits/create.rb
index 00ec64becc8..02e1e4c78bf 100644
--- a/app/graphql/mutations/commits/create.rb
+++ b/app/graphql/mutations/commits/create.rb
@@ -58,7 +58,7 @@ module Mutations
commit_message: message,
branch_name: branch,
start_branch: args[:start_branch] || branch,
- actions: actions.map { |action| action.to_h }
+ actions: actions.map(&:to_h)
}
result = ::Files::MultiService.new(project, current_user, attributes).execute
diff --git a/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb b/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb
index 445b2eb6441..508e1627032 100644
--- a/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb
+++ b/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb
@@ -18,7 +18,7 @@ module Mutations
# Cannot use prepare to use `.to_h` on each input due to
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87472#note_945199865
- widget_params.transform_values { |values| values.to_h }
+ widget_params.transform_values(&:to_h)
end
end
end
diff --git a/app/graphql/mutations/jira_import/start.rb b/app/graphql/mutations/jira_import/start.rb
index ea071c45bcf..2ba20e163a5 100644
--- a/app/graphql/mutations/jira_import/start.rb
+++ b/app/graphql/mutations/jira_import/start.rb
@@ -30,7 +30,7 @@ module Mutations
def resolve(project_path:, jira_project_key:, users_mapping:)
project = authorized_find!(project_path)
- mapping = users_mapping.to_ary.map { |map| map.to_hash }
+ mapping = users_mapping.to_ary.map(&:to_hash)
service_response = ::JiraImport::StartImportService
.new(context[:current_user], project, jira_project_key, mapping)
diff --git a/app/graphql/mutations/members/groups/bulk_update.rb b/app/graphql/mutations/members/groups/bulk_update.rb
new file mode 100644
index 00000000000..d0b19bd9634
--- /dev/null
+++ b/app/graphql/mutations/members/groups/bulk_update.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Members
+ module Groups
+ class BulkUpdate < ::Mutations::BaseMutation
+ graphql_name 'GroupMemberBulkUpdate'
+
+ include Gitlab::Utils::StrongMemoize
+
+ authorize :admin_group_member
+
+ field :group_members,
+ [Types::GroupMemberType],
+ null: true,
+ description: 'Group members after mutation.'
+
+ argument :group_id,
+ ::Types::GlobalIDType[::Group],
+ required: true,
+ description: 'Global ID of the group.'
+
+ argument :user_ids,
+ [::Types::GlobalIDType[::User]],
+ required: true,
+ description: 'Global IDs of the group members.'
+
+ argument :access_level,
+ ::Types::MemberAccessLevelEnum,
+ required: true,
+ description: 'Access level to update the members to.'
+
+ argument :expires_at,
+ Types::TimeType,
+ required: false,
+ description: 'Date and time the membership expires.'
+
+ MAX_MEMBERS_UPDATE_LIMIT = 50
+ MAX_MEMBERS_UPDATE_ERROR = "Count of members to be updated should be less than #{MAX_MEMBERS_UPDATE_LIMIT}."
+ INVALID_MEMBERS_ERROR = 'Only access level of direct members can be updated.'
+
+ def resolve(group_id:, **args)
+ result = ::Members::UpdateService.new(current_user, args.except(:user_ids)).execute(@updatable_group_members)
+
+ {
+ group_members: result[:members],
+ errors: Array.wrap(result[:message])
+ }
+ rescue Gitlab::Access::AccessDeniedError
+ {
+ errors: ["Unable to update members, please check user permissions."]
+ }
+ end
+
+ private
+
+ def ready?(**args)
+ group = authorized_find!(group_id: args[:group_id])
+ user_ids = args.fetch(:user_ids, {}).map(&:model_id)
+ @updatable_group_members = only_direct_group_members(group, user_ids)
+
+ if @updatable_group_members.size > MAX_MEMBERS_UPDATE_LIMIT
+ raise Gitlab::Graphql::Errors::InvalidMemberCountError, MAX_MEMBERS_UPDATE_ERROR
+ end
+
+ if @updatable_group_members.size != user_ids.size
+ raise Gitlab::Graphql::Errors::InvalidMembersError, INVALID_MEMBERS_ERROR
+ end
+
+ super
+ end
+
+ def find_object(group_id:)
+ GitlabSchema.object_from_id(group_id, expected_type: ::Group)
+ end
+
+ def only_direct_group_members(group, user_ids)
+ group
+ .members
+ .with_user(user_ids).to_a
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/merge_requests/accept.rb b/app/graphql/mutations/merge_requests/accept.rb
index ebd9e2b8fdd..64572091379 100644
--- a/app/graphql/mutations/merge_requests/accept.rb
+++ b/app/graphql/mutations/merge_requests/accept.rb
@@ -21,14 +21,14 @@ module Mutations
::Types::MergeStrategyEnum,
required: false,
as: :auto_merge_strategy,
- description: 'How to merge this merge request.'
+ description: 'How to merge the merge request.'
argument :commit_message, ::GraphQL::Types::String,
required: false,
description: 'Custom merge commit message.'
argument :sha, ::GraphQL::Types::String,
required: true,
- description: 'HEAD SHA at the time when this merge was requested.'
+ description: 'HEAD SHA at the time when the merge was requested.'
argument :squash_commit_message, ::GraphQL::Types::String,
required: false,
description: 'Custom squash commit message (if squash is true).'
diff --git a/app/graphql/mutations/notes/create/note.rb b/app/graphql/mutations/notes/create/note.rb
index 9b105b7fe1c..0f1be32d088 100644
--- a/app/graphql/mutations/notes/create/note.rb
+++ b/app/graphql/mutations/notes/create/note.rb
@@ -10,7 +10,7 @@ module Mutations
argument :discussion_id,
::Types::GlobalIDType[::Discussion],
required: false,
- description: 'Global ID of the discussion this note is in reply to.'
+ description: 'Global ID of the discussion the note is in reply to.'
argument :merge_request_diff_head_sha,
GraphQL::Types::String,
diff --git a/app/graphql/mutations/packages/bulk_destroy.rb b/app/graphql/mutations/packages/bulk_destroy.rb
index a0756d0c3f9..86f8de91e2b 100644
--- a/app/graphql/mutations/packages/bulk_destroy.rb
+++ b/app/graphql/mutations/packages/bulk_destroy.rb
@@ -16,11 +16,10 @@ module Mutations
def resolve(ids:)
raise_resource_not_available_error!(TOO_MANY_IDS_ERROR) if ids.size > MAX_PACKAGES
- ids = GitlabSchema.parse_gids(ids, expected_type: ::Packages::Package)
- .map(&:model_id)
+ model_ids = ids.map(&:model_id)
service = ::Packages::MarkPackagesForDestructionService.new(
- packages: packages_from(ids),
+ packages: packages_from(model_ids),
current_user: current_user
)
result = service.execute
diff --git a/app/graphql/mutations/releases/create.rb b/app/graphql/mutations/releases/create.rb
index ba1fa8d446c..15175aea9a5 100644
--- a/app/graphql/mutations/releases/create.rb
+++ b/app/graphql/mutations/releases/create.rb
@@ -30,7 +30,7 @@ module Mutations
required: false,
description: 'Description (also known as "release notes") of the release.'
- argument :released_at, Types::TimeType,
+ argument :released_at, Types::TimeType, # rubocop:disable Graphql/Descriptions
required: false,
description: 'Date and time for the release. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). Only provide this field if creating an upcoming or historical release.'
diff --git a/app/graphql/resolvers/ci/jobs_resolver.rb b/app/graphql/resolvers/ci/jobs_resolver.rb
index 91f29948ad0..31cc350f331 100644
--- a/app/graphql/resolvers/ci/jobs_resolver.rb
+++ b/app/graphql/resolvers/ci/jobs_resolver.rb
@@ -19,10 +19,15 @@ module Resolvers
required: false,
description: 'Filter jobs by retry-status.'
- def resolve(statuses: nil, security_report_types: [], retried: nil)
+ argument :when_executed, [::GraphQL::Types::String],
+ required: false,
+ description: 'Filter jobs by when they are executed.'
+
+ def resolve(statuses: nil, security_report_types: [], retried: nil, when_executed: nil)
jobs = init_collection(security_report_types)
jobs = jobs.with_status(statuses) if statuses.present?
jobs = jobs.retried if retried
+ jobs = jobs.with_when_executed(when_executed) if when_executed.present?
jobs = jobs.latest if retried == false
jobs
diff --git a/app/graphql/resolvers/ci/runner_groups_resolver.rb b/app/graphql/resolvers/ci/runner_groups_resolver.rb
index 3360e820bd2..c1d9bcbb9bb 100644
--- a/app/graphql/resolvers/ci/runner_groups_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_groups_resolver.rb
@@ -6,7 +6,7 @@ module Resolvers
include Gitlab::Graphql::Authorize::AuthorizeResource
include ResolvesGroups
- type Types::GroupConnection, null: true
+ type 'Types::GroupConnection', null: true
authorize :read_runner
authorizes_object!
diff --git a/app/graphql/resolvers/concerns/board_item_filterable.rb b/app/graphql/resolvers/concerns/board_item_filterable.rb
index 9c0ada4f72c..035cdbbd282 100644
--- a/app/graphql/resolvers/concerns/board_item_filterable.rb
+++ b/app/graphql/resolvers/concerns/board_item_filterable.rb
@@ -22,6 +22,7 @@ module BoardItemFilterable
rewrite_param_name(filters[:or], :author_usernames, :author_username)
rewrite_param_name(filters[:or], :assignee_usernames, :assignee_username)
+ rewrite_param_name(filters[:or], :label_names, :label_name)
end
filters
diff --git a/app/graphql/resolvers/concerns/caching_array_resolver.rb b/app/graphql/resolvers/concerns/caching_array_resolver.rb
index e7555dcf42c..62649518142 100644
--- a/app/graphql/resolvers/concerns/caching_array_resolver.rb
+++ b/app/graphql/resolvers/concerns/caching_array_resolver.rb
@@ -63,6 +63,7 @@ module CachingArrayResolver
queries.in_groups_of(max_union_size, false).each do |group|
by_id = model_class
+ .select(all_fields, :union_member_idx)
.from_union(tag(group), remove_duplicates: false)
.preload(preload) # rubocop: disable CodeReuse/ActiveRecord
.group_by { |r| r[primary_key] }
diff --git a/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb b/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb
index c6e32be245d..2ea7a02bf15 100644
--- a/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb
+++ b/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb
@@ -20,7 +20,7 @@ module Issues
end
def preloads
- {
+ preload_hash = {
alert_management_alert: [:alert_management_alert],
assignees: [:assignees],
participants: Issue.participant_includes,
@@ -28,6 +28,9 @@ module Issues
customer_relations_contacts: { customer_relations_contacts: [:group] },
escalation_status: [:incident_management_issuable_escalation_status]
}
+ preload_hash[:type] = :work_item_type if Feature.enabled?(:issue_type_uses_work_item_types_table)
+
+ preload_hash
end
end
end
diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
index d56951bc821..c68e120ee24 100644
--- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb
+++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
@@ -34,7 +34,7 @@ module ResolvesMergeRequests
end
def unconditional_includes
- [:target_project]
+ [:target_project, :author]
end
def preloads
diff --git a/app/graphql/resolvers/concerns/search_arguments.rb b/app/graphql/resolvers/concerns/search_arguments.rb
index ccc012f2bf9..cc1a13fdf29 100644
--- a/app/graphql/resolvers/concerns/search_arguments.rb
+++ b/app/graphql/resolvers/concerns/search_arguments.rb
@@ -18,6 +18,7 @@ module SearchArguments
def ready?(**args)
validate_search_in_params!(args)
validate_anonymous_search_access!(args)
+ validate_search_rate_limit!(args)
super
end
@@ -39,6 +40,28 @@ module SearchArguments
'`search` should be present when including the `in` argument'
end
+ def validate_search_rate_limit!(args)
+ return if args[:search].blank? || context[:request].nil? || Feature.disabled?(:rate_limit_issuable_searches)
+
+ if current_user.present?
+ rate_limiter_key = :search_rate_limit
+ rate_limiter_scope = [current_user]
+ else
+ rate_limiter_key = :search_rate_limit_unauthenticated
+ rate_limiter_scope = [context[:request].ip]
+ end
+
+ if ::Gitlab::ApplicationRateLimiter.throttled_request?(
+ context[:request],
+ current_user,
+ rate_limiter_key,
+ scope: rate_limiter_scope
+ )
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable,
+ 'This endpoint has been requested with the search argument too many times. Try again later.'
+ end
+ end
+
def prepare_finder_params(args)
prepare_search_params(args)
end
diff --git a/app/graphql/resolvers/issues/base_resolver.rb b/app/graphql/resolvers/issues/base_resolver.rb
index 9a2c4572abb..fefd17d5e20 100644
--- a/app/graphql/resolvers/issues/base_resolver.rb
+++ b/app/graphql/resolvers/issues/base_resolver.rb
@@ -129,7 +129,8 @@ module Resolvers
params[:or] = params[:or].to_h if params[:or]
params[:iids] ||= [params.delete(:iid)].compact if params[:iid]
- prepare_author_username_params(params)
+ rewrite_param_name(params[:or], :author_usernames, :author_username)
+ rewrite_param_name(params[:or], :label_names, :label_name)
prepare_assignee_username_params(params)
prepare_release_tag_params(params)
@@ -143,20 +144,14 @@ module Resolvers
args[:release_tag] ||= release_tag_wildcard
end
- def prepare_author_username_params(args)
- args[:or][:author_username] = args[:or].delete(:author_usernames) if args.dig(:or, :author_usernames).present?
- end
-
def prepare_assignee_username_params(args)
- args[:assignee_username] = args.delete(:assignee_usernames) if args[:assignee_usernames].present?
-
- if args.dig(:or, :assignee_usernames).present?
- args[:or][:assignee_username] = args[:or].delete(:assignee_usernames)
- end
-
- return unless args.dig(:not, :assignee_usernames).present?
+ rewrite_param_name(args, :assignee_usernames, :assignee_username)
+ rewrite_param_name(args[:or], :assignee_usernames, :assignee_username)
+ rewrite_param_name(args[:not], :assignee_usernames, :assignee_username)
+ end
- args[:not][:assignee_username] = args[:not].delete(:assignee_usernames)
+ def rewrite_param_name(params, old_name, new_name)
+ params[new_name] = params.delete(old_name) if params && params[old_name].present?
end
def mutually_exclusive_release_tag_args
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index 3e61ba755d8..24009bf7e18 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -5,6 +5,8 @@ module Resolvers
prepend ::Issues::LookAheadPreloads
include ::Issues::SortArguments
+ NON_FILTER_ARGUMENTS = %i[sort lookahead].freeze
+
argument :state, Types::IssuableStateEnum,
required: false,
description: 'Current state of this issue.'
@@ -17,6 +19,14 @@ module Resolvers
::Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
end
+ def ready?(**args)
+ unless filter_provided?(args)
+ raise Gitlab::Graphql::Errors::ArgumentError, _('You must provide at least one filter argument for this query')
+ end
+
+ super
+ end
+
def resolve_with_lookahead(**args)
return unless Feature.enabled?(:root_level_issues_query)
@@ -32,5 +42,11 @@ module Resolvers
issues
end
end
+
+ private
+
+ def filter_provided?(args)
+ args.except(*NON_FILTER_ARGUMENTS).values.any?(&:present?)
+ end
end
end
diff --git a/app/graphql/resolvers/projects/branch_rules_resolver.rb b/app/graphql/resolvers/projects/branch_rules_resolver.rb
index e99d7ae4d5f..d1b39df602f 100644
--- a/app/graphql/resolvers/projects/branch_rules_resolver.rb
+++ b/app/graphql/resolvers/projects/branch_rules_resolver.rb
@@ -10,7 +10,15 @@ module Resolvers
alias_method :project, :object
def resolve_with_lookahead(**args)
- apply_lookahead(project.protected_branches)
+ protected_branches.map do |protected_branch|
+ ::Projects::BranchRule.new(project, protected_branch)
+ end
+ end
+
+ private
+
+ def protected_branches
+ apply_lookahead(project.protected_branches.sorted_by_name)
end
end
end
diff --git a/app/graphql/resolvers/timelog_resolver.rb b/app/graphql/resolvers/timelog_resolver.rb
index 52c4508003a..dc42a5f38c9 100644
--- a/app/graphql/resolvers/timelog_resolver.rb
+++ b/app/graphql/resolvers/timelog_resolver.rb
@@ -34,19 +34,23 @@ module Resolvers
required: false,
description: 'List timelogs for a user.'
+ argument :sort, Types::TimeTracking::TimelogSortEnum,
+ description: 'List timelogs in a particular order.',
+ required: false,
+ default_value: { field: 'spent_at', direction: :asc }
+
def resolve_with_lookahead(**args)
validate_args!(object, args)
- timelogs = object&.timelogs || Timelog.limit(GitlabSchema.default_max_page_size)
+ timelogs = object&.timelogs || Timelog.all
- if args.any?
- args = parse_datetime_args(args)
+ args = parse_datetime_args(args)
- timelogs = apply_user_filter(timelogs, args)
- timelogs = apply_project_filter(timelogs, args)
- timelogs = apply_time_filter(timelogs, args)
- timelogs = apply_group_filter(timelogs, args)
- end
+ timelogs = apply_user_filter(timelogs, args)
+ timelogs = apply_project_filter(timelogs, args)
+ timelogs = apply_time_filter(timelogs, args)
+ timelogs = apply_group_filter(timelogs, args)
+ timelogs = apply_sorting(timelogs, args)
apply_lookahead(timelogs)
end
@@ -60,7 +64,12 @@ module Resolvers
end
def validate_args!(object, args)
- if args.empty? && object.nil?
+ # sort is always provided because of its default value so we
+ # should check the remaining args to make sure at least one filter
+ # argument was provided
+ cleaned_args = args.except(:sort)
+
+ if cleaned_args.empty? && object.nil?
raise_argument_error('Provide at least one argument')
elsif args[:start_time] && args[:start_date]
raise_argument_error('Provide either a start date or time, but not both')
@@ -132,6 +141,15 @@ module Resolvers
timelogs
end
+ def apply_sorting(timelogs, args)
+ return timelogs unless args[:sort]
+
+ field = args[:sort][:field]
+ direction = args[:sort][:direction]
+
+ timelogs.sort_by_field(field, direction)
+ end
+
def raise_argument_error(message)
raise Gitlab::Graphql::Errors::ArgumentError, message
end
diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb
index a3de875c196..83ed8c37250 100644
--- a/app/graphql/resolvers/work_items_resolver.rb
+++ b/app/graphql/resolvers/work_items_resolver.rb
@@ -55,7 +55,7 @@ module Resolvers
last_edited_by: :last_edited_by,
assignees: :assignees,
parent: :work_item_parent,
- children: { work_item_children: [:author, { project: :project_feature }] },
+ children: { work_item_children_by_created_at: [:author, { project: :project_feature }] },
labels: :labels,
milestone: :milestone
}
diff --git a/app/graphql/types/access_level_enum.rb b/app/graphql/types/access_level_enum.rb
index 299952e4685..d58e7230a8e 100644
--- a/app/graphql/types/access_level_enum.rb
+++ b/app/graphql/types/access_level_enum.rb
@@ -14,3 +14,5 @@ module Types
value 'OWNER', value: Gitlab::Access::OWNER, description: 'Owner access.'
end
end
+
+Types::AccessLevelEnum.prepend_mod_with('Types::AccessLevelEnum')
diff --git a/app/graphql/types/achievements/achievement_type.rb b/app/graphql/types/achievements/achievement_type.rb
new file mode 100644
index 00000000000..e2b9495c83d
--- /dev/null
+++ b/app/graphql/types/achievements/achievement_type.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Types
+ module Achievements
+ class AchievementType < BaseObject
+ graphql_name 'Achievement'
+
+ authorize :read_achievement
+
+ field :id,
+ ::Types::GlobalIDType[::Achievements::Achievement],
+ null: false,
+ description: 'ID of the achievement.'
+
+ field :namespace,
+ ::Types::NamespaceType,
+ null: false,
+ description: 'Namespace of the achievement.'
+
+ field :name,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Name of the achievement.'
+
+ field :avatar_url,
+ GraphQL::Types::String,
+ null: true,
+ description: 'URL to avatar of the achievement.'
+
+ field :description,
+ GraphQL::Types::String,
+ null: true,
+ description: 'Description or notes for the achievement.'
+
+ field :revokeable,
+ GraphQL::Types::Boolean,
+ null: false,
+ description: 'Revokeability of the achievement.'
+
+ field :created_at,
+ Types::TimeType,
+ null: false,
+ description: 'Timestamp the achievement was created.'
+
+ field :updated_at,
+ Types::TimeType,
+ null: false,
+ description: 'Timestamp the achievement was last updated.'
+
+ def avatar_url
+ object.avatar_url(only_path: false)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/runner_countable_connection_type.rb b/app/graphql/types/ci/runner_countable_connection_type.rb
new file mode 100644
index 00000000000..f5c3a2c1f5f
--- /dev/null
+++ b/app/graphql/types/ci/runner_countable_connection_type.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class RunnerCountableConnectionType < ::Types::CountableConnectionType
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
+
+Types::Ci::RunnerCountableConnectionType.prepend_mod
diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb
index 5d34906f7b8..35339624e37 100644
--- a/app/graphql/types/ci/runner_type.rb
+++ b/app/graphql/types/ci/runner_type.rb
@@ -6,7 +6,7 @@ module Types
graphql_name 'CiRunner'
edge_type_class(RunnerWebUrlEdge)
- connection_type_class(Types::CountableConnectionType)
+ connection_type_class(RunnerCountableConnectionType)
authorize :read_runner
present_using ::Ci::RunnerPresenter
@@ -38,10 +38,9 @@ module Types
field :executor_name, GraphQL::Types::String, null: true,
description: 'Executor last advertised by the runner.',
method: :executor_name
- field :groups, 'Types::GroupConnection',
- null: true,
- resolver: ::Resolvers::Ci::RunnerGroupsResolver,
- description: 'Groups the runner is associated with. For group runners only.'
+ field :groups, null: true,
+ resolver: ::Resolvers::Ci::RunnerGroupsResolver,
+ description: 'Groups the runner is associated with. For group runners only.'
field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
description: 'ID of the runner.'
field :ip_address, GraphQL::Types::String, null: true,
diff --git a/app/graphql/types/description_version_type.rb b/app/graphql/types/description_version_type.rb
new file mode 100644
index 00000000000..bee30597e4c
--- /dev/null
+++ b/app/graphql/types/description_version_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ class DescriptionVersionType < BaseObject
+ graphql_name 'DescriptionVersion'
+
+ authorize :read_issuable
+
+ field :id, ::Types::GlobalIDType[::DescriptionVersion],
+ null: false,
+ description: 'ID of the description version.'
+
+ field :description, GraphQL::Types::String,
+ null: true,
+ description: 'Content of the given description version.'
+ end
+end
+
+Types::DescriptionVersionType.prepend_mod
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index dd2ad26ce49..4948063610a 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -117,7 +117,6 @@ module Types
description: 'Collection of design images associated with this issue.'
field :type, Types::IssueTypeEnum, null: true,
- method: :issue_type,
description: 'Type of the issue.'
field :alert_management_alert,
@@ -198,6 +197,14 @@ module Types
def escalation_status
object.supports_escalation? ? object.escalation_status&.status_name : nil
end
+
+ def type
+ if Feature.enabled?(:issue_type_uses_work_item_types_table)
+ object.work_item_type.base_type
+ else
+ object.issue_type
+ end
+ end
end
end
diff --git a/app/graphql/types/issues/unioned_issue_filter_input_type.rb b/app/graphql/types/issues/unioned_issue_filter_input_type.rb
index 9c7261279c7..a9c5b3c24ce 100644
--- a/app/graphql/types/issues/unioned_issue_filter_input_type.rb
+++ b/app/graphql/types/issues/unioned_issue_filter_input_type.rb
@@ -11,6 +11,9 @@ module Types
argument :author_usernames, [GraphQL::Types::String],
required: false,
description: 'Filters issues that are authored by one of the given users.'
+ argument :label_names, [GraphQL::Types::String],
+ required: false,
+ description: 'Filters issues that have at least one of the given labels.'
end
end
end
diff --git a/app/graphql/types/member_access_level_enum.rb b/app/graphql/types/member_access_level_enum.rb
new file mode 100644
index 00000000000..8f89b882641
--- /dev/null
+++ b/app/graphql/types/member_access_level_enum.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ class MemberAccessLevelEnum < BaseEnum
+ graphql_name 'MemberAccessLevel'
+ description 'Access level of a group or project member'
+
+ value 'GUEST', value: Gitlab::Access::GUEST, description: 'Guest access.'
+ value 'REPORTER', value: Gitlab::Access::REPORTER, description: 'Reporter access.'
+ value 'DEVELOPER', value: Gitlab::Access::DEVELOPER, description: 'Developer access.'
+ value 'MAINTAINER', value: Gitlab::Access::MAINTAINER, description: 'Maintainer access.'
+ value 'OWNER', value: Gitlab::Access::OWNER, description: 'Owner access.'
+ end
+end
+
+Types::MemberAccessLevelEnum.prepend_mod_with('Types::MemberAccessLevelEnum')
diff --git a/app/graphql/types/member_interface.rb b/app/graphql/types/member_interface.rb
index edadbcddfb3..4c9ee6246a3 100644
--- a/app/graphql/types/member_interface.rb
+++ b/app/graphql/types/member_interface.rb
@@ -46,7 +46,7 @@ module Types
def merge_request_interaction(id: nil)
Gitlab::Graphql::Lazy.with_value(GitlabSchema.object_from_id(id, expected_class: ::MergeRequest)) do |merge_request|
- Users::MergeRequestInteraction.new(user: object.user, merge_request: merge_request) if merge_request
+ ::Users::MergeRequestInteraction.new(user: object.user, merge_request: merge_request) if merge_request
end
end
end
diff --git a/app/graphql/types/merge_requests/interacts_with_merge_request.rb b/app/graphql/types/merge_requests/interacts_with_merge_request.rb
index bef2d39dc5c..672a2a315d4 100644
--- a/app/graphql/types/merge_requests/interacts_with_merge_request.rb
+++ b/app/graphql/types/merge_requests/interacts_with_merge_request.rb
@@ -16,7 +16,7 @@ module Types
def merge_request_interaction(parent:, id: nil)
# need the connection parent if called from a connection node:
parent = parent.parent if parent.try(:field)&.connection?
- Users::MergeRequestInteraction.new(user: object, merge_request: parent)
+ ::Users::MergeRequestInteraction.new(user: object, merge_request: parent)
end
end
end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index b342e57804b..5a92ba754aa 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -6,6 +6,7 @@ module Types
include Gitlab::Graphql::MountMutation
+ mount_mutation Mutations::Achievements::Create
mount_mutation Mutations::Admin::SidekiqQueues::DeleteJobs
mount_mutation Mutations::AlertManagement::CreateAlertIssue
mount_mutation Mutations::AlertManagement::UpdateAlertStatus
@@ -66,6 +67,7 @@ module Types
mount_mutation Mutations::Issues::LinkAlerts
mount_mutation Mutations::Issues::UnlinkAlert
mount_mutation Mutations::Labels::Create
+ mount_mutation Mutations::Members::Groups::BulkUpdate
mount_mutation Mutations::MergeRequests::Accept
mount_mutation Mutations::MergeRequests::Create
mount_mutation Mutations::MergeRequests::Update
diff --git a/app/graphql/types/namespace/shared_runners_setting_enum.rb b/app/graphql/types/namespace/shared_runners_setting_enum.rb
index 4773e414aeb..fd067c9d803 100644
--- a/app/graphql/types/namespace/shared_runners_setting_enum.rb
+++ b/app/graphql/types/namespace/shared_runners_setting_enum.rb
@@ -4,10 +4,21 @@ module Types
class Namespace::SharedRunnersSettingEnum < BaseEnum
graphql_name 'SharedRunnersSetting'
- ::Namespace::SHARED_RUNNERS_SETTINGS.each do |type|
+ DEPRECATED_SETTINGS = [::Namespace::SR_DISABLED_WITH_OVERRIDE].freeze
+
+ ::Namespace::SHARED_RUNNERS_SETTINGS.excluding(DEPRECATED_SETTINGS).each do |type|
value type.upcase,
description: "Sharing of runners is #{type.tr('_', ' ')}.",
value: type
end
+
+ value ::Namespace::SR_DISABLED_WITH_OVERRIDE.upcase,
+ description: "Sharing of runners is disabled and overridable.",
+ value: ::Namespace::SR_DISABLED_WITH_OVERRIDE,
+ deprecated: {
+ reason: :renamed,
+ replacement: ::Namespace::SR_DISABLED_AND_OVERRIDABLE,
+ milestone: "17.0"
+ }
end
end
diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb
index 0f634e7c2d3..fc55ff512b6 100644
--- a/app/graphql/types/namespace_type.rb
+++ b/app/graphql/types/namespace_type.rb
@@ -63,6 +63,13 @@ module Types
description: "Timelog categories for the namespace.",
alpha: { milestone: '15.3' }
+ field :achievements,
+ Types::Achievements::AchievementType.connection_type,
+ null: true,
+ alpha: { milestone: '15.8' },
+ description: "Achievements for the namespace. " \
+ "Returns `null` if the `achievements` feature flag is disabled."
+
markdown_field :description_html, null: true
def timelog_categories
@@ -76,6 +83,10 @@ module Types
def root_storage_statistics
Gitlab::Graphql::Loaders::BatchRootStorageStatisticsLoader.new(object.id).find
end
+
+ def achievements
+ object.achievements if Feature.enabled?(:achievements, object)
+ end
end
end
diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb
index 05629ea9223..5055facb21b 100644
--- a/app/graphql/types/notes/note_type.rb
+++ b/app/graphql/types/notes/note_type.rb
@@ -11,54 +11,72 @@ module Types
implements(Types::ResolvableInterface)
- field :id, ::Types::GlobalIDType[::Note], null: false,
- description: 'ID of the note.'
+ field :id, ::Types::GlobalIDType[::Note],
+ null: false,
+ description: 'ID of the note.'
field :project, Types::ProjectType,
- null: true,
- description: 'Project associated with the note.'
+ null: true,
+ description: 'Project associated with the note.'
field :author, Types::UserType,
- null: false,
- description: 'User who wrote this note.'
+ null: false,
+ description: 'User who wrote this note.'
field :system, GraphQL::Types::Boolean,
- null: false,
- description: 'Indicates whether this note was created by the system or by a user.'
+ null: false,
+ description: 'Indicates whether this note was created by the system or by a user.'
field :system_note_icon_name,
- GraphQL::Types::String,
- null: true,
- description: 'Name of the icon corresponding to a system note.'
+ GraphQL::Types::String,
+ null: true,
+ description: 'Name of the icon corresponding to a system note.'
field :body, GraphQL::Types::String,
- null: false,
- method: :note,
- description: 'Content of the note.'
-
- field :confidential, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if this note is confidential.',
- method: :confidential?,
- deprecated: {
- reason: :renamed,
- replacement: 'internal',
- milestone: '15.5'
- }
-
- field :internal, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if this note is internal.',
- method: :confidential?
-
- field :created_at, Types::TimeType, null: false,
- description: 'Timestamp of the note creation.'
- field :discussion, Types::Notes::DiscussionType, null: true,
- description: 'Discussion this note is a part of.'
- field :position, Types::Notes::DiffPositionType, null: true,
- description: 'Position of this note on a diff.'
- field :updated_at, Types::TimeType, null: false,
- description: "Timestamp of the note's last activity."
+ null: false,
+ method: :note,
+ description: 'Content of the note.'
+
+ field :confidential, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if this note is confidential.',
+ method: :confidential?,
+ deprecated: {
+ reason: :renamed,
+ replacement: 'internal',
+ milestone: '15.5'
+ }
+
+ field :internal, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if this note is internal.',
+ method: :confidential?
+
+ field :created_at, Types::TimeType,
+ null: false,
+ description: 'Timestamp of the note creation.'
+ field :discussion, Types::Notes::DiscussionType,
+ null: true,
+ description: 'Discussion this note is a part of.'
+ field :position, Types::Notes::DiffPositionType,
+ null: true,
+ description: 'Position of this note on a diff.'
+ field :updated_at, Types::TimeType,
+ null: false,
+ description: "Timestamp of the note's last activity."
field :url, GraphQL::Types::String,
- null: true,
- description: 'URL to view this Note in the Web UI.'
+ null: true,
+ description: 'URL to view this Note in the Web UI.'
+
+ field :last_edited_at, Types::TimeType,
+ null: true,
+ description: 'Timestamp when note was last edited.'
+ field :last_edited_by, Types::UserType,
+ null: true,
+ description: 'User who last edited the note.'
+
+ field :system_note_metadata, Types::Notes::SystemNoteMetadataType,
+ null: true,
+ description: 'Metadata for the given note if it is a system note.'
markdown_field :body_html, null: true, method: :note
diff --git a/app/graphql/types/notes/noteable_interface.rb b/app/graphql/types/notes/noteable_interface.rb
index bd22f12d6f0..537084dff62 100644
--- a/app/graphql/types/notes/noteable_interface.rb
+++ b/app/graphql/types/notes/noteable_interface.rb
@@ -7,6 +7,7 @@ module Types
field :notes, Types::Notes::NoteType.connection_type, null: false, description: "All notes on this noteable."
field :discussions, Types::Notes::DiscussionType.connection_type, null: false, description: "All discussions on this noteable."
+ field :commenters, Types::UserType.connection_type, null: false, description: "All commenters on this noteable."
def self.resolve_type(object, context)
case object
@@ -24,6 +25,10 @@ module Types
raise "Unknown GraphQL type for #{object}"
end
end
+
+ def commenters
+ object.commenters(user: current_user)
+ end
end
end
end
diff --git a/app/graphql/types/notes/system_note_metadata_type.rb b/app/graphql/types/notes/system_note_metadata_type.rb
new file mode 100644
index 00000000000..b3dd7e037f9
--- /dev/null
+++ b/app/graphql/types/notes/system_note_metadata_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Notes
+ class SystemNoteMetadataType < BaseObject
+ graphql_name 'SystemNoteMetadata'
+
+ authorize :read_note
+
+ field :id, ::Types::GlobalIDType[::SystemNoteMetadata],
+ null: false,
+ description: 'Global ID of the specific system note metadata.'
+
+ field :action, GraphQL::Types::String,
+ null: true,
+ description: 'System note metadata action.'
+ field :description_version, ::Types::DescriptionVersionType,
+ null: true,
+ description: 'Version of the changed description.'
+ end
+ end
+end
diff --git a/app/graphql/types/projects/branch_rule_type.rb b/app/graphql/types/projects/branch_rule_type.rb
index 1afd2cc3fef..08b1203d4a3 100644
--- a/app/graphql/types/projects/branch_rule_type.rb
+++ b/app/graphql/types/projects/branch_rule_type.rb
@@ -5,7 +5,6 @@ module Types
class BranchRuleType < BaseObject
graphql_name 'BranchRule'
description 'List of branch rules for a project, grouped by branch name.'
- accepts ::ProtectedBranch
authorize :read_protected_branch
alias_method :branch_rule, :object
@@ -22,6 +21,12 @@ module Types
calls_gitaly: true,
description: "Check if this branch rule protects the project's default branch."
+ field :is_protected,
+ type: GraphQL::Types::Boolean,
+ null: false,
+ method: :protected?,
+ description: "Check if this branch rule protects access for the branch."
+
field :matching_branches_count,
type: GraphQL::Types::Int,
null: false,
@@ -30,9 +35,8 @@ module Types
field :branch_protection,
type: Types::BranchRules::BranchProtectionType,
- null: false,
- description: 'Branch protections configured for this branch rule.',
- method: :itself
+ null: true,
+ description: 'Branch protections configured for this branch rule.'
field :created_at,
Types::TimeType,
@@ -43,10 +47,6 @@ module Types
Types::TimeType,
null: false,
description: 'Timestamp of when the branch rule was last updated.'
-
- def matching_branches_count
- branch_rule.matching(branch_rule.project.repository.branch_names).count
- end
end
end
end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 7263f792bae..990ba1fb7fc 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -7,56 +7,17 @@ module Types
# The design management context object needs to implement #issue
DesignManagementObject = Struct.new(:issue)
- field :project, Types::ProjectType,
- null: true,
- resolver: Resolvers::ProjectResolver,
- description: "Find a project."
-
- field :projects, Types::ProjectType.connection_type,
- null: true,
- resolver: Resolvers::ProjectsResolver,
- description: "Find projects visible to the current user."
-
- field :group, Types::GroupType,
- null: true,
- resolver: Resolvers::GroupResolver,
- description: "Find a group."
-
- field :current_user, Types::UserType,
- null: true,
- description: "Get information about current user."
-
- field :namespace, Types::NamespaceType,
- null: true,
- resolver: Resolvers::NamespaceResolver,
- description: "Find a namespace."
-
- field :metadata, Types::MetadataType,
- null: true,
- resolver: Resolvers::MetadataResolver,
- description: 'Metadata about GitLab.'
-
- field :query_complexity, Types::QueryComplexityType,
+ field :board_list, ::Types::BoardListType,
null: true,
- description: 'Information about the complexity of the GraphQL query.'
-
- field :snippets,
- Types::SnippetType.connection_type,
+ resolver: Resolvers::BoardListResolver
+ field :ci_application_settings, Types::Ci::ApplicationSettingType,
null: true,
- resolver: Resolvers::SnippetsResolver,
- description: 'Find Snippets visible to the current user.'
-
- field :design_management, Types::DesignManagementType,
- null: false,
- description: 'Fields related to design management.'
-
- field :milestone, ::Types::MilestoneType,
+ description: 'CI related settings that apply to the entire instance.'
+ field :ci_config, resolver: Resolvers::Ci::ConfigResolver, complexity: 126 # AUTHENTICATED_MAX_COMPLEXITY / 2 + 1
+ field :ci_variables,
+ Types::Ci::InstanceVariableType.connection_type,
null: true,
- extras: [:lookahead],
- description: 'Find a milestone.' do
- argument :id, ::Types::GlobalIDType[Milestone], required: true, description: 'Find a milestone by its ID.'
- end
-
+ description: "List of the instance's CI/CD variables."
field :container_repository, Types::ContainerRepositoryDetailsType,
null: true,
description: 'Find a container repository.' do
@@ -65,107 +26,116 @@ module Types
required: true,
description: 'Global ID of the container repository.'
end
-
- field :package,
- description: 'Find a package. This field can only be resolved for one query in any single request. Returns `null` if a package has no `default` status.',
- resolver: Resolvers::PackageDetailsResolver
-
- field :user, Types::UserType,
- null: true,
- description: 'Find a user.',
- resolver: Resolvers::UserResolver
-
- field :users, Types::UserType.connection_type,
+ field :current_user, Types::UserType,
null: true,
- description: 'Find users.',
- resolver: Resolvers::UsersResolver
-
+ description: "Get information about current user."
+ field :design_management, Types::DesignManagementType,
+ null: false,
+ description: 'Fields related to design management.'
field :echo, resolver: Resolvers::EchoResolver
-
- field :issues,
+ field :gitpod_enabled, GraphQL::Types::Boolean,
null: true,
- alpha: { milestone: '15.6' },
- resolver: Resolvers::IssuesResolver,
- description: 'Issues visible by the current user.' \
- ' Returns null if the `root_level_issues_query` feature flag is disabled.'
-
+ description: "Whether Gitpod is enabled in application settings."
+ field :group, Types::GroupType,
+ null: true,
+ resolver: Resolvers::GroupResolver,
+ description: "Find a group."
field :issue, Types::IssueType,
null: true,
description: 'Find an issue.' do
argument :id, ::Types::GlobalIDType[::Issue], required: true, description: 'Global ID of the issue.'
end
-
- field :work_item, Types::WorkItemType,
+ field :issues,
null: true,
- resolver: Resolvers::WorkItemResolver,
- alpha: { milestone: '15.1' },
- description: 'Find a work item.'
-
+ alpha: { milestone: '15.6' },
+ resolver: Resolvers::IssuesResolver,
+ description: 'Find issues visible to the current user.' \
+ ' At least one filter must be provided.' \
+ ' Returns `null` if the `root_level_issues_query` feature flag is disabled.'
+ field :jobs,
+ ::Types::Ci::JobType.connection_type,
+ null: true,
+ description: 'All jobs on this GitLab instance.',
+ resolver: ::Resolvers::Ci::AllJobsResolver
field :merge_request, Types::MergeRequestType,
null: true,
description: 'Find a merge request.' do
argument :id, ::Types::GlobalIDType[::MergeRequest], required: true, description: 'Global ID of the merge request.'
end
-
- field :usage_trends_measurements, Types::Admin::Analytics::UsageTrends::MeasurementType.connection_type,
+ field :metadata, Types::MetadataType,
null: true,
- description: 'Get statistics on the instance.',
- resolver: Resolvers::Admin::Analytics::UsageTrends::MeasurementsResolver
-
- field :ci_application_settings, Types::Ci::ApplicationSettingType,
+ resolver: Resolvers::MetadataResolver,
+ description: 'Metadata about GitLab.'
+ field :milestone, ::Types::MilestoneType,
null: true,
- description: 'CI related settings that apply to the entire instance.'
-
- field :runner_platforms, resolver: Resolvers::Ci::RunnerPlatformsResolver
- field :runner_setup, resolver: Resolvers::Ci::RunnerSetupResolver
-
+ extras: [:lookahead],
+ description: 'Find a milestone.' do
+ argument :id, ::Types::GlobalIDType[Milestone], required: true, description: 'Find a milestone by its ID.'
+ end
+ field :namespace, Types::NamespaceType,
+ null: true,
+ resolver: Resolvers::NamespaceResolver,
+ description: "Find a namespace."
+ field :package,
+ description: 'Find a package. This field can only be resolved for one query in any single request. Returns `null` if a package has no `default` status.',
+ resolver: Resolvers::PackageDetailsResolver
+ field :project, Types::ProjectType,
+ null: true,
+ resolver: Resolvers::ProjectResolver,
+ description: "Find a project."
+ field :projects, Types::ProjectType.connection_type,
+ null: true,
+ resolver: Resolvers::ProjectsResolver,
+ description: "Find projects visible to the current user."
+ field :query_complexity, Types::QueryComplexityType,
+ null: true,
+ description: 'Information about the complexity of the GraphQL query.'
field :runner, Types::Ci::RunnerType,
null: true,
resolver: Resolvers::Ci::RunnerResolver,
extras: [:lookahead],
description: "Find a runner."
-
+ field :runner_platforms, resolver: Resolvers::Ci::RunnerPlatformsResolver
+ field :runner_setup, resolver: Resolvers::Ci::RunnerSetupResolver
field :runners, Types::Ci::RunnerType.connection_type,
null: true,
resolver: Resolvers::Ci::RunnersResolver,
description: "Find runners visible to the current user."
-
- field :ci_variables,
- Types::Ci::InstanceVariableType.connection_type,
+ field :snippets,
+ Types::SnippetType.connection_type,
null: true,
- description: "List of the instance's CI/CD variables."
-
- field :ci_config, resolver: Resolvers::Ci::ConfigResolver, complexity: 126 # AUTHENTICATED_MAX_COMPLEXITY / 2 + 1
-
+ resolver: Resolvers::SnippetsResolver,
+ description: 'Find Snippets visible to the current user.'
field :timelogs, Types::TimelogType.connection_type,
null: true,
description: 'Find timelogs visible to the current user.',
extras: [:lookahead],
complexity: 5,
resolver: ::Resolvers::TimelogResolver
-
- field :board_list, ::Types::BoardListType,
- null: true,
- resolver: Resolvers::BoardListResolver
-
field :todo,
null: true,
resolver: Resolvers::TodoResolver
-
field :topics, Types::Projects::TopicType.connection_type,
null: true,
resolver: Resolvers::TopicsResolver,
description: "Find project topics."
-
- field :gitpod_enabled, GraphQL::Types::Boolean,
+ field :usage_trends_measurements, Types::Admin::Analytics::UsageTrends::MeasurementType.connection_type,
null: true,
- description: "Whether Gitpod is enabled in application settings."
-
- field :jobs,
- ::Types::Ci::JobType.connection_type,
+ description: 'Get statistics on the instance.',
+ resolver: Resolvers::Admin::Analytics::UsageTrends::MeasurementsResolver
+ field :user, Types::UserType,
null: true,
- description: 'All jobs on this GitLab instance.',
- resolver: ::Resolvers::Ci::AllJobsResolver
+ description: 'Find a user.',
+ resolver: Resolvers::UserResolver
+ field :users, Types::UserType.connection_type,
+ null: true,
+ description: 'Find users.',
+ resolver: Resolvers::UsersResolver
+ field :work_item, Types::WorkItemType,
+ null: true,
+ resolver: Resolvers::WorkItemResolver,
+ alpha: { milestone: '15.1' },
+ description: 'Find a work item.'
def design_management
DesignManagementObject.new(nil)
diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb
index 8c90a8df611..c5d6e26e94b 100644
--- a/app/graphql/types/repository/blob_type.rb
+++ b/app/graphql/types/repository/blob_type.rb
@@ -44,11 +44,11 @@ module Types
field :fork_and_view_path, GraphQL::Types::String, null: true,
description: 'Web path to view this blob using a forked project.'
- field :size, GraphQL::Types::Int, null: true,
- description: 'Size (in bytes) of the blob.'
+ field :size, GraphQL::Types::BigInt, null: true,
+ description: 'Size (in bytes) of the blob.'
- field :raw_size, GraphQL::Types::Int, null: true,
- description: 'Size (in bytes) of the blob, or the blob target if stored externally.'
+ field :raw_size, GraphQL::Types::BigInt, null: true,
+ description: 'Size (in bytes) of the blob, or the blob target if stored externally.'
field :raw_blob, GraphQL::Types::String, null: true, method: :data,
description: 'Raw content of the blob.'
diff --git a/app/graphql/types/time_tracking/timelog_connection_type.rb b/app/graphql/types/time_tracking/timelog_connection_type.rb
new file mode 100644
index 00000000000..43e6955c2a3
--- /dev/null
+++ b/app/graphql/types/time_tracking/timelog_connection_type.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Types
+ module TimeTracking
+ # rubocop: disable Graphql/AuthorizeTypes
+ class TimelogConnectionType < CountableConnectionType
+ field :total_spent_time,
+ GraphQL::Types::Int,
+ null: false,
+ description: 'Total time spent in seconds.'
+
+ def total_spent_time
+ # rubocop: disable CodeReuse/ActiveRecord
+ relation = object.items
+
+ # sometimes relation is an Array
+ relation = relation.reorder(nil) if relation.respond_to?(:reorder)
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ relation.sum(:time_spent)
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/time_tracking/timelog_sort_enum.rb b/app/graphql/types/time_tracking/timelog_sort_enum.rb
new file mode 100644
index 00000000000..ad21c084d78
--- /dev/null
+++ b/app/graphql/types/time_tracking/timelog_sort_enum.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+ module TimeTracking
+ class TimelogSortEnum < SortEnum
+ graphql_name 'TimelogSort'
+ description 'Values for sorting timelogs'
+
+ sortable_fields = ['Spent at', 'Time spent']
+
+ sortable_fields.each do |field|
+ value "#{field.upcase.tr(' ', '_')}_ASC",
+ value: { field: field.downcase.tr(' ', '_'), direction: :asc },
+ description: "#{field} by ascending order."
+ value "#{field.upcase.tr(' ', '_')}_DESC",
+ value: { field: field.downcase.tr(' ', '_'), direction: :desc },
+ description: "#{field} by descending order."
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/timelog_type.rb b/app/graphql/types/timelog_type.rb
index 3856e1aa3b3..3a060518cd9 100644
--- a/app/graphql/types/timelog_type.rb
+++ b/app/graphql/types/timelog_type.rb
@@ -4,6 +4,8 @@ module Types
class TimelogType < BaseObject
graphql_name 'Timelog'
+ connection_type_class(Types::TimeTracking::TimelogConnectionType)
+
authorize :read_issuable
expose_permissions Types::PermissionTypes::Timelog
diff --git a/app/graphql/types/todo_action_enum.rb b/app/graphql/types/todo_action_enum.rb
index 33e1c4e98a4..fda96796c0f 100644
--- a/app/graphql/types/todo_action_enum.rb
+++ b/app/graphql/types/todo_action_enum.rb
@@ -11,6 +11,6 @@ module Types
value 'directly_addressed', value: 7, description: 'User was directly addressed.'
value 'merge_train_removed', value: 8, description: 'Merge request authored by the user was removed from the merge train.'
value 'review_requested', value: 9, description: 'Review was requested from the user.'
- value 'member_access_requested', value: 10, description: 'Group access requested from the user.'
+ value 'member_access_requested', value: 10, description: 'Group or project access requested from the user.'
end
end
diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb
index 51046d09f90..a5bed3b9e19 100644
--- a/app/graphql/types/user_interface.rb
+++ b/app/graphql/types/user_interface.rb
@@ -42,10 +42,23 @@ module Types
null: true,
description: 'User email.', method: :public_email,
deprecated: { reason: :renamed, replacement: 'User.publicEmail', milestone: '13.7' }
+ field :emails,
+ type: Types::Users::EmailType.connection_type,
+ null: true,
+ description: "User's email addresses."
field :public_email,
type: GraphQL::Types::String,
null: true,
description: "User's public email."
+ field :commit_email,
+ type: GraphQL::Types::String,
+ null: true,
+ description: "User's default commit email.",
+ authorize: :read_user_email_address
+ field :namespace_commit_emails,
+ type: Types::Users::NamespaceCommitEmailType.connection_type,
+ null: true,
+ description: "User's custom namespace commit emails."
field :avatar_url,
type: GraphQL::Types::String,
null: true,
diff --git a/app/graphql/types/users/email_type.rb b/app/graphql/types/users/email_type.rb
new file mode 100644
index 00000000000..5e0b9563f57
--- /dev/null
+++ b/app/graphql/types/users/email_type.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Types
+ module Users
+ class EmailType < BaseObject
+ graphql_name 'Email'
+
+ authorize :read_user_email_address
+
+ field :id,
+ GraphQL::Types::ID,
+ null: false,
+ description: 'Internal ID of the email.'
+
+ field :email,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Email address.'
+
+ field :confirmed_at,
+ Types::TimeType,
+ null: true,
+ description: 'Timestamp the email was confirmed.'
+
+ field :created_at,
+ Types::TimeType,
+ null: false,
+ description: 'Timestamp the email was created.'
+
+ field :updated_at,
+ Types::TimeType,
+ null: false,
+ description: 'Timestamp the email was last updated.'
+ end
+ end
+end
diff --git a/app/graphql/types/users/namespace_commit_email_type.rb b/app/graphql/types/users/namespace_commit_email_type.rb
new file mode 100644
index 00000000000..a64423c7169
--- /dev/null
+++ b/app/graphql/types/users/namespace_commit_email_type.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Types
+ module Users
+ class NamespaceCommitEmailType < BaseObject
+ graphql_name 'NamespaceCommitEmail'
+
+ authorize :read_user_email_address
+
+ field :id,
+ GraphQL::Types::ID,
+ null: false,
+ description: 'Internal ID of the namespace commit email.'
+
+ field :email,
+ Types::Users::EmailType,
+ null: false,
+ description: 'Email.'
+
+ field :namespace,
+ Types::NamespaceType,
+ null: false,
+ description: 'Namespace.'
+
+ field :created_at,
+ Types::TimeType,
+ null: false,
+ description: 'Timestamp the namespace commit email was created.'
+
+ field :updated_at,
+ Types::TimeType,
+ null: false,
+ description: 'Timestamp the namespace commit email was last updated.'
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widgets/description_type.rb b/app/graphql/types/work_items/widgets/description_type.rb
index 4861f7f46d8..92dca965b59 100644
--- a/app/graphql/types/work_items/widgets/description_type.rb
+++ b/app/graphql/types/work_items/widgets/description_type.rb
@@ -26,9 +26,7 @@ module Types
null: true,
description: 'User that made the last edit to the work item\'s description.'
- markdown_field :description_html, null: true do |resolved_object|
- resolved_object.work_item
- end
+ markdown_field :description_html, null: true, &:work_item
end
# rubocop:enable Graphql/AuthorizeTypes
end