summaryrefslogtreecommitdiff
path: root/app/graphql
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-11-17 11:33:21 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-11-17 11:33:21 +0000
commit7021455bd1ed7b125c55eb1b33c5a01f2bc55ee0 (patch)
tree5bdc2229f5198d516781f8d24eace62fc7e589e9 /app/graphql
parent185b095e93520f96e9cfc31d9c3e69b498cdab7c (diff)
downloadgitlab-ce-7021455bd1ed7b125c55eb1b33c5a01f2bc55ee0.tar.gz
Add latest changes from gitlab-org/gitlab@15-6-stable-eev15.6.0-rc42
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/graphql_triggers.rb4
-rw-r--r--app/graphql/mutations/ci/job/artifacts_destroy.rb2
-rw-r--r--app/graphql/mutations/ci/job/base.rb2
-rw-r--r--app/graphql/mutations/ci/job_artifact/destroy.rb2
-rw-r--r--app/graphql/mutations/ci/pipeline_schedule/take_ownership.rb27
-rw-r--r--app/graphql/mutations/ci/runner/bulk_delete.rb16
-rw-r--r--app/graphql/mutations/ci/runner/update.rb2
-rw-r--r--app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb3
-rw-r--r--app/graphql/mutations/container_repositories/destroy.rb8
-rw-r--r--app/graphql/mutations/incident_management/timeline_event/create.rb4
-rw-r--r--app/graphql/mutations/incident_management/timeline_event_tag/base.rb25
-rw-r--r--app/graphql/mutations/incident_management/timeline_event_tag/create.rb29
-rw-r--r--app/graphql/mutations/work_items/create.rb9
-rw-r--r--app/graphql/mutations/work_items/create_from_task.rb7
-rw-r--r--app/graphql/mutations/work_items/delete.rb7
-rw-r--r--app/graphql/mutations/work_items/delete_task.rb7
-rw-r--r--app/graphql/mutations/work_items/update.rb7
-rw-r--r--app/graphql/mutations/work_items/update_task.rb7
-rw-r--r--app/graphql/resolvers/base_issues_resolver.rb64
-rw-r--r--app/graphql/resolvers/blobs_resolver.rb2
-rw-r--r--app/graphql/resolvers/bulk_labels_resolver.rb2
-rw-r--r--app/graphql/resolvers/concerns/board_item_filterable.rb18
-rw-r--r--app/graphql/resolvers/concerns/issue_resolver_arguments.rb167
-rw-r--r--app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb35
-rw-r--r--app/graphql/resolvers/concerns/issues/sort_arguments.rb26
-rw-r--r--app/graphql/resolvers/concerns/project_search_arguments.rb1
-rw-r--r--app/graphql/resolvers/concerns/search_arguments.rb10
-rw-r--r--app/graphql/resolvers/group_issues_resolver.rb5
-rw-r--r--app/graphql/resolvers/incident_management/timeline_event_tags_resolver.rb15
-rw-r--r--app/graphql/resolvers/incident_management/timeline_events_resolver.rb8
-rw-r--r--app/graphql/resolvers/issue_status_counts_resolver.rb26
-rw-r--r--app/graphql/resolvers/issues/base_parent_resolver.rb48
-rw-r--r--app/graphql/resolvers/issues/base_resolver.rb186
-rw-r--r--app/graphql/resolvers/issues_resolver.rb29
-rw-r--r--app/graphql/resolvers/project_issues_resolver.rb9
-rw-r--r--app/graphql/resolvers/work_item_resolver.rb5
-rw-r--r--app/graphql/resolvers/work_items/types_resolver.rb10
-rw-r--r--app/graphql/resolvers/work_items_resolver.rb5
-rw-r--r--app/graphql/types/base_argument.rb4
-rw-r--r--app/graphql/types/base_enum.rb4
-rw-r--r--app/graphql/types/base_field.rb4
-rw-r--r--app/graphql/types/boards/board_issue_input_type.rb4
-rw-r--r--app/graphql/types/branch_protections/merge_access_level_type.rb2
-rw-r--r--app/graphql/types/branch_protections/push_access_level_type.rb2
-rw-r--r--app/graphql/types/ci/job_need_union.rb5
-rw-r--r--app/graphql/types/commit_signature_interface.rb37
-rw-r--r--app/graphql/types/commit_signatures/gpg_signature_type.rb29
-rw-r--r--app/graphql/types/commit_signatures/verification_status_enum.rb18
-rw-r--r--app/graphql/types/commit_signatures/x509_signature_type.rb22
-rw-r--r--app/graphql/types/commit_type.rb7
-rw-r--r--app/graphql/types/concerns/gitlab_style_deprecations.rb18
-rw-r--r--app/graphql/types/deployment_details_type.rb4
-rw-r--r--app/graphql/types/deployment_type.rb2
-rw-r--r--app/graphql/types/group_type.rb4
-rw-r--r--app/graphql/types/incident_management/timeline_event_tag_type.rb23
-rw-r--r--app/graphql/types/incident_management/timeline_event_type.rb7
-rw-r--r--app/graphql/types/issue_connection.rb15
-rw-r--r--app/graphql/types/issue_type.rb10
-rw-r--r--app/graphql/types/issue_type_enum.rb6
-rw-r--r--app/graphql/types/issues/unioned_issue_filter_input_type.rb16
-rw-r--r--app/graphql/types/merge_request_type.rb5
-rw-r--r--app/graphql/types/metadata_type.rb2
-rw-r--r--app/graphql/types/mutation_type.rb4
-rw-r--r--app/graphql/types/packages/package_base_type.rb2
-rw-r--r--app/graphql/types/packages/package_links_type.rb19
-rw-r--r--app/graphql/types/permission_types/ci/runner.rb2
-rw-r--r--app/graphql/types/project_type.rb25
-rw-r--r--app/graphql/types/projects/branch_rule_type.rb12
-rw-r--r--app/graphql/types/projects/repository_language_type.rb20
-rw-r--r--app/graphql/types/query_type.rb9
-rw-r--r--app/graphql/types/release_links_type.rb10
-rw-r--r--app/graphql/types/release_source_type.rb2
-rw-r--r--app/graphql/types/release_type.rb2
-rw-r--r--app/graphql/types/repository_type.rb2
-rw-r--r--app/graphql/types/subscription_type.rb3
-rw-r--r--app/graphql/types/work_items/widget_interface.rb5
-rw-r--r--app/graphql/types/work_items/widgets/milestone_input_type.rb17
-rw-r--r--app/graphql/types/work_items/widgets/milestone_type.rb23
-rw-r--r--app/graphql/types/x509_certificate_type.rb39
-rw-r--r--app/graphql/types/x509_issuer_type.rb29
80 files changed, 948 insertions, 366 deletions
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb
index dc4f838ae36..710e7fe110c 100644
--- a/app/graphql/graphql_triggers.rb
+++ b/app/graphql/graphql_triggers.rb
@@ -25,6 +25,10 @@ module GraphqlTriggers
GitlabSchema.subscriptions.trigger('issuableDatesUpdated', { issuable_id: issuable.to_gid }, issuable)
end
+ def self.issuable_milestone_updated(issuable)
+ GitlabSchema.subscriptions.trigger('issuableMilestoneUpdated', { issuable_id: issuable.to_gid }, issuable)
+ end
+
def self.merge_request_reviewers_updated(merge_request)
GitlabSchema.subscriptions.trigger(
'mergeRequestReviewersUpdated',
diff --git a/app/graphql/mutations/ci/job/artifacts_destroy.rb b/app/graphql/mutations/ci/job/artifacts_destroy.rb
index 34c58fc1240..9171299f353 100644
--- a/app/graphql/mutations/ci/job/artifacts_destroy.rb
+++ b/app/graphql/mutations/ci/job/artifacts_destroy.rb
@@ -18,7 +18,7 @@ module Mutations
null: false,
description: 'Number of artifacts deleted.'
- def find_object(id: )
+ def find_object(id:)
GlobalID::Locator.locate(id)
end
diff --git a/app/graphql/mutations/ci/job/base.rb b/app/graphql/mutations/ci/job/base.rb
index 6ea8e25a58d..f68f0507b28 100644
--- a/app/graphql/mutations/ci/job/base.rb
+++ b/app/graphql/mutations/ci/job/base.rb
@@ -10,7 +10,7 @@ module Mutations
required: true,
description: 'ID of the job to mutate.'
- def find_object(id: )
+ def find_object(id:)
GlobalID::Locator.locate(id)
end
end
diff --git a/app/graphql/mutations/ci/job_artifact/destroy.rb b/app/graphql/mutations/ci/job_artifact/destroy.rb
index 47b3535d631..add1b431fbf 100644
--- a/app/graphql/mutations/ci/job_artifact/destroy.rb
+++ b/app/graphql/mutations/ci/job_artifact/destroy.rb
@@ -20,7 +20,7 @@ module Mutations
null: true,
description: 'Deleted artifact.'
- def find_object(id: )
+ def find_object(id:)
GlobalID::Locator.locate(id)
end
diff --git a/app/graphql/mutations/ci/pipeline_schedule/take_ownership.rb b/app/graphql/mutations/ci/pipeline_schedule/take_ownership.rb
new file mode 100644
index 00000000000..2e4312f0045
--- /dev/null
+++ b/app/graphql/mutations/ci/pipeline_schedule/take_ownership.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module PipelineSchedule
+ class TakeOwnership < Base
+ graphql_name 'PipelineScheduleTakeOwnership'
+
+ authorize :take_ownership_pipeline_schedule
+
+ field :pipeline_schedule,
+ Types::Ci::PipelineScheduleType,
+ description: 'Updated pipeline schedule ownership.'
+
+ def resolve(id:)
+ schedule = authorized_find!(id: id)
+
+ service_response = ::Ci::PipelineSchedules::TakeOwnershipService.new(schedule, current_user).execute
+ {
+ pipeline_schedule: schedule,
+ errors: service_response.errors
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/runner/bulk_delete.rb b/app/graphql/mutations/ci/runner/bulk_delete.rb
index 4265099d28e..f053eda0f55 100644
--- a/app/graphql/mutations/ci/runner/bulk_delete.rb
+++ b/app/graphql/mutations/ci/runner/bulk_delete.rb
@@ -25,13 +25,12 @@ module Mutations
'Only present if operation was performed synchronously.'
def resolve(**runner_attrs)
- raise_resource_not_available_error! unless Ability.allowed?(current_user, :delete_runners)
-
if ids = runner_attrs[:ids]
- runners = find_all_runners_by_ids(model_ids_of(ids))
+ runner_ids = model_ids_of(ids)
+ runners = find_all_runners_by_ids(runner_ids)
- result = ::Ci::Runners::BulkDeleteRunnersService.new(runners: runners).execute
- result.payload.slice(:deleted_count, :deleted_ids).merge(errors: [])
+ result = ::Ci::Runners::BulkDeleteRunnersService.new(runners: runners, current_user: current_user).execute
+ result.payload.slice(:deleted_count, :deleted_ids, :errors)
else
{ errors: [] }
end
@@ -39,14 +38,15 @@ module Mutations
private
- def model_ids_of(ids)
- ids.filter_map { |gid| gid.model_id.to_i }
+ def model_ids_of(global_ids)
+ global_ids.filter_map { |gid| gid.model_id.to_i }
end
def find_all_runners_by_ids(ids)
return ::Ci::Runner.none if ids.blank?
- ::Ci::Runner.id_in(ids)
+ limit = ::Ci::Runners::BulkDeleteRunnersService::RUNNER_LIMIT
+ ::Ci::Runner.id_in(ids).limit(limit + 1)
end
end
end
diff --git a/app/graphql/mutations/ci/runner/update.rb b/app/graphql/mutations/ci/runner/update.rb
index 2f2c8c4c668..3c99cde60a4 100644
--- a/app/graphql/mutations/ci/runner/update.rb
+++ b/app/graphql/mutations/ci/runner/update.rb
@@ -68,7 +68,7 @@ module Mutations
response = { runner: runner, errors: [] }
::Ci::Runner.transaction do
- associate_runner_projects(response, runner, associated_projects_ids) if associated_projects_ids.present?
+ associate_runner_projects(response, runner, associated_projects_ids) unless associated_projects_ids.nil?
update_runner(response, runner, runner_attrs)
end
diff --git a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb
index e42e59de78f..6738f268e92 100644
--- a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb
+++ b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb
@@ -33,6 +33,9 @@ module Mutations
argument :labels_widget, ::Types::WorkItems::Widgets::LabelsUpdateInputType,
required: false,
description: 'Input for labels widget.'
+ argument :milestone_widget, ::Types::WorkItems::Widgets::MilestoneInputType,
+ required: false,
+ description: 'Input for milestone widget.'
end
end
end
diff --git a/app/graphql/mutations/container_repositories/destroy.rb b/app/graphql/mutations/container_repositories/destroy.rb
index 2a45291be22..fe1c3fe4e61 100644
--- a/app/graphql/mutations/container_repositories/destroy.rb
+++ b/app/graphql/mutations/container_repositories/destroy.rb
@@ -21,9 +21,11 @@ module Mutations
container_repository = authorized_find!(id: id)
container_repository.delete_scheduled!
- # rubocop:disable CodeReuse/Worker
- DeleteContainerRepositoryWorker.perform_async(current_user.id, container_repository.id)
- # rubocop:enable CodeReuse/Worker
+
+ unless Feature.enabled?(:container_registry_delete_repository_with_cron_worker)
+ DeleteContainerRepositoryWorker.perform_async(current_user.id, container_repository.id) # rubocop:disable CodeReuse/Worker
+ end
+
track_event(:delete_repository, :container)
{
diff --git a/app/graphql/mutations/incident_management/timeline_event/create.rb b/app/graphql/mutations/incident_management/timeline_event/create.rb
index 1907954cada..419b814dc8c 100644
--- a/app/graphql/mutations/incident_management/timeline_event/create.rb
+++ b/app/graphql/mutations/incident_management/timeline_event/create.rb
@@ -18,6 +18,10 @@ module Mutations
required: true,
description: 'Timestamp of when the event occurred.'
+ argument :timeline_event_tag_names, [GraphQL::Types::String],
+ required: false,
+ description: copy_field_description(Types::IncidentManagement::TimelineEventType, :timeline_event_tags)
+
def resolve(incident_id:, **args)
incident = authorized_find!(id: incident_id)
diff --git a/app/graphql/mutations/incident_management/timeline_event_tag/base.rb b/app/graphql/mutations/incident_management/timeline_event_tag/base.rb
new file mode 100644
index 00000000000..b1d07203ca1
--- /dev/null
+++ b/app/graphql/mutations/incident_management/timeline_event_tag/base.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Mutations
+ module IncidentManagement
+ module TimelineEventTag
+ class Base < BaseMutation
+ field :timeline_event_tag,
+ ::Types::IncidentManagement::TimelineEventTagType,
+ null: true,
+ description: 'Timeline event tag.'
+
+ authorize :admin_incident_management_timeline_event_tag
+
+ private
+
+ def response(result)
+ {
+ timeline_event_tag: result.payload[:timeline_event_tag],
+ errors: result.errors
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/incident_management/timeline_event_tag/create.rb b/app/graphql/mutations/incident_management/timeline_event_tag/create.rb
new file mode 100644
index 00000000000..14b1d288365
--- /dev/null
+++ b/app/graphql/mutations/incident_management/timeline_event_tag/create.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Mutations
+ module IncidentManagement
+ module TimelineEventTag
+ class Create < Base
+ graphql_name 'TimelineEventTagCreate'
+
+ include FindsProject
+
+ argument :project_path, GraphQL::Types::ID,
+ required: true,
+ description: 'Project to create the timeline event tag in.'
+
+ argument :name, GraphQL::Types::String,
+ required: true,
+ description: 'Name of the tag.'
+
+ def resolve(project_path:, **args)
+ project = authorized_find!(project_path)
+
+ response ::IncidentManagement::TimelineEventTags::CreateService.new(
+ project, current_user, args
+ ).execute
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb
index ece00e04ed9..793e5d3caf8 100644
--- a/app/graphql/mutations/work_items/create.rb
+++ b/app/graphql/mutations/work_items/create.rb
@@ -9,7 +9,7 @@ module Mutations
include FindsProject
include Mutations::WorkItems::Widgetable
- description "Creates a work item. Available only when feature flag `work_items` is enabled."
+ description "Creates a work item."
authorize :create_work_item
@@ -22,6 +22,9 @@ module Mutations
argument :hierarchy_widget, ::Types::WorkItems::Widgets::HierarchyCreateInputType,
required: false,
description: 'Input for hierarchy widget.'
+ argument :milestone_widget, ::Types::WorkItems::Widgets::MilestoneInputType,
+ required: false,
+ description: 'Input for milestone widget.'
argument :project_path, GraphQL::Types::ID,
required: true,
description: 'Full path of the project the work item is associated with.'
@@ -39,10 +42,6 @@ module Mutations
def resolve(project_path:, **attributes)
project = authorized_find!(project_path)
- unless project.work_items_feature_flag_enabled?
- return { errors: ['`work_items` feature flag disabled for this project'] }
- end
-
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
params = global_id_compatibility_params(attributes).merge(author_id: current_user.id)
type = ::WorkItems::Type.find(attributes[:work_item_type_id])
diff --git a/app/graphql/mutations/work_items/create_from_task.rb b/app/graphql/mutations/work_items/create_from_task.rb
index 5ebe8b2c6d7..4ef8269a42f 100644
--- a/app/graphql/mutations/work_items/create_from_task.rb
+++ b/app/graphql/mutations/work_items/create_from_task.rb
@@ -7,8 +7,7 @@ module Mutations
include Mutations::SpamProtection
- description "Creates a work item from a task in another work item's description." \
- " Available only when feature flag `work_items` is enabled."
+ description "Creates a work item from a task in another work item's description."
authorize :update_work_item
@@ -31,10 +30,6 @@ module Mutations
def resolve(id:, work_item_data:)
work_item = authorized_find!(id: id)
- unless work_item.project.work_items_feature_flag_enabled?
- return { errors: ['`work_items` feature flag disabled for this project'] }
- end
-
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
result = ::WorkItems::CreateFromTaskService.new(
diff --git a/app/graphql/mutations/work_items/delete.rb b/app/graphql/mutations/work_items/delete.rb
index 240a8b4c11e..4b0067d40d4 100644
--- a/app/graphql/mutations/work_items/delete.rb
+++ b/app/graphql/mutations/work_items/delete.rb
@@ -4,8 +4,7 @@ module Mutations
module WorkItems
class Delete < BaseMutation
graphql_name 'WorkItemDelete'
- description "Deletes a work item." \
- " Available only when feature flag `work_items` is enabled."
+ description "Deletes a work item."
authorize :delete_work_item
@@ -20,10 +19,6 @@ module Mutations
def resolve(id:)
work_item = authorized_find!(id: id)
- unless work_item.project.work_items_feature_flag_enabled?
- return { errors: ['`work_items` feature flag disabled for this project'] }
- end
-
result = ::WorkItems::DeleteService.new(
project: work_item.project,
current_user: current_user
diff --git a/app/graphql/mutations/work_items/delete_task.rb b/app/graphql/mutations/work_items/delete_task.rb
index b1bfed0cbf1..47ab3748ab4 100644
--- a/app/graphql/mutations/work_items/delete_task.rb
+++ b/app/graphql/mutations/work_items/delete_task.rb
@@ -5,8 +5,7 @@ module Mutations
class DeleteTask < BaseMutation
graphql_name 'WorkItemDeleteTask'
- description "Deletes a task in a work item's description." \
- ' Available only when feature flag `work_items` is enabled.'
+ description "Deletes a task in a work item's description."
authorize :update_work_item
@@ -29,10 +28,6 @@ module Mutations
work_item = authorized_find!(id: id)
task_data[:task] = authorized_find_task!(task_data[:id])
- unless work_item.project.work_items_feature_flag_enabled?
- return { errors: ['`work_items` feature flag disabled for this project'] }
- end
-
result = ::WorkItems::DeleteTaskService.new(
work_item: work_item,
current_user: current_user,
diff --git a/app/graphql/mutations/work_items/update.rb b/app/graphql/mutations/work_items/update.rb
index b4ed0a1a3ca..04c63d8e876 100644
--- a/app/graphql/mutations/work_items/update.rb
+++ b/app/graphql/mutations/work_items/update.rb
@@ -4,8 +4,7 @@ module Mutations
module WorkItems
class Update < BaseMutation
graphql_name 'WorkItemUpdate'
- description "Updates a work item by Global ID." \
- " Available only when feature flag `work_items` is enabled."
+ description "Updates a work item by Global ID."
include Mutations::SpamProtection
include Mutations::WorkItems::UpdateArguments
@@ -20,10 +19,6 @@ module Mutations
def resolve(id:, **attributes)
work_item = authorized_find!(id: id)
- unless work_item.project.work_items_feature_flag_enabled?
- return { errors: ['`work_items` feature flag disabled for this project'] }
- end
-
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
widget_params = extract_widget_params!(work_item.work_item_type, attributes)
diff --git a/app/graphql/mutations/work_items/update_task.rb b/app/graphql/mutations/work_items/update_task.rb
index 35fbe672b66..aeb4f1d0f06 100644
--- a/app/graphql/mutations/work_items/update_task.rb
+++ b/app/graphql/mutations/work_items/update_task.rb
@@ -4,8 +4,7 @@ module Mutations
module WorkItems
class UpdateTask < BaseMutation
graphql_name 'WorkItemUpdateTask'
- description "Updates a work item's task by Global ID." \
- " Available only when feature flag `work_items` is enabled."
+ description "Updates a work item's task by Global ID."
include Mutations::SpamProtection
@@ -30,10 +29,6 @@ module Mutations
work_item = authorized_find!(id: id)
task = authorized_find_task!(task_data_hash[:id])
- unless work_item.project.work_items_feature_flag_enabled?
- return { errors: ['`work_items` feature flag disabled for this project'] }
- end
-
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
::WorkItems::UpdateService.new(
diff --git a/app/graphql/resolvers/base_issues_resolver.rb b/app/graphql/resolvers/base_issues_resolver.rb
deleted file mode 100644
index 6357132705e..00000000000
--- a/app/graphql/resolvers/base_issues_resolver.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-module Resolvers
- class BaseIssuesResolver < BaseResolver
- prepend IssueResolverArguments
-
- argument :sort, Types::IssueSortEnum,
- description: 'Sort issues by this criteria.',
- required: false,
- default_value: :created_desc
- argument :state, Types::IssuableStateEnum,
- required: false,
- description: 'Current state of this issue.'
-
- # see app/graphql/types/issue_connection.rb
- type 'Types::IssueConnection', null: true
-
- NON_STABLE_CURSOR_SORTS = %i[priority_asc priority_desc
- popularity_asc popularity_desc
- label_priority_asc label_priority_desc
- milestone_due_asc milestone_due_desc
- escalation_status_asc escalation_status_desc].freeze
-
- def continue_issue_resolve(parent, finder, **args)
- issues = Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all { |q| apply_lookahead(q) }
-
- if non_stable_cursor_sort?(args[:sort])
- # Certain complex sorts are not supported by the stable cursor pagination yet.
- # In these cases, we use offset pagination, so we return the correct connection.
- offset_pagination(issues)
- else
- issues
- end
- end
-
- private
-
- def unconditional_includes
- [
- {
- project: [:project_feature, :group]
- },
- :author
- ]
- end
-
- def preloads
- {
- alert_management_alert: [:alert_management_alert],
- assignees: [:assignees],
- participants: Issue.participant_includes,
- timelogs: [:timelogs],
- customer_relations_contacts: { customer_relations_contacts: [:group] },
- escalation_status: [:incident_management_issuable_escalation_status]
- }
- end
-
- def non_stable_cursor_sort?(sort)
- NON_STABLE_CURSOR_SORTS.include?(sort)
- end
- end
-end
-
-Resolvers::BaseIssuesResolver.prepend_mod_with('Resolvers::BaseIssuesResolver')
diff --git a/app/graphql/resolvers/blobs_resolver.rb b/app/graphql/resolvers/blobs_resolver.rb
index 0704a845bb0..fb5fa4465f9 100644
--- a/app/graphql/resolvers/blobs_resolver.rb
+++ b/app/graphql/resolvers/blobs_resolver.rb
@@ -5,7 +5,7 @@ module Resolvers
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::Tree::BlobType.connection_type, null: true
- authorize :download_code
+ authorize :read_code
calls_gitaly!
alias_method :repository, :object
diff --git a/app/graphql/resolvers/bulk_labels_resolver.rb b/app/graphql/resolvers/bulk_labels_resolver.rb
index 7362e257fb6..d7e9564352d 100644
--- a/app/graphql/resolvers/bulk_labels_resolver.rb
+++ b/app/graphql/resolvers/bulk_labels_resolver.rb
@@ -9,7 +9,7 @@ module Resolvers
def resolve
authorize!(object)
- BatchLoader::GraphQL.for(object.id).batch(cache: false) do |ids, loader, args|
+ BatchLoader::GraphQL.for(object.id).batch(key: object.class.name, cache: false) do |ids, loader, args|
labels = Label.for_targets(object.class.id_in(ids)).group_by(&:target_id)
ids.each do |id|
diff --git a/app/graphql/resolvers/concerns/board_item_filterable.rb b/app/graphql/resolvers/concerns/board_item_filterable.rb
index 1457a02e44f..9c0ada4f72c 100644
--- a/app/graphql/resolvers/concerns/board_item_filterable.rb
+++ b/app/graphql/resolvers/concerns/board_item_filterable.rb
@@ -14,6 +14,16 @@ module BoardItemFilterable
set_filter_values(filters[:not])
end
+ if filters[:or]
+ if ::Feature.disabled?(:or_issuable_queries, resource_parent)
+ raise ::Gitlab::Graphql::Errors::ArgumentError,
+ "'or' arguments are only allowed when the `or_issuable_queries` feature flag is enabled."
+ end
+
+ rewrite_param_name(filters[:or], :author_usernames, :author_username)
+ rewrite_param_name(filters[:or], :assignee_usernames, :assignee_username)
+ end
+
filters
end
@@ -30,6 +40,14 @@ module BoardItemFilterable
filters[:assignee_id] = filters.delete(:assignee_wildcard_id)
end
end
+
+ def rewrite_param_name(filters, old_name, new_name)
+ filters[new_name] = filters.delete(old_name) if filters[old_name].present?
+ end
+
+ def resource_parent
+ respond_to?(:board) ? board.resource_parent : list.board.resource_parent
+ end
end
::BoardItemFilterable.prepend_mod_with('Resolvers::BoardItemFilterable')
diff --git a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
deleted file mode 100644
index 8295bd58388..00000000000
--- a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
+++ /dev/null
@@ -1,167 +0,0 @@
-# frozen_string_literal: true
-
-module IssueResolverArguments
- extend ActiveSupport::Concern
-
- prepended do
- include SearchArguments
- include LooksAhead
-
- argument :iid, GraphQL::Types::String,
- required: false,
- description: 'IID of the issue. For example, "1".'
- argument :iids, [GraphQL::Types::String],
- required: false,
- description: 'List of IIDs of issues. For example, `["1", "2"]`.'
- argument :label_name, [GraphQL::Types::String, null: true],
- required: false,
- description: 'Labels applied to this issue.'
- argument :milestone_title, [GraphQL::Types::String, null: true],
- required: false,
- description: 'Milestone applied to this issue.'
- argument :author_username, GraphQL::Types::String,
- required: false,
- description: 'Username of the author of the issue.'
- argument :assignee_username, GraphQL::Types::String,
- required: false,
- description: 'Username of a user assigned to the issue.',
- deprecated: { reason: 'Use `assigneeUsernames`', milestone: '13.11' }
- argument :assignee_usernames, [GraphQL::Types::String],
- required: false,
- description: 'Usernames of users assigned to the issue.'
- argument :assignee_id, GraphQL::Types::String,
- required: false,
- description: 'ID of a user assigned to the issues. Wildcard values "NONE" and "ANY" are supported.'
- argument :created_before, Types::TimeType,
- required: false,
- description: 'Issues created before this date.'
- argument :created_after, Types::TimeType,
- required: false,
- description: 'Issues created after this date.'
- argument :updated_before, Types::TimeType,
- required: false,
- description: 'Issues updated before this date.'
- argument :updated_after, Types::TimeType,
- required: false,
- description: 'Issues updated after this date.'
- argument :closed_before, Types::TimeType,
- required: false,
- description: 'Issues closed before this date.'
- argument :closed_after, Types::TimeType,
- required: false,
- description: 'Issues closed after this date.'
- argument :types, [Types::IssueTypeEnum],
- as: :issue_types,
- description: 'Filter issues by the given issue types.',
- required: false
- argument :milestone_wildcard_id, ::Types::MilestoneWildcardIdEnum,
- required: false,
- description: 'Filter issues by milestone ID wildcard.'
- argument :my_reaction_emoji, GraphQL::Types::String,
- required: false,
- description: 'Filter by reaction emoji applied by the current user. Wildcard values "NONE" and "ANY" are supported.'
- argument :confidential,
- GraphQL::Types::Boolean,
- required: false,
- description: 'Filter for confidential issues. If "false", excludes confidential issues. If "true", returns only confidential issues.'
- argument :not, Types::Issues::NegatedIssueFilterInputType,
- description: 'Negated arguments.',
- required: false
- argument :crm_contact_id, GraphQL::Types::String,
- required: false,
- description: 'ID of a contact assigned to the issues.'
- argument :crm_organization_id, GraphQL::Types::String,
- required: false,
- description: 'ID of an organization assigned to the issues.'
- end
-
- def resolve_with_lookahead(**args)
- return Issue.none if resource_parent.nil?
-
- finder = IssuesFinder.new(current_user, prepare_finder_params(args))
-
- continue_issue_resolve(resource_parent, finder, **args)
- end
-
- def ready?(**args)
- args[:not] = args[:not].to_h if args[:not].present?
-
- params_not_mutually_exclusive(args, mutually_exclusive_assignee_username_args)
- params_not_mutually_exclusive(args, mutually_exclusive_milestone_args)
- params_not_mutually_exclusive(args.fetch(:not, {}), mutually_exclusive_milestone_args)
- params_not_mutually_exclusive(args, mutually_exclusive_release_tag_args)
-
- super
- end
-
- class_methods do
- def resolver_complexity(args, child_complexity:)
- complexity = super
- complexity += 2 if args[:labelName]
-
- complexity
- end
-
- def accept_release_tag
- argument :release_tag, [GraphQL::Types::String],
- required: false,
- description: "Release tag associated with the issue's milestone."
- argument :release_tag_wildcard_id, Types::ReleaseTagWildcardIdEnum,
- required: false,
- description: 'Filter issues by release tag ID wildcard.'
- end
- end
-
- private
-
- def prepare_finder_params(args)
- params = super(args)
- params[:iids] ||= [params.delete(:iid)].compact if params[:iid]
- params[:attempt_project_search_optimizations] = true if params[:search].present?
-
- prepare_assignee_username_params(params)
- prepare_release_tag_params(params)
-
- params
- end
-
- def prepare_release_tag_params(args)
- release_tag_wildcard = args.delete(:release_tag_wildcard_id)
- return if release_tag_wildcard.blank?
-
- args[:release_tag] ||= release_tag_wildcard
- end
-
- def prepare_assignee_username_params(args)
- args[:assignee_username] = args.delete(:assignee_usernames) if args[:assignee_usernames].present?
- args[:not][:assignee_username] = args[:not].delete(:assignee_usernames) if args.dig(:not, :assignee_usernames).present?
- end
-
- def mutually_exclusive_release_tag_args
- [:release_tag, :release_tag_wildcard_id]
- end
-
- def mutually_exclusive_milestone_args
- [:milestone_title, :milestone_wildcard_id]
- end
-
- def mutually_exclusive_assignee_username_args
- [:assignee_usernames, :assignee_username]
- end
-
- def params_not_mutually_exclusive(args, mutually_exclusive_args)
- if args.slice(*mutually_exclusive_args).compact.size > 1
- arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(', ')
- raise ::Gitlab::Graphql::Errors::ArgumentError, "only one of [#{arg_str}] arguments is allowed at the same time."
- end
- end
-
- def resource_parent
- # The project could have been loaded in batch by `BatchLoader`.
- # At this point we need the `id` of the project to query for issues, so
- # make sure it's loaded and not `nil` before continuing.
- strong_memoize(:resource_parent) do
- object.respond_to?(:sync) ? object.sync : object
- end
- end
-end
diff --git a/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb b/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb
new file mode 100644
index 00000000000..c6e32be245d
--- /dev/null
+++ b/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Issues
+ module LookAheadPreloads
+ extend ActiveSupport::Concern
+
+ prepended do
+ include ::LooksAhead
+ end
+
+ private
+
+ def unconditional_includes
+ [
+ {
+ project: [:project_feature, :group]
+ },
+ :author
+ ]
+ end
+
+ def preloads
+ {
+ alert_management_alert: [:alert_management_alert],
+ assignees: [:assignees],
+ participants: Issue.participant_includes,
+ timelogs: [:timelogs],
+ customer_relations_contacts: { customer_relations_contacts: [:group] },
+ escalation_status: [:incident_management_issuable_escalation_status]
+ }
+ end
+ end
+end
+
+Issues::LookAheadPreloads.prepend_mod
diff --git a/app/graphql/resolvers/concerns/issues/sort_arguments.rb b/app/graphql/resolvers/concerns/issues/sort_arguments.rb
new file mode 100644
index 00000000000..70ae6bd8a5b
--- /dev/null
+++ b/app/graphql/resolvers/concerns/issues/sort_arguments.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Issues
+ module SortArguments
+ extend ActiveSupport::Concern
+
+ NON_STABLE_CURSOR_SORTS = %i[priority_asc priority_desc
+ popularity_asc popularity_desc
+ label_priority_asc label_priority_desc
+ milestone_due_asc milestone_due_desc
+ escalation_status_asc escalation_status_desc].freeze
+
+ included do
+ argument :sort, Types::IssueSortEnum,
+ description: 'Sort issues by this criteria.',
+ required: false,
+ default_value: :created_desc
+ end
+
+ private
+
+ def non_stable_cursor_sort?(sort)
+ NON_STABLE_CURSOR_SORTS.include?(sort)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/project_search_arguments.rb b/app/graphql/resolvers/concerns/project_search_arguments.rb
index 7e03963f412..faf3b85fc14 100644
--- a/app/graphql/resolvers/concerns/project_search_arguments.rb
+++ b/app/graphql/resolvers/concerns/project_search_arguments.rb
@@ -25,7 +25,6 @@ module ProjectSearchArguments
def project_finder_params(params)
{
- without_deleted: true,
non_public: params[:membership],
search: params[:search],
search_namespaces: params[:search_namespaces],
diff --git a/app/graphql/resolvers/concerns/search_arguments.rb b/app/graphql/resolvers/concerns/search_arguments.rb
index 95c6dbf7497..ccc012f2bf9 100644
--- a/app/graphql/resolvers/concerns/search_arguments.rb
+++ b/app/graphql/resolvers/concerns/search_arguments.rb
@@ -46,9 +46,17 @@ module SearchArguments
def prepare_search_params(args)
return args unless args[:search].present?
+ args[:in] = args[:in].join(',') if args[:in].present?
+ set_search_optimization_param(args)
+
+ args
+ end
+
+ def set_search_optimization_param(args)
+ return args unless respond_to?(:resource_parent, true) && resource_parent.present?
+
parent_type = resource_parent.is_a?(Project) ? :project : :group
args[:"attempt_#{parent_type}_search_optimizations"] = true
- args[:in] = args[:in].join(',') if args[:in].present?
args
end
diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb
index 05c5e803539..43f01395896 100644
--- a/app/graphql/resolvers/group_issues_resolver.rb
+++ b/app/graphql/resolvers/group_issues_resolver.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from BaseIssuesResolver)
+# rubocop:disable Graphql/ResolverType (inherited from Issues::BaseParentResolver)
module Resolvers
- class GroupIssuesResolver < BaseIssuesResolver
+ class GroupIssuesResolver < Issues::BaseParentResolver
def self.issuable_collection_name
'issues'
end
@@ -18,3 +18,4 @@ module Resolvers
end
end
end
+# rubocop:enable Graphql/ResolverType
diff --git a/app/graphql/resolvers/incident_management/timeline_event_tags_resolver.rb b/app/graphql/resolvers/incident_management/timeline_event_tags_resolver.rb
new file mode 100644
index 00000000000..ac6577d119b
--- /dev/null
+++ b/app/graphql/resolvers/incident_management/timeline_event_tags_resolver.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module IncidentManagement
+ class TimelineEventTagsResolver < BaseResolver
+ include LooksAhead
+
+ type ::Types::IncidentManagement::TimelineEventTagType.connection_type, null: true
+
+ def resolve(**args)
+ apply_lookahead(::IncidentManagement::TimelineEventTagsFinder.new(current_user, object, args).execute)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/incident_management/timeline_events_resolver.rb b/app/graphql/resolvers/incident_management/timeline_events_resolver.rb
index b9978259e6b..0d46b1387b0 100644
--- a/app/graphql/resolvers/incident_management/timeline_events_resolver.rb
+++ b/app/graphql/resolvers/incident_management/timeline_events_resolver.rb
@@ -22,11 +22,17 @@ module Resolvers
prepare: ->(id, ctx) { id.model_id }
end
- def resolve(**args)
+ def resolve_with_lookahead(**args)
incident = args[:incident_id].find
apply_lookahead(::IncidentManagement::TimelineEventsFinder.new(current_user, incident, args).execute)
end
+
+ def preloads
+ {
+ timeline_event_tags: [:timeline_event_tags]
+ }
+ end
end
end
end
diff --git a/app/graphql/resolvers/issue_status_counts_resolver.rb b/app/graphql/resolvers/issue_status_counts_resolver.rb
index db5c91daac2..92cda77d717 100644
--- a/app/graphql/resolvers/issue_status_counts_resolver.rb
+++ b/app/graphql/resolvers/issue_status_counts_resolver.rb
@@ -1,17 +1,29 @@
# frozen_string_literal: true
module Resolvers
- class IssueStatusCountsResolver < BaseResolver
- prepend IssueResolverArguments
-
+ class IssueStatusCountsResolver < Issues::BaseResolver
type Types::IssueStatusCountsType, null: true
+
accept_release_tag
- extras [:lookahead]
+ def resolve(**args)
+ return Issue.none if resource_parent.nil?
+
+ finder = IssuesFinder.new(current_user, prepare_finder_params(args))
+ finder.parent_param = resource_parent
+
+ Gitlab::IssuablesCountForState.new(finder, resource_parent)
+ end
+
+ private
- def continue_issue_resolve(parent, finder, **args)
- finder.parent_param = parent
- apply_lookahead(Gitlab::IssuablesCountForState.new(finder, parent))
+ def resource_parent
+ # The project could have been loaded in batch by `BatchLoader`.
+ # At this point we need the `id` of the project to query for issues, so
+ # make sure it's loaded and not `nil` before continuing.
+ strong_memoize(:resource_parent) do
+ object.respond_to?(:sync) ? object.sync : object
+ end
end
end
end
diff --git a/app/graphql/resolvers/issues/base_parent_resolver.rb b/app/graphql/resolvers/issues/base_parent_resolver.rb
new file mode 100644
index 00000000000..6308e56f049
--- /dev/null
+++ b/app/graphql/resolvers/issues/base_parent_resolver.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Issues
+ class BaseParentResolver < Issues::BaseResolver
+ prepend ::Issues::LookAheadPreloads
+ include ::Issues::SortArguments
+
+ argument :state, Types::IssuableStateEnum,
+ required: false,
+ description: 'Current state of this issue.'
+
+ # see app/graphql/types/issue_connection.rb
+ type 'Types::IssueConnection', null: true
+
+ def resolve_with_lookahead(**args)
+ return Issue.none if resource_parent.nil?
+
+ finder = IssuesFinder.new(current_user, prepare_finder_params(args))
+
+ issues = Gitlab::Graphql::Loaders::IssuableLoader.new(resource_parent, finder).batching_find_all do |q|
+ apply_lookahead(q)
+ end
+
+ if non_stable_cursor_sort?(args[:sort])
+ # Certain complex sorts are not supported by the stable cursor pagination yet.
+ # In these cases, we use offset pagination, so we return the correct connection.
+ offset_pagination(issues)
+ else
+ issues
+ end
+ end
+
+ private
+
+ def resource_parent
+ # The project could have been loaded in batch by `BatchLoader`.
+ # At this point we need the `id` of the project to query for issues, so
+ # make sure it's loaded and not `nil` before continuing.
+ strong_memoize(:resource_parent) do
+ object.respond_to?(:sync) ? object.sync : object
+ end
+ end
+ end
+ end
+end
+
+Resolvers::Issues::BaseParentResolver.prepend_mod
diff --git a/app/graphql/resolvers/issues/base_resolver.rb b/app/graphql/resolvers/issues/base_resolver.rb
new file mode 100644
index 00000000000..9a2c4572abb
--- /dev/null
+++ b/app/graphql/resolvers/issues/base_resolver.rb
@@ -0,0 +1,186 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Issues
+ # rubocop:disable Graphql/ResolverType
+ class BaseResolver < Resolvers::BaseResolver
+ include SearchArguments
+
+ argument :assignee_id, GraphQL::Types::String,
+ required: false,
+ description: 'ID of a user assigned to the issues. Wildcard values "NONE" and "ANY" are supported.'
+ argument :assignee_username, GraphQL::Types::String,
+ required: false,
+ description: 'Username of a user assigned to the issue.',
+ deprecated: { reason: 'Use `assigneeUsernames`', milestone: '13.11' }
+ argument :assignee_usernames, [GraphQL::Types::String],
+ required: false,
+ description: 'Usernames of users assigned to the issue.'
+ argument :author_username, GraphQL::Types::String,
+ required: false,
+ description: 'Username of the author of the issue.'
+ argument :closed_after, Types::TimeType,
+ required: false,
+ description: 'Issues closed after this date.'
+ argument :closed_before, Types::TimeType,
+ required: false,
+ description: 'Issues closed before this date.'
+ argument :confidential,
+ GraphQL::Types::Boolean,
+ required: false,
+ description: 'Filter for confidential issues. If "false", excludes confidential issues.' \
+ ' If "true", returns only confidential issues.'
+ argument :created_after, Types::TimeType,
+ required: false,
+ description: 'Issues created after this date.'
+ argument :created_before, Types::TimeType,
+ required: false,
+ description: 'Issues created before this date.'
+ argument :crm_contact_id, GraphQL::Types::String,
+ required: false,
+ description: 'ID of a contact assigned to the issues.'
+ argument :crm_organization_id, GraphQL::Types::String,
+ required: false,
+ description: 'ID of an organization assigned to the issues.'
+ argument :iid, GraphQL::Types::String,
+ required: false,
+ description: 'IID of the issue. For example, "1".'
+ argument :iids, [GraphQL::Types::String],
+ required: false,
+ description: 'List of IIDs of issues. For example, `["1", "2"]`.'
+ argument :label_name, [GraphQL::Types::String, { null: true }],
+ required: false,
+ description: 'Labels applied to this issue.'
+ argument :milestone_title, [GraphQL::Types::String, { null: true }],
+ required: false,
+ description: 'Milestone applied to this issue.'
+ argument :milestone_wildcard_id, ::Types::MilestoneWildcardIdEnum,
+ required: false,
+ description: 'Filter issues by milestone ID wildcard.'
+ argument :my_reaction_emoji, GraphQL::Types::String,
+ required: false,
+ description: 'Filter by reaction emoji applied by the current user.' \
+ ' Wildcard values "NONE" and "ANY" are supported.'
+ argument :not, Types::Issues::NegatedIssueFilterInputType,
+ description: 'Negated arguments.',
+ required: false
+ argument :or, Types::Issues::UnionedIssueFilterInputType,
+ description: 'List of arguments with inclusive OR.',
+ required: false
+ argument :types, [Types::IssueTypeEnum],
+ as: :issue_types,
+ description: 'Filter issues by the given issue types.',
+ required: false
+ argument :updated_after, Types::TimeType,
+ required: false,
+ description: 'Issues updated after this date.'
+ argument :updated_before, Types::TimeType,
+ required: false,
+ description: 'Issues updated before this date.'
+
+ class << self
+ def resolver_complexity(args, child_complexity:)
+ complexity = super
+ complexity += 2 if args[:labelName]
+
+ complexity
+ end
+
+ def accept_release_tag
+ argument :release_tag, [GraphQL::Types::String],
+ required: false,
+ description: "Release tag associated with the issue's milestone."
+ argument :release_tag_wildcard_id, Types::ReleaseTagWildcardIdEnum,
+ required: false,
+ description: 'Filter issues by release tag ID wildcard.'
+ end
+ end
+
+ def ready?(**args)
+ if args[:or].present? && or_issuable_queries_disabled?
+ raise ::Gitlab::Graphql::Errors::ArgumentError,
+ "'or' arguments are only allowed when the `or_issuable_queries` feature flag is enabled."
+ end
+
+ args[:not] = args[:not].to_h if args[:not]
+ args[:or] = args[:or].to_h if args[:or]
+
+ params_not_mutually_exclusive(args, mutually_exclusive_assignee_username_args)
+ params_not_mutually_exclusive(args, mutually_exclusive_milestone_args)
+ params_not_mutually_exclusive(args.fetch(:not, {}), mutually_exclusive_milestone_args)
+ params_not_mutually_exclusive(args, mutually_exclusive_release_tag_args)
+
+ super
+ end
+
+ private
+
+ def or_issuable_queries_disabled?
+ if respond_to?(:resource_parent, true)
+ ::Feature.disabled?(:or_issuable_queries, resource_parent)
+ else
+ ::Feature.disabled?(:or_issuable_queries)
+ end
+ end
+
+ def prepare_finder_params(args)
+ params = super(args)
+ params[:not] = params[:not].to_h if params[:not]
+ params[:or] = params[:or].to_h if params[:or]
+ params[:iids] ||= [params.delete(:iid)].compact if params[:iid]
+
+ prepare_author_username_params(params)
+ prepare_assignee_username_params(params)
+ prepare_release_tag_params(params)
+
+ params
+ end
+
+ def prepare_release_tag_params(args)
+ release_tag_wildcard = args.delete(:release_tag_wildcard_id)
+ return if release_tag_wildcard.blank?
+
+ 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?
+
+ args[:not][:assignee_username] = args[:not].delete(:assignee_usernames)
+ end
+
+ def mutually_exclusive_release_tag_args
+ [:release_tag, :release_tag_wildcard_id]
+ end
+
+ def mutually_exclusive_milestone_args
+ [:milestone_title, :milestone_wildcard_id]
+ end
+
+ def mutually_exclusive_assignee_username_args
+ [:assignee_usernames, :assignee_username]
+ end
+
+ def params_not_mutually_exclusive(args, mutually_exclusive_args)
+ return unless args.slice(*mutually_exclusive_args).compact.size > 1
+
+ arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(', ')
+ raise ::Gitlab::Graphql::Errors::ArgumentError,
+ "only one of [#{arg_str}] arguments is allowed at the same time."
+ end
+ end
+ # rubocop:enable Graphql/ResolverType
+ end
+end
+
+Resolvers::Issues::BaseResolver.prepend_mod
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index 4b52ef61d57..e3102a7d32a 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -1,8 +1,31 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from BaseIssuesResolver)
module Resolvers
- class IssuesResolver < BaseIssuesResolver
- accept_release_tag
+ class IssuesResolver < Issues::BaseResolver
+ prepend ::Issues::LookAheadPreloads
+ include ::Issues::SortArguments
+
+ argument :state, Types::IssuableStateEnum,
+ required: false,
+ description: 'Current state of this issue.'
+
+ # see app/graphql/types/issue_connection.rb
+ type 'Types::IssueConnection', null: true
+
+ def resolve_with_lookahead(**args)
+ return unless Feature.enabled?(:root_level_issues_query)
+
+ issues = apply_lookahead(
+ IssuesFinder.new(current_user, prepare_finder_params(args)).execute
+ )
+
+ if non_stable_cursor_sort?(args[:sort])
+ # Certain complex sorts are not supported by the stable cursor pagination yet.
+ # In these cases, we use offset pagination, so we return the correct connection.
+ offset_pagination(issues)
+ else
+ issues
+ end
+ end
end
end
diff --git a/app/graphql/resolvers/project_issues_resolver.rb b/app/graphql/resolvers/project_issues_resolver.rb
new file mode 100644
index 00000000000..f869d8f11c6
--- /dev/null
+++ b/app/graphql/resolvers/project_issues_resolver.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+# rubocop:disable Graphql/ResolverType (inherited from Issues::BaseParentResolver)
+module Resolvers
+ class ProjectIssuesResolver < Issues::BaseParentResolver
+ accept_release_tag
+ end
+end
+# rubocop:enable Graphql/ResolverType
diff --git a/app/graphql/resolvers/work_item_resolver.rb b/app/graphql/resolvers/work_item_resolver.rb
index 9eb7d6bc693..b174a0d2693 100644
--- a/app/graphql/resolvers/work_item_resolver.rb
+++ b/app/graphql/resolvers/work_item_resolver.rb
@@ -11,10 +11,7 @@ module Resolvers
argument :id, ::Types::GlobalIDType[::WorkItem], required: true, description: 'Global ID of the work item.'
def resolve(id:)
- work_item = authorized_find!(id: id)
- return unless work_item.project.work_items_feature_flag_enabled?
-
- work_item
+ authorized_find!(id: id)
end
private
diff --git a/app/graphql/resolvers/work_items/types_resolver.rb b/app/graphql/resolvers/work_items/types_resolver.rb
index 5f9f8ab5572..2508125d392 100644
--- a/app/graphql/resolvers/work_items/types_resolver.rb
+++ b/app/graphql/resolvers/work_items/types_resolver.rb
@@ -11,8 +11,6 @@ module Resolvers
' Argument is experimental and can be removed in the future without notice.'
def resolve(taskable: nil)
- return unless feature_flag_enabled_for_parent?(object)
-
# This will require a finder in the future when groups/projects get their work item types
# All groups/projects use the default types for now
base_scope = ::WorkItems::Type.default
@@ -20,14 +18,6 @@ module Resolvers
base_scope.order_by_name_asc
end
-
- private
-
- def feature_flag_enabled_for_parent?(parent)
- return false unless parent.is_a?(::Project) || parent.is_a?(::Group)
-
- parent.work_items_feature_flag_enabled?
- end
end
end
end
diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb
index a4cbcc61ead..42f4f99d4a9 100644
--- a/app/graphql/resolvers/work_items_resolver.rb
+++ b/app/graphql/resolvers/work_items_resolver.rb
@@ -26,7 +26,7 @@ module Resolvers
required: false
def resolve_with_lookahead(**args)
- return WorkItem.none if resource_parent.nil? || !resource_parent.work_items_feature_flag_enabled?
+ return WorkItem.none if resource_parent.nil?
finder = ::WorkItems::WorkItemsFinder.new(current_user, prepare_finder_params(args))
@@ -55,7 +55,8 @@ module Resolvers
last_edited_by: :last_edited_by,
assignees: :assignees,
parent: :work_item_parent,
- labels: :labels
+ labels: :labels,
+ milestone: :milestone
}
end
diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb
index 2c899e9edaa..4086015dad6 100644
--- a/app/graphql/types/base_argument.rb
+++ b/app/graphql/types/base_argument.rb
@@ -4,10 +4,10 @@ module Types
class BaseArgument < GraphQL::Schema::Argument
include GitlabStyleDeprecations
- attr_reader :deprecation, :doc_reference
+ attr_reader :doc_reference
def initialize(*args, **kwargs, &block)
- @deprecation = gitlab_deprecation(kwargs)
+ init_gitlab_deprecation(kwargs)
@doc_reference = kwargs.delete(:see)
# our custom addition `nullable` which allows us to declare
diff --git a/app/graphql/types/base_enum.rb b/app/graphql/types/base_enum.rb
index 0224aeddac6..11877b79e59 100644
--- a/app/graphql/types/base_enum.rb
+++ b/app/graphql/types/base_enum.rb
@@ -6,10 +6,8 @@ module Types
class CustomValue < GraphQL::Schema::EnumValue
include ::GitlabStyleDeprecations
- attr_reader :deprecation
-
def initialize(name, desc = nil, **kwargs)
- @deprecation = gitlab_deprecation(kwargs)
+ init_gitlab_deprecation(kwargs)
super(name, desc, **kwargs)
end
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 6f64e5b5053..36ba3399754 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -8,16 +8,16 @@ module Types
DEFAULT_COMPLEXITY = 1
- attr_reader :deprecation, :doc_reference
+ attr_reader :doc_reference
def initialize(**kwargs, &block)
+ init_gitlab_deprecation(kwargs)
@calls_gitaly = !!kwargs.delete(:calls_gitaly)
@doc_reference = kwargs.delete(:see)
@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])
- @deprecation = gitlab_deprecation(kwargs)
after_connection_extensions = kwargs.delete(:late_extensions) || []
super(**kwargs, &block)
diff --git a/app/graphql/types/boards/board_issue_input_type.rb b/app/graphql/types/boards/board_issue_input_type.rb
index 0dd7fbc87da..897e3d05948 100644
--- a/app/graphql/types/boards/board_issue_input_type.rb
+++ b/app/graphql/types/boards/board_issue_input_type.rb
@@ -9,6 +9,10 @@ module Types
required: false,
description: 'List of negated arguments.'
+ argument :or, Types::Issues::UnionedIssueFilterInputType,
+ required: false,
+ description: 'List of arguments with inclusive OR.'
+
argument :search, GraphQL::Types::String,
required: false,
description: 'Search query for issue title or description.'
diff --git a/app/graphql/types/branch_protections/merge_access_level_type.rb b/app/graphql/types/branch_protections/merge_access_level_type.rb
index 85295e1ba25..e8fcd57ba80 100644
--- a/app/graphql/types/branch_protections/merge_access_level_type.rb
+++ b/app/graphql/types/branch_protections/merge_access_level_type.rb
@@ -4,7 +4,7 @@ module Types
module BranchProtections
class MergeAccessLevelType < BaseAccessLevelType # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'MergeAccessLevel'
- description 'Represents the merge access level of a branch protection.'
+ description 'Defines which user roles, users, or groups can merge into a protected branch.'
accepts ::ProtectedBranch::MergeAccessLevel
end
end
diff --git a/app/graphql/types/branch_protections/push_access_level_type.rb b/app/graphql/types/branch_protections/push_access_level_type.rb
index bfbdc4edbea..c5e21fad88d 100644
--- a/app/graphql/types/branch_protections/push_access_level_type.rb
+++ b/app/graphql/types/branch_protections/push_access_level_type.rb
@@ -4,7 +4,7 @@ module Types
module BranchProtections
class PushAccessLevelType < BaseAccessLevelType # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'PushAccessLevel'
- description 'Represents the push access level of a branch protection.'
+ description 'Defines which user roles, users, or groups can push to a protected branch.'
accepts ::ProtectedBranch::PushAccessLevel
end
end
diff --git a/app/graphql/types/ci/job_need_union.rb b/app/graphql/types/ci/job_need_union.rb
index 59608a6a312..61ad5432db8 100644
--- a/app/graphql/types/ci/job_need_union.rb
+++ b/app/graphql/types/ci/job_need_union.rb
@@ -8,9 +8,10 @@ module Types
possible_types Types::Ci::JobType, Types::Ci::BuildNeedType
def self.resolve_type(object, context)
- if object.is_a?(::Ci::BuildNeed)
+ case object
+ when ::Ci::BuildNeed
Types::Ci::BuildNeedType
- elsif object.is_a?(CommitStatus)
+ when CommitStatus
Types::Ci::JobType
else
raise TypeNotSupportedError
diff --git a/app/graphql/types/commit_signature_interface.rb b/app/graphql/types/commit_signature_interface.rb
new file mode 100644
index 00000000000..6b0c16e538a
--- /dev/null
+++ b/app/graphql/types/commit_signature_interface.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Types
+ module CommitSignatureInterface
+ include Types::BaseInterface
+
+ graphql_name 'CommitSignature'
+
+ description 'Represents signing information for a commit'
+
+ field :verification_status, CommitSignatures::VerificationStatusEnum,
+ null: true,
+ description: 'Indicates verification status of the associated key or certificate.'
+
+ field :commit_sha, GraphQL::Types::String,
+ null: true,
+ description: 'SHA of the associated commit.'
+
+ field :project, Types::ProjectType,
+ null: true,
+ description: 'Project of the associated commit.'
+
+ orphan_types Types::CommitSignatures::GpgSignatureType,
+ Types::CommitSignatures::X509SignatureType
+
+ def self.resolve_type(object, context)
+ case object
+ when ::CommitSignatures::GpgSignature
+ Types::CommitSignatures::GpgSignatureType
+ when ::CommitSignatures::X509CommitSignature
+ Types::CommitSignatures::X509SignatureType
+ else
+ raise 'Unsupported commit signature type'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/commit_signatures/gpg_signature_type.rb b/app/graphql/types/commit_signatures/gpg_signature_type.rb
new file mode 100644
index 00000000000..2a845fff3e2
--- /dev/null
+++ b/app/graphql/types/commit_signatures/gpg_signature_type.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Types
+ module CommitSignatures
+ class GpgSignatureType < Types::BaseObject
+ graphql_name 'GpgSignature'
+ description 'GPG signature for a signed commit'
+
+ implements Types::CommitSignatureInterface
+
+ authorize :download_code
+
+ field :user, Types::UserType, null: true,
+ description: 'User associated with the key.'
+
+ field :gpg_key_user_name, GraphQL::Types::String,
+ null: true,
+ description: 'User name associated with the GPG key.'
+
+ field :gpg_key_user_email, GraphQL::Types::String,
+ null: true,
+ description: 'User email associated with the GPG key.'
+
+ field :gpg_key_primary_keyid, GraphQL::Types::String,
+ null: true,
+ description: 'ID of the GPG key.'
+ end
+ end
+end
diff --git a/app/graphql/types/commit_signatures/verification_status_enum.rb b/app/graphql/types/commit_signatures/verification_status_enum.rb
new file mode 100644
index 00000000000..9df1b7abd82
--- /dev/null
+++ b/app/graphql/types/commit_signatures/verification_status_enum.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+# rubocop:disable Graphql/AuthorizeTypes
+
+module Types
+ module CommitSignatures
+ class VerificationStatusEnum < BaseEnum
+ graphql_name 'VerificationStatus'
+ description 'Verification status of a GPG or X.509 signature for a commit.'
+
+ ::CommitSignatures::GpgSignature.verification_statuses.each do |status, _|
+ value status.upcase, value: status, description: "#{status} verification status."
+ end
+ end
+ end
+end
+
+# rubocop:enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/commit_signatures/x509_signature_type.rb b/app/graphql/types/commit_signatures/x509_signature_type.rb
new file mode 100644
index 00000000000..9ac96dbc015
--- /dev/null
+++ b/app/graphql/types/commit_signatures/x509_signature_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module CommitSignatures
+ class X509SignatureType < Types::BaseObject
+ graphql_name 'X509Signature'
+ description 'X.509 signature for a signed commit'
+
+ implements Types::CommitSignatureInterface
+
+ authorize :download_code
+
+ field :user, Types::UserType, null: true,
+ calls_gitaly: true,
+ description: 'User associated with the key.'
+
+ field :x509_certificate, Types::X509CertificateType,
+ null: true,
+ description: 'Certificate used for the signature.'
+ end
+ end
+end
diff --git a/app/graphql/types/commit_type.rb b/app/graphql/types/commit_type.rb
index dfb02f29fb7..5dd862c7388 100644
--- a/app/graphql/types/commit_type.rb
+++ b/app/graphql/types/commit_type.rb
@@ -4,7 +4,7 @@ module Types
class CommitType < BaseObject
graphql_name 'Commit'
- authorize :download_code
+ authorize :read_code
present_using CommitPresenter
@@ -40,6 +40,11 @@ module Types
field :web_path, type: GraphQL::Types::String, null: false,
description: 'Web path of the commit.'
+ field :signature, type: Types::CommitSignatureInterface,
+ null: true,
+ calls_gitaly: true,
+ description: 'Signature of the commit.'
+
field :signature_html, type: GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'Rendered HTML of the commit signature.'
diff --git a/app/graphql/types/concerns/gitlab_style_deprecations.rb b/app/graphql/types/concerns/gitlab_style_deprecations.rb
index e404f1fcad9..859a27cac4c 100644
--- a/app/graphql/types/concerns/gitlab_style_deprecations.rb
+++ b/app/graphql/types/concerns/gitlab_style_deprecations.rb
@@ -1,14 +1,22 @@
# frozen_string_literal: true
-# Concern for handling deprecation arguments.
+# Concern for handling GraphQL deprecations.
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-schema-items
module GitlabStyleDeprecations
extend ActiveSupport::Concern
+ included do
+ attr_accessor :deprecation
+ end
+
+ def visible?(ctx)
+ super && ctx[:remove_deprecated] == true ? deprecation.nil? : true
+ end
+
private
- # Mutate the arguments, returns the deprecation
- def gitlab_deprecation(kwargs)
+ # Set deprecation, mutate the arguments
+ def init_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-schema-items'
@@ -17,14 +25,12 @@ module GitlabStyleDeprecations
# GitLab allows items to be marked as "alpha", which leverages GraphQL deprecations.
deprecation_args = kwargs.extract!(:alpha, :deprecated)
- deprecation = ::Gitlab::Graphql::Deprecation.parse(**deprecation_args)
+ self.deprecation = ::Gitlab::Graphql::Deprecation.parse(**deprecation_args)
return unless deprecation
raise ArgumentError, "Bad deprecation. #{deprecation.errors.full_messages.to_sentence}" unless deprecation.valid?
kwargs[:deprecation_reason] = deprecation.deprecation_reason
kwargs[:description] = deprecation.edit_description(kwargs[:description])
-
- deprecation
end
end
diff --git a/app/graphql/types/deployment_details_type.rb b/app/graphql/types/deployment_details_type.rb
index f8ba0cb1b24..bbb5cc8e3f1 100644
--- a/app/graphql/types/deployment_details_type.rb
+++ b/app/graphql/types/deployment_details_type.rb
@@ -5,7 +5,7 @@ module Types
graphql_name 'DeploymentDetails'
description 'The details of the deployment'
authorize :read_deployment
- present_using Deployments::DeploymentPresenter
+ present_using ::Deployments::DeploymentPresenter
field :tags,
[Types::DeploymentTagType],
@@ -13,3 +13,5 @@ module Types
calls_gitaly: true
end
end
+
+Types::DeploymentDetailsType.prepend_mod_with('Types::DeploymentDetailsType')
diff --git a/app/graphql/types/deployment_type.rb b/app/graphql/types/deployment_type.rb
index 70a3a4cb574..59b59dc4e1d 100644
--- a/app/graphql/types/deployment_type.rb
+++ b/app/graphql/types/deployment_type.rb
@@ -11,7 +11,7 @@ module Types
graphql_name 'Deployment'
description 'The deployment of an environment'
- present_using Deployments::DeploymentPresenter
+ present_using ::Deployments::DeploymentPresenter
authorize :read_deployment
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index 45357de5502..4e5ddbac8a2 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -231,9 +231,7 @@ module Types
field :work_item_types, Types::WorkItems::TypeType.connection_type,
resolver: Resolvers::WorkItems::TypesResolver,
- description: 'Work item types available to the group.' \
- ' Returns `null` if `work_items` feature flag is disabled.' \
- ' This flag is disabled by default, because the feature is experimental and is subject to change without notice.'
+ description: 'Work item types available to the group.'
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
diff --git a/app/graphql/types/incident_management/timeline_event_tag_type.rb b/app/graphql/types/incident_management/timeline_event_tag_type.rb
new file mode 100644
index 00000000000..452294d4797
--- /dev/null
+++ b/app/graphql/types/incident_management/timeline_event_tag_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ module IncidentManagement
+ class TimelineEventTagType < BaseObject
+ graphql_name 'TimelineEventTagType'
+
+ description 'Describes a tag on an incident management timeline event.'
+
+ authorize :read_incident_management_timeline_event_tag
+
+ field :id,
+ Types::GlobalIDType[::IncidentManagement::TimelineEventTag],
+ null: false,
+ description: 'ID of the timeline event tag.'
+
+ field :name,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Name of the timeline event tag.'
+ end
+ end
+end
diff --git a/app/graphql/types/incident_management/timeline_event_type.rb b/app/graphql/types/incident_management/timeline_event_type.rb
index 690facc8732..939dd9f09e5 100644
--- a/app/graphql/types/incident_management/timeline_event_type.rb
+++ b/app/graphql/types/incident_management/timeline_event_type.rb
@@ -53,6 +53,13 @@ module Types
null: false,
description: 'Timestamp when the event occurred.'
+ field :timeline_event_tags,
+ ::Types::IncidentManagement::TimelineEventTagType.connection_type,
+ null: true,
+ description: 'Tags for the incident timeline event.',
+ extras: [:lookahead],
+ resolver: Resolvers::IncidentManagement::TimelineEventTagsResolver
+
field :created_at,
Types::TimeType,
null: false,
diff --git a/app/graphql/types/issue_connection.rb b/app/graphql/types/issue_connection.rb
index 8e5c88648ea..2f07888b43e 100644
--- a/app/graphql/types/issue_connection.rb
+++ b/app/graphql/types/issue_connection.rb
@@ -1,15 +1,22 @@
# frozen_string_literal: true
# Normally this wouldn't be needed and we could use
+#
# type Types::IssueType.connection_type, null: true
-# in a resolver. However we can end up with cyclic definitions,
-# which can result in errors like
+#
+# in a resolver. However we can end up with cyclic definitions.
+# Running the spec locally can result in errors like
+#
# NameError: uninitialized constant Resolvers::GroupIssuesResolver
#
-# Now we would use
+# or other errors. To fix this, we created this file and use
+#
# type "Types::IssueConnection", null: true
+#
# which gives a delayed resolution, and the proper connection type.
+#
# See app/graphql/resolvers/base_issues_resolver.rb
# Reference: https://github.com/rmosolgo/graphql-ruby/issues/3974#issuecomment-1084444214
-
+# and https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#testing-tips-and-tricks
+#
Types::IssueConnection = Types::IssueType.connection_type
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index 76fac831199..dd2ad26ce49 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -123,7 +123,15 @@ module Types
field :alert_management_alert,
Types::AlertManagement::AlertType,
null: true,
- description: 'Alert associated to this issue.'
+ description: 'Alert associated to this issue.',
+ deprecated: { reason: 'Use `alert_management_alerts`', milestone: '15.6' }
+
+ field :alert_management_alerts,
+ Types::AlertManagement::AlertType.connection_type,
+ null: true,
+ description: 'Alert Management alerts associated to this issue.',
+ extras: [:lookahead],
+ resolver: Resolvers::AlertManagement::AlertResolver
field :severity, Types::IssuableSeverityEnum, null: true,
description: 'Severity level of the incident.'
diff --git a/app/graphql/types/issue_type_enum.rb b/app/graphql/types/issue_type_enum.rb
index 1044c2ceea4..78cd27f60c3 100644
--- a/app/graphql/types/issue_type_enum.rb
+++ b/app/graphql/types/issue_type_enum.rb
@@ -10,7 +10,11 @@ module Types
end
value 'TASK', value: 'task',
- description: 'Task issue type. Available only when feature flag `work_items` is enabled.',
+ description: 'Task issue type.',
alpha: { milestone: '15.2' }
+
+ value 'OBJECTIVE', value: 'objective',
+ description: 'Objective issue type. Available only when feature flag `okrs_mvc` is enabled.',
+ alpha: { milestone: '15.6' }
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
new file mode 100644
index 00000000000..9c7261279c7
--- /dev/null
+++ b/app/graphql/types/issues/unioned_issue_filter_input_type.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ module Issues
+ class UnionedIssueFilterInputType < BaseInputObject
+ graphql_name 'UnionedIssueFilterInput'
+
+ argument :assignee_usernames, [GraphQL::Types::String],
+ required: false,
+ description: 'Filters issues that are assigned to at least one of the given users.'
+ argument :author_usernames, [GraphQL::Types::String],
+ required: false,
+ description: 'Filters issues that are authored by one of the given users.'
+ end
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 8cc600fc68e..49bf7aa638c 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -20,7 +20,7 @@ module Types
description: 'Timestamp of when the merge request was created.'
field :description, GraphQL::Types::String, null: true,
description: 'Description of the merge request (Markdown rendered as HTML for caching).'
- field :diff_head_sha, GraphQL::Types::String, null: true,
+ field :diff_head_sha, GraphQL::Types::String, null: true, calls_gitaly: true,
description: 'Diff head SHA of the merge request.'
field :diff_refs, Types::DiffRefsType, null: true,
description: 'References of the base SHA, the head SHA, and the start SHA for this merge request.'
@@ -100,8 +100,7 @@ module Types
field :detailed_merge_status, ::Types::MergeRequests::DetailedMergeStatusEnum, null: true,
calls_gitaly: true,
- description: 'Detailed merge status of the merge request.',
- alpha: { milestone: '15.3' }
+ description: 'Detailed merge status of the merge request.'
field :mergeable_discussions_state, GraphQL::Types::Boolean, null: true,
calls_gitaly: true,
diff --git a/app/graphql/types/metadata_type.rb b/app/graphql/types/metadata_type.rb
index b00fcfd38ad..492cca365f3 100644
--- a/app/graphql/types/metadata_type.rb
+++ b/app/graphql/types/metadata_type.rb
@@ -6,6 +6,8 @@ module Types
authorize :read_instance_metadata
+ field :enterprise, GraphQL::Types::Boolean, null: false,
+ description: 'Enterprise edition.'
field :kas, ::Types::Metadata::KasType, null: false,
description: 'Metadata about KAS.'
field :revision, GraphQL::Types::String, null: false,
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 5ffc1aeacad..1cbb2ede544 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -47,10 +47,11 @@ module Types
mount_mutation Mutations::DependencyProxy::ImageTtlGroupPolicy::Update
mount_mutation Mutations::DependencyProxy::GroupSettings::Update
mount_mutation Mutations::Environments::CanaryIngress::Update
- mount_mutation Mutations::IncidentManagement::TimelineEvent::Create
+ mount_mutation Mutations::IncidentManagement::TimelineEvent::Create, alpha: { milestone: '15.6' }
mount_mutation Mutations::IncidentManagement::TimelineEvent::PromoteFromNote
mount_mutation Mutations::IncidentManagement::TimelineEvent::Update
mount_mutation Mutations::IncidentManagement::TimelineEvent::Destroy
+ mount_mutation Mutations::IncidentManagement::TimelineEventTag::Create
mount_mutation Mutations::Issues::Create
mount_mutation Mutations::Issues::SetAssignees
mount_mutation Mutations::Issues::SetCrmContacts
@@ -115,6 +116,7 @@ module Types
mount_mutation Mutations::Ci::Pipeline::Destroy
mount_mutation Mutations::Ci::Pipeline::Retry
mount_mutation Mutations::Ci::PipelineSchedule::Delete
+ mount_mutation Mutations::Ci::PipelineSchedule::TakeOwnership
mount_mutation Mutations::Ci::CiCdSettingsUpdate, deprecated: {
reason: :renamed,
replacement: 'ProjectCiCdSettingsUpdate',
diff --git a/app/graphql/types/packages/package_base_type.rb b/app/graphql/types/packages/package_base_type.rb
index 2dc4a2a2bb6..9ec4bb73c47 100644
--- a/app/graphql/types/packages/package_base_type.rb
+++ b/app/graphql/types/packages/package_base_type.rb
@@ -12,6 +12,8 @@ module Types
field :id, ::Types::GlobalIDType[::Packages::Package], null: false, description: 'ID of the package.'
+ field :_links, Types::Packages::PackageLinksType, null: false, method: :itself,
+ description: 'Map of links to perform actions on the package.'
field :can_destroy, GraphQL::Types::Boolean, null: false, description: 'Whether the user can destroy the package.'
field :created_at, Types::TimeType, null: false, description: 'Date of creation.'
field :metadata, Types::Packages::MetadataType,
diff --git a/app/graphql/types/packages/package_links_type.rb b/app/graphql/types/packages/package_links_type.rb
new file mode 100644
index 00000000000..f16937530b9
--- /dev/null
+++ b/app/graphql/types/packages/package_links_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ class PackageLinksType < BaseObject
+ graphql_name 'PackageLinks'
+ description 'Represents links to perform actions on the package'
+ authorize :read_package
+
+ include ::Routing::PackagesHelper
+
+ field :web_path, GraphQL::Types::String, null: true, description: 'Path to the package details page.'
+
+ def web_path
+ package_path(object)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/permission_types/ci/runner.rb b/app/graphql/types/permission_types/ci/runner.rb
index 2e92a4011e9..096dcd272cc 100644
--- a/app/graphql/types/permission_types/ci/runner.rb
+++ b/app/graphql/types/permission_types/ci/runner.rb
@@ -6,7 +6,7 @@ module Types
class Runner < BasePermissionType
graphql_name 'RunnerPermissions'
- abilities :read_runner, :update_runner, :delete_runner
+ abilities :read_runner, :update_runner, :delete_runner, :assign_runner
end
end
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index a41af34ef4c..771dad00fb3 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -37,6 +37,10 @@ module Types
null: false,
description: 'Path of the project.'
+ field :incident_management_timeline_event_tags, [Types::IncidentManagement::TimelineEventTagType],
+ null: true,
+ description: 'Timeline event tags for the project.'
+
field :sast_ci_configuration, Types::CiConfiguration::Sast::Type,
null: true,
calls_gitaly: true,
@@ -226,8 +230,7 @@ module Types
Types::IssueType.connection_type,
null: true,
description: 'Issues of the project.',
- extras: [:lookahead],
- resolver: Resolvers::IssuesResolver
+ resolver: Resolvers::ProjectIssuesResolver
field :work_items,
Types::WorkItemType.connection_type,
@@ -241,7 +244,6 @@ module Types
Types::IssueStatusCountsType,
null: true,
description: 'Counts of issues by status for the project.',
- extras: [:lookahead],
resolver: Resolvers::IssueStatusCountsResolver
field :milestones, Types::MilestoneType.connection_type,
@@ -275,7 +277,7 @@ module Types
Types::IssueType,
null: true,
description: 'A single issue of the project.',
- resolver: Resolvers::IssuesResolver.single
+ resolver: Resolvers::ProjectIssuesResolver.single
field :packages,
description: 'Packages of the project.',
@@ -513,9 +515,7 @@ module Types
field :work_item_types, Types::WorkItems::TypeType.connection_type,
resolver: Resolvers::WorkItems::TypesResolver,
- description: 'Work item types available to the project.' \
- ' Returns `null` if `work_items` feature flag is disabled.' \
- ' This flag is disabled by default, because the feature is experimental and is subject to change without notice.'
+ description: 'Work item types available to the project.'
field :timelog_categories, Types::TimeTracking::TimelogCategoryType.connection_type,
null: true,
@@ -532,6 +532,11 @@ module Types
description: "Branch rules configured for the project.",
resolver: Resolvers::Projects::BranchRulesResolver
+ field :languages, [Types::Projects::RepositoryLanguageType],
+ null: true,
+ description: "Programming languages used in the project.",
+ calls_gitaly: true
+
def timelog_categories
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
end
@@ -598,7 +603,7 @@ module Types
end
def sast_ci_configuration
- return unless Ability.allowed?(current_user, :download_code, object)
+ return unless Ability.allowed?(current_user, :read_code, object)
::Security::CiConfiguration::SastParserService.new(object).configuration
end
@@ -609,6 +614,10 @@ module Types
object.service_desk_address
end
+ def languages
+ ::Projects::RepositoryLanguagesService.new(project, current_user).execute
+ end
+
private
def project
diff --git a/app/graphql/types/projects/branch_rule_type.rb b/app/graphql/types/projects/branch_rule_type.rb
index e7632c17cca..1afd2cc3fef 100644
--- a/app/graphql/types/projects/branch_rule_type.rb
+++ b/app/graphql/types/projects/branch_rule_type.rb
@@ -8,6 +8,8 @@ module Types
accepts ::ProtectedBranch
authorize :read_protected_branch
+ alias_method :branch_rule, :object
+
field :name,
type: GraphQL::Types::String,
null: false,
@@ -20,6 +22,12 @@ module Types
calls_gitaly: true,
description: "Check if this branch rule protects the project's default branch."
+ field :matching_branches_count,
+ type: GraphQL::Types::Int,
+ null: false,
+ calls_gitaly: true,
+ description: 'Number of existing branches that match this branch rule.'
+
field :branch_protection,
type: Types::BranchRules::BranchProtectionType,
null: false,
@@ -35,6 +43,10 @@ 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/projects/repository_language_type.rb b/app/graphql/types/projects/repository_language_type.rb
new file mode 100644
index 00000000000..76c645c0e85
--- /dev/null
+++ b/app/graphql/types/projects/repository_language_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Projects
+ # rubocop: disable Graphql/AuthorizeTypes
+ class RepositoryLanguageType < BaseObject
+ graphql_name 'RepositoryLanguage'
+
+ field :name, GraphQL::Types::String, null: false,
+ description: 'Name of the repository language.'
+
+ field :share, GraphQL::Types::Float, null: true,
+ description: "Percentage of the repository's languages."
+
+ field :color, Types::ColorType, null: true,
+ description: 'Color to visualize the repository language.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 1b39f43659e..21cb3f9e06c 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -82,6 +82,13 @@ module Types
field :echo, resolver: Resolvers::EchoResolver
+ field :issues,
+ 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.'
+
field :issue, Types::IssueType,
null: true,
description: 'Find an issue.' do
@@ -92,7 +99,7 @@ module Types
null: true,
resolver: Resolvers::WorkItemResolver,
alpha: { milestone: '15.1' },
- description: 'Find a work item. Returns `null` if `work_items` feature flag is disabled.'
+ description: 'Find a work item.'
field :merge_request, Types::MergeRequestType,
null: true,
diff --git a/app/graphql/types/release_links_type.rb b/app/graphql/types/release_links_type.rb
index 6bc767152e8..2258adc131c 100644
--- a/app/graphql/types/release_links_type.rb
+++ b/app/graphql/types/release_links_type.rb
@@ -14,12 +14,12 @@ module Types
GraphQL::Types::String,
null: true,
description: 'HTTP URL of the issues page, filtered by this release and `state=closed`.',
- authorize: :download_code
+ authorize: :read_code
field :closed_merge_requests_url,
GraphQL::Types::String,
null: true,
description: 'HTTP URL of the merge request page , filtered by this release and `state=closed`.',
- authorize: :download_code
+ authorize: :read_code
field :edit_url, GraphQL::Types::String, null: true,
description: "HTTP URL of the release's edit page.",
authorize: :update_release
@@ -27,17 +27,17 @@ module Types
GraphQL::Types::String,
null: true,
description: 'HTTP URL of the merge request page , filtered by this release and `state=merged`.',
- authorize: :download_code
+ authorize: :read_code
field :opened_issues_url,
GraphQL::Types::String,
null: true,
description: 'HTTP URL of the issues page, filtered by this release and `state=open`.',
- authorize: :download_code
+ authorize: :read_code
field :opened_merge_requests_url,
GraphQL::Types::String,
null: true,
description: 'HTTP URL of the merge request page, filtered by this release and `state=open`.',
- authorize: :download_code
+ authorize: :read_code
field :self_url, GraphQL::Types::String, null: true,
description: 'HTTP URL of the release.'
end
diff --git a/app/graphql/types/release_source_type.rb b/app/graphql/types/release_source_type.rb
index e05a2926ac1..e1959738c4b 100644
--- a/app/graphql/types/release_source_type.rb
+++ b/app/graphql/types/release_source_type.rb
@@ -5,7 +5,7 @@ module Types
graphql_name 'ReleaseSource'
description 'Represents the source code attached to a release in a particular format'
- authorize :download_code
+ authorize :read_code
field :format, GraphQL::Types::String, null: true,
description: 'Format of the source.'
diff --git a/app/graphql/types/release_type.rb b/app/graphql/types/release_type.rb
index d70fe05c906..a20e53ad1bd 100644
--- a/app/graphql/types/release_type.rb
+++ b/app/graphql/types/release_type.rb
@@ -39,7 +39,7 @@ module Types
description: 'Name of the tag associated with the release.'
field :tag_path, GraphQL::Types::String, null: true,
description: 'Relative web path to the tag associated with the release.',
- authorize: :download_code
+ authorize: :read_code
field :upcoming_release, GraphQL::Types::Boolean, null: true, method: :upcoming_release?,
description: 'Indicates the release is an upcoming release.'
field :historical_release, GraphQL::Types::Boolean, null: true, method: :historical_release?,
diff --git a/app/graphql/types/repository_type.rb b/app/graphql/types/repository_type.rb
index ba94f59ab6c..ab5d1bd8c9e 100644
--- a/app/graphql/types/repository_type.rb
+++ b/app/graphql/types/repository_type.rb
@@ -4,7 +4,7 @@ module Types
class RepositoryType < BaseObject
graphql_name 'Repository'
- authorize :download_code
+ authorize :read_code
field :blobs, Types::Repository::BlobType.connection_type, null: true, resolver: Resolvers::BlobsResolver, calls_gitaly: true,
description: 'Blobs contained within the repository'
diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb
index 3b8f5c64beb..9d5edec82b2 100644
--- a/app/graphql/types/subscription_type.rb
+++ b/app/graphql/types/subscription_type.rb
@@ -22,6 +22,9 @@ module Types
field :issuable_dates_updated, subscription: Subscriptions::IssuableUpdated, null: true,
description: 'Triggered when the due date or start date of an issuable is updated.'
+ field :issuable_milestone_updated, subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the milestone of an issuable is updated.'
+
field :merge_request_reviewers_updated,
subscription: Subscriptions::IssuableUpdated,
null: true,
diff --git a/app/graphql/types/work_items/widget_interface.rb b/app/graphql/types/work_items/widget_interface.rb
index a3943361114..b85d0a23535 100644
--- a/app/graphql/types/work_items/widget_interface.rb
+++ b/app/graphql/types/work_items/widget_interface.rb
@@ -16,7 +16,8 @@ module Types
::Types::WorkItems::Widgets::HierarchyType,
::Types::WorkItems::Widgets::LabelsType,
::Types::WorkItems::Widgets::AssigneesType,
- ::Types::WorkItems::Widgets::StartAndDueDateType
+ ::Types::WorkItems::Widgets::StartAndDueDateType,
+ ::Types::WorkItems::Widgets::MilestoneType
].freeze
def self.ce_orphan_types
@@ -38,6 +39,8 @@ module Types
::Types::WorkItems::Widgets::LabelsType
when ::WorkItems::Widgets::StartAndDueDate
::Types::WorkItems::Widgets::StartAndDueDateType
+ when ::WorkItems::Widgets::Milestone
+ ::Types::WorkItems::Widgets::MilestoneType
else
raise "Unknown GraphQL type for widget #{object}"
end
diff --git a/app/graphql/types/work_items/widgets/milestone_input_type.rb b/app/graphql/types/work_items/widgets/milestone_input_type.rb
new file mode 100644
index 00000000000..996c782373f
--- /dev/null
+++ b/app/graphql/types/work_items/widgets/milestone_input_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module Widgets
+ class MilestoneInputType < BaseInputObject
+ graphql_name 'WorkItemWidgetMilestoneInput'
+
+ argument :milestone_id,
+ Types::GlobalIDType[::Milestone],
+ required: :nullable,
+ prepare: ->(id, _) { id.model_id unless id.nil? },
+ description: 'Milestone to assign to the work item.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widgets/milestone_type.rb b/app/graphql/types/work_items/widgets/milestone_type.rb
new file mode 100644
index 00000000000..73318e58a00
--- /dev/null
+++ b/app/graphql/types/work_items/widgets/milestone_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module Widgets
+ # Disabling widget level authorization as it might be too granular
+ # and we already authorize the parent work item
+ # rubocop:disable Graphql/AuthorizeTypes
+ class MilestoneType < BaseObject
+ graphql_name 'WorkItemWidgetMilestone'
+ description 'Represents a milestone widget'
+
+ implements Types::WorkItems::WidgetInterface
+
+ field :milestone,
+ ::Types::MilestoneType,
+ null: true,
+ description: 'Milestone of the work item.'
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+ end
+ end
+end
diff --git a/app/graphql/types/x509_certificate_type.rb b/app/graphql/types/x509_certificate_type.rb
new file mode 100644
index 00000000000..806aa441af7
--- /dev/null
+++ b/app/graphql/types/x509_certificate_type.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+# rubocop:disable Graphql/AuthorizeTypes
+
+module Types
+ class X509CertificateType < Types::BaseObject
+ graphql_name 'X509Certificate'
+ description 'Represents an X.509 certificate.'
+
+ field :certificate_status, GraphQL::Types::String,
+ null: false,
+ description: 'Indicates if the certificate is good or revoked.'
+
+ field :created_at, Types::TimeType, null: false,
+ description: 'Timestamp of when the certificate was saved.'
+
+ field :email, GraphQL::Types::String, null: false,
+ description: 'Email associated with the cerificate.'
+
+ field :id, GraphQL::Types::ID, null: false, description: 'ID of the certificate.'
+
+ field :serial_number, GraphQL::Types::String, null: false,
+ description: 'Serial number of the certificate.'
+
+ field :subject, GraphQL::Types::String, null: false, description: 'Subject of the certificate.'
+
+ field :subject_key_identifier, GraphQL::Types::String,
+ null: false,
+ description: 'Subject key identifier of the certificate.'
+
+ field :updated_at, Types::TimeType, null: false,
+ description: 'Timestamp of when the certificate was last updated.'
+
+ field :x509_issuer, Types::X509IssuerType, null: false,
+ description: 'Issuer of the certificate.'
+ end
+end
+
+# rubocop:enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/x509_issuer_type.rb b/app/graphql/types/x509_issuer_type.rb
new file mode 100644
index 00000000000..a5759e48ee0
--- /dev/null
+++ b/app/graphql/types/x509_issuer_type.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+# rubocop:disable Graphql/AuthorizeTypes
+
+module Types
+ class X509IssuerType < Types::BaseObject
+ graphql_name 'X509Issuer'
+ description 'Issuer of an X.509 certificate.'
+
+ field :created_at, Types::TimeType, null: true,
+ description: 'Timestamp of when the issuer was created.'
+
+ field :crl_url, GraphQL::Types::String, null: true,
+ description: 'Certificate revokation list of the issuer.'
+
+ field :id, GraphQL::Types::ID, null: true, description: 'ID of the issuer.'
+
+ field :subject, GraphQL::Types::String, null: true, description: 'Subject of the issuer.'
+
+ field :subject_key_identifier, GraphQL::Types::String,
+ null: true,
+ description: 'Subject key identifier of the issuer.'
+
+ field :updated_at, Types::TimeType, null: true,
+ description: 'Timestamp of when the issuer was last updated.'
+ end
+end
+
+# rubocop:enable Graphql/AuthorizeTypes