Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-11-14 11:41:52 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-11-14 11:41:52 +0300
commit585826cb22ecea5998a2c2a4675735c94bdeedac (patch)
tree5b05f0b30d33cef48963609e8a18a4dff260eab3 /app/graphql
parentdf221d036e5d0c6c0ee4d55b9c97f481ee05dee8 (diff)
Add latest changes from gitlab-org/gitlab@16-6-stable-eev16.6.0-rc42
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/mutations/base_mutation.rb6
-rw-r--r--app/graphql/mutations/ci/catalog/resources/create.rb36
-rw-r--r--app/graphql/mutations/ci/catalog/resources/unpublish.rb30
-rw-r--r--app/graphql/mutations/ci/job/cancel.rb2
-rw-r--r--app/graphql/mutations/ci/pipeline/cancel.rb2
-rw-r--r--app/graphql/mutations/commits/create.rb2
-rw-r--r--app/graphql/mutations/container_registry/protection/rule/create.rb63
-rw-r--r--app/graphql/mutations/merge_requests/accept.rb4
-rw-r--r--app/graphql/mutations/namespace/package_settings/update.rb10
-rw-r--r--app/graphql/mutations/organizations/create.rb35
-rw-r--r--app/graphql/mutations/packages/protection/rule/delete.rb40
-rw-r--r--app/graphql/mutations/saved_replies/base.rb4
-rw-r--r--app/graphql/mutations/saved_replies/create.rb2
-rw-r--r--app/graphql/mutations/saved_replies/destroy.rb2
-rw-r--r--app/graphql/mutations/saved_replies/update.rb2
-rw-r--r--app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb2
-rw-r--r--app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb2
-rw-r--r--app/graphql/resolvers/ci/catalog/resource_resolver.rb48
-rw-r--r--app/graphql/resolvers/ci/catalog/resources_resolver.rb54
-rw-r--r--app/graphql/resolvers/ci/catalog/versions_resolver.rb24
-rw-r--r--app/graphql/resolvers/ci/runners_resolver.rb14
-rw-r--r--app/graphql/resolvers/concerns/caching_array_resolver.rb2
-rw-r--r--app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb7
-rw-r--r--app/graphql/resolvers/container_repository_tags_resolver.rb56
-rw-r--r--app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb16
-rw-r--r--app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb16
-rw-r--r--app/graphql/resolvers/group_issues_resolver.rb2
-rw-r--r--app/graphql/resolvers/issues/base_parent_resolver.rb9
-rw-r--r--app/graphql/resolvers/issues_resolver.rb7
-rw-r--r--app/graphql/resolvers/namespaces/work_items_resolver.rb2
-rw-r--r--app/graphql/resolvers/packages_base_resolver.rb6
-rw-r--r--app/graphql/resolvers/project_issues_resolver.rb2
-rw-r--r--app/graphql/resolvers/project_members_resolver.rb4
-rw-r--r--app/graphql/resolvers/project_milestones_resolver.rb1
-rw-r--r--app/graphql/resolvers/projects/snippets_resolver.rb4
-rw-r--r--app/graphql/resolvers/projects_resolver.rb42
-rw-r--r--app/graphql/resolvers/saved_reply_resolver.rb2
-rw-r--r--app/graphql/resolvers/snippets_resolver.rb4
-rw-r--r--app/graphql/resolvers/users/frecent_groups_resolver.rb23
-rw-r--r--app/graphql/resolvers/users/frecent_projects_resolver.rb21
-rw-r--r--app/graphql/resolvers/users/organizations_resolver.rb18
-rw-r--r--app/graphql/resolvers/users/snippets_resolver.rb4
-rw-r--r--app/graphql/resolvers/work_items/linked_items_resolver.rb26
-rw-r--r--app/graphql/types/abuse_report_type.rb9
-rw-r--r--app/graphql/types/analytics/cycle_analytics/value_stream_type.rb32
-rw-r--r--app/graphql/types/base_argument.rb22
-rw-r--r--app/graphql/types/base_input_object.rb2
-rw-r--r--app/graphql/types/ci/catalog/resource_scope_enum.rb14
-rw-r--r--app/graphql/types/ci/catalog/resource_sort_enum.rb19
-rw-r--r--app/graphql/types/ci/catalog/resource_type.rb120
-rw-r--r--app/graphql/types/ci/pipeline_status_enum.rb1
-rw-r--r--app/graphql/types/container_registry/protection/rule_access_level_enum.rb17
-rw-r--r--app/graphql/types/container_registry/protection/rule_type.rb41
-rw-r--r--app/graphql/types/container_repository_details_type.rb3
-rw-r--r--app/graphql/types/data_transfer/project_data_transfer_type.rb1
-rw-r--r--app/graphql/types/group_member_type.rb4
-rw-r--r--app/graphql/types/issuable_state_enum.rb5
-rw-r--r--app/graphql/types/merge_request_review_state_enum.rb8
-rw-r--r--app/graphql/types/merge_request_type.rb3
-rw-r--r--app/graphql/types/mutation_type.rb33
-rw-r--r--app/graphql/types/namespace/package_settings_type.rb15
-rw-r--r--app/graphql/types/notes/noteable_interface.rb2
-rw-r--r--app/graphql/types/organizations/organization_type.rb4
-rw-r--r--app/graphql/types/organizations/organization_user_badge_type.rb22
-rw-r--r--app/graphql/types/organizations/organization_user_type.rb4
-rw-r--r--app/graphql/types/packages/package_base_type.rb10
-rw-r--r--app/graphql/types/packages/protection/rule_type.rb5
-rw-r--r--app/graphql/types/packages/pypi/metadatum_type.rb9
-rw-r--r--app/graphql/types/permission_types/abuse_report.rb11
-rw-r--r--app/graphql/types/permission_types/base_permission_type.rb2
-rw-r--r--app/graphql/types/permission_types/ci/job.rb1
-rw-r--r--app/graphql/types/permission_types/ci/pipeline.rb1
-rw-r--r--app/graphql/types/permission_types/package.rb12
-rw-r--r--app/graphql/types/project_type.rb6
-rw-r--r--app/graphql/types/projects/detailed_import_status_type.rb39
-rw-r--r--app/graphql/types/query_type.rb22
-rw-r--r--app/graphql/types/security/codequality_reports_comparer/degradation_type.rb3
-rw-r--r--app/graphql/types/security/codequality_reports_comparer/report_generation_status_enum.rb16
-rw-r--r--app/graphql/types/security/codequality_reports_comparer/report_type.rb2
-rw-r--r--app/graphql/types/security/codequality_reports_comparer/status_enum.rb8
-rw-r--r--app/graphql/types/security/codequality_reports_comparer/summary_type.rb2
-rw-r--r--app/graphql/types/security/codequality_reports_comparer_type.rb7
-rw-r--r--app/graphql/types/user_interface.rb16
-rw-r--r--app/graphql/types/work_items/linked_item_type.rb22
-rw-r--r--app/graphql/types/work_items/widgets/linked_items_type.rb1
85 files changed, 1068 insertions, 166 deletions
diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb
index 994668b5f8f..8419f7d5eae 100644
--- a/app/graphql/mutations/base_mutation.rb
+++ b/app/graphql/mutations/base_mutation.rb
@@ -30,12 +30,6 @@ module Mutations
def ready?(**args)
raise_resource_not_available_error!(ERROR_MESSAGE) if read_only?
- missing_args = self.class.arguments.values
- .reject { |arg| arg.accepts?(args.fetch(arg.keyword, :not_given)) }
- .map(&:graphql_name)
-
- raise ArgumentError, "Arguments must be provided: #{missing_args.join(", ")}" if missing_args.any?
-
true
end
diff --git a/app/graphql/mutations/ci/catalog/resources/create.rb b/app/graphql/mutations/ci/catalog/resources/create.rb
new file mode 100644
index 00000000000..7f934e101c8
--- /dev/null
+++ b/app/graphql/mutations/ci/catalog/resources/create.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module Catalog
+ module Resources
+ class Create < BaseMutation
+ graphql_name 'CatalogResourcesCreate'
+
+ argument :project_path, GraphQL::Types::ID,
+ required: true,
+ description: 'Project to convert to a catalog resource.'
+
+ authorize :add_catalog_resource
+
+ def resolve(project_path:)
+ project = authorized_find!(project_path: project_path)
+ response = ::Ci::Catalog::Resources::CreateService.new(project, current_user).execute
+
+ errors = response.success? ? [] : [response.message]
+
+ {
+ errors: errors
+ }
+ end
+
+ private
+
+ def find_object(project_path:)
+ Project.find_by_full_path(project_path)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/catalog/resources/unpublish.rb b/app/graphql/mutations/ci/catalog/resources/unpublish.rb
new file mode 100644
index 00000000000..e45e9646147
--- /dev/null
+++ b/app/graphql/mutations/ci/catalog/resources/unpublish.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module Catalog
+ module Resources
+ class Unpublish < BaseMutation
+ graphql_name 'CatalogResourceUnpublish'
+
+ authorize :add_catalog_resource
+
+ argument :id, ::Types::GlobalIDType[::Ci::Catalog::Resource],
+ required: true,
+ description: 'Global ID of the catalog resource to unpublish.'
+
+ def resolve(id:)
+ catalog_resource = ::Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(id))
+ authorize!(catalog_resource&.project)
+
+ catalog_resource.unpublish!
+
+ {
+ errors: []
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/job/cancel.rb b/app/graphql/mutations/ci/job/cancel.rb
index dc9f4d19779..44a7772019d 100644
--- a/app/graphql/mutations/ci/job/cancel.rb
+++ b/app/graphql/mutations/ci/job/cancel.rb
@@ -11,7 +11,7 @@ module Mutations
null: true,
description: 'Job after the mutation.'
- authorize :update_build
+ authorize :cancel_build
def resolve(id:)
job = authorized_find!(id: id)
diff --git a/app/graphql/mutations/ci/pipeline/cancel.rb b/app/graphql/mutations/ci/pipeline/cancel.rb
index 810f458fd75..1014462d0b1 100644
--- a/app/graphql/mutations/ci/pipeline/cancel.rb
+++ b/app/graphql/mutations/ci/pipeline/cancel.rb
@@ -6,7 +6,7 @@ module Mutations
class Cancel < Base
graphql_name 'PipelineCancel'
- authorize :update_pipeline
+ authorize :cancel_pipeline
def resolve(id:)
pipeline = authorized_find!(id: id)
diff --git a/app/graphql/mutations/commits/create.rb b/app/graphql/mutations/commits/create.rb
index 02e1e4c78bf..cbe2c49e950 100644
--- a/app/graphql/mutations/commits/create.rb
+++ b/app/graphql/mutations/commits/create.rb
@@ -64,7 +64,7 @@ module Mutations
result = ::Files::MultiService.new(project, current_user, attributes).execute
{
- content: actions.pluck(:content), # rubocop:disable CodeReuse/ActiveRecord because actions is an Array, not a Relation
+ content: actions.pluck(:content), # rubocop:disable CodeReuse/ActiveRecord -- Array#pluck
commit: (project.repository.commit(result[:result]) if result[:status] == :success),
commit_pipeline_path: UrlHelpers.new.graphql_etag_pipeline_sha_path(result[:result]),
errors: Array.wrap(result[:message])
diff --git a/app/graphql/mutations/container_registry/protection/rule/create.rb b/app/graphql/mutations/container_registry/protection/rule/create.rb
new file mode 100644
index 00000000000..cf8416480a2
--- /dev/null
+++ b/app/graphql/mutations/container_registry/protection/rule/create.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ContainerRegistry
+ module Protection
+ module Rule
+ class Create < ::Mutations::BaseMutation
+ graphql_name 'CreateContainerRegistryProtectionRule'
+ description 'Creates a protection rule to restrict access to a project\'s container registry. ' \
+ 'Available only when feature flag `container_registry_protected_containers` is enabled.'
+
+ include FindsProject
+
+ authorize :admin_container_image
+
+ argument :project_path,
+ GraphQL::Types::ID,
+ required: true,
+ description: 'Full path of the project where a protection rule is located.'
+
+ argument :container_path_pattern,
+ GraphQL::Types::String,
+ required: true,
+ description:
+ 'ContainerRegistryname protected by the protection rule. For example `@my-scope/my-container-*`. ' \
+ 'Wildcard character `*` allowed.'
+
+ argument :push_protected_up_to_access_level,
+ Types::ContainerRegistry::Protection::RuleAccessLevelEnum,
+ required: true,
+ description:
+ 'Max GitLab access level to prevent from pushing container images to the container registry. ' \
+ 'For example `DEVELOPER`, `MAINTAINER`, `OWNER`.'
+
+ argument :delete_protected_up_to_access_level,
+ Types::ContainerRegistry::Protection::RuleAccessLevelEnum,
+ required: true,
+ description:
+ 'Max GitLab access level to prevent from deleting container images in the container registry. ' \
+ 'For example `DEVELOPER`, `MAINTAINER`, `OWNER`.'
+
+ field :container_registry_protection_rule,
+ Types::ContainerRegistry::Protection::RuleType,
+ null: true,
+ description: 'Container registry protection rule after mutation.'
+
+ def resolve(project_path:, **kwargs)
+ project = authorized_find!(project_path)
+
+ if Feature.disabled?(:container_registry_protected_containers, project)
+ raise_resource_not_available_error!("'container_registry_protected_containers' feature flag is disabled")
+ end
+
+ response = ::ContainerRegistry::Protection::CreateRuleService.new(project, current_user, kwargs).execute
+
+ { container_registry_protection_rule: response.payload[:container_registry_protection_rule],
+ errors: response.errors }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/merge_requests/accept.rb b/app/graphql/mutations/merge_requests/accept.rb
index 220ebea22c7..604fdd49f45 100644
--- a/app/graphql/mutations/merge_requests/accept.rb
+++ b/app/graphql/mutations/merge_requests/accept.rb
@@ -9,6 +9,10 @@ module Mutations
Accepts a merge request.
When accepted, the source branch will be scheduled to merge into the target branch, either
immediately if possible, or using one of the automatic merge strategies.
+
+ [In GitLab 16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/421510), the merging happens asynchronously.
+ This results in `mergeRequest` and `state` not updating after a mutation request,
+ because the merging may not have happened yet.
DESC
NOT_MERGEABLE = 'This branch cannot be merged'
diff --git a/app/graphql/mutations/namespace/package_settings/update.rb b/app/graphql/mutations/namespace/package_settings/update.rb
index 4e71bed52c6..97c16ee79fe 100644
--- a/app/graphql/mutations/namespace/package_settings/update.rb
+++ b/app/graphql/mutations/namespace/package_settings/update.rb
@@ -8,8 +8,6 @@ module Mutations
include Mutations::ResolvesNamespace
- NUGET_DUPLICATES_FF_ERROR = '`nuget_duplicates_option` feature flag is disabled.'
-
description <<~DESC
These settings can be adjusted by the group Owner or Maintainer.
[Issue 370471](https://gitlab.com/gitlab-org/gitlab/-/issues/370471) proposes limiting
@@ -91,10 +89,6 @@ module Mutations
def resolve(namespace_path:, **args)
namespace = authorized_find!(namespace_path: namespace_path)
- if nuget_duplicate_settings_present?(args) && Feature.disabled?(:nuget_duplicates_option, namespace)
- raise_resource_not_available_error! NUGET_DUPLICATES_FF_ERROR
- end
-
result = ::Namespaces::PackageSettings::UpdateService
.new(container: namespace, current_user: current_user, params: args)
.execute
@@ -110,10 +104,6 @@ module Mutations
def find_object(namespace_path:)
resolve_namespace(full_path: namespace_path)
end
-
- def nuget_duplicate_settings_present?(args)
- args.key?(:nuget_duplicates_allowed) || args.key?(:nuget_duplicate_exception_regex)
- end
end
end
end
diff --git a/app/graphql/mutations/organizations/create.rb b/app/graphql/mutations/organizations/create.rb
new file mode 100644
index 00000000000..0d1b204a4c1
--- /dev/null
+++ b/app/graphql/mutations/organizations/create.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Organizations
+ class Create < BaseMutation
+ graphql_name 'OrganizationCreate'
+
+ authorize :create_organization
+
+ field :organization,
+ ::Types::Organizations::OrganizationType,
+ null: true,
+ description: 'Organization created.'
+
+ argument :name, GraphQL::Types::String,
+ required: true,
+ description: 'Name for the organization.'
+
+ argument :path, GraphQL::Types::String,
+ required: true,
+ description: 'Path for the organization.'
+
+ def resolve(args)
+ authorize!(:global)
+
+ result = ::Organizations::CreateService.new(
+ current_user: current_user,
+ params: args
+ ).execute
+
+ { organization: result.payload, errors: result.errors }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/packages/protection/rule/delete.rb b/app/graphql/mutations/packages/protection/rule/delete.rb
new file mode 100644
index 00000000000..bd0159d3c23
--- /dev/null
+++ b/app/graphql/mutations/packages/protection/rule/delete.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Packages
+ module Protection
+ module Rule
+ class Delete < ::Mutations::BaseMutation
+ graphql_name 'DeletePackagesProtectionRule'
+ description 'Deletes a protection rule for packages. ' \
+ 'Available only when feature flag `packages_protected_packages` is enabled.'
+
+ authorize :admin_package
+
+ argument :id,
+ ::Types::GlobalIDType[::Packages::Protection::Rule],
+ required: true,
+ description: 'Global ID of the package protection rule to delete.'
+
+ field :package_protection_rule,
+ Types::Packages::Protection::RuleType,
+ null: true,
+ description: 'Packages protection rule that was deleted successfully.'
+
+ def resolve(id:, **_kwargs)
+ if Feature.disabled?(:packages_protected_packages)
+ raise_resource_not_available_error!("'packages_protected_packages' feature flag is disabled")
+ end
+
+ package_protection_rule = authorized_find!(id: id)
+
+ response = ::Packages::Protection::DeleteRuleService.new(package_protection_rule,
+ current_user: current_user).execute
+
+ { package_protection_rule: response.payload[:package_protection_rule], errors: response.errors }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/saved_replies/base.rb b/app/graphql/mutations/saved_replies/base.rb
index 4923fcb7851..79761645eb7 100644
--- a/app/graphql/mutations/saved_replies/base.rb
+++ b/app/graphql/mutations/saved_replies/base.rb
@@ -23,10 +23,6 @@ module Mutations
end
end
- def feature_enabled?
- Feature.enabled?(:saved_replies, current_user)
- end
-
def find_object(id)
GitlabSchema.find_by_gid(id)
end
diff --git a/app/graphql/mutations/saved_replies/create.rb b/app/graphql/mutations/saved_replies/create.rb
index d97461a1c2a..25c02b79cb8 100644
--- a/app/graphql/mutations/saved_replies/create.rb
+++ b/app/graphql/mutations/saved_replies/create.rb
@@ -16,8 +16,6 @@ module Mutations
description: copy_field_description(Types::SavedReplyType, :content)
def resolve(name:, content:)
- raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled?
-
result = ::Users::SavedReplies::CreateService.new(current_user: current_user, name: name, content: content).execute
present_result(result)
end
diff --git a/app/graphql/mutations/saved_replies/destroy.rb b/app/graphql/mutations/saved_replies/destroy.rb
index 7cd0f21ad45..655ed9cb798 100644
--- a/app/graphql/mutations/saved_replies/destroy.rb
+++ b/app/graphql/mutations/saved_replies/destroy.rb
@@ -12,8 +12,6 @@ module Mutations
description: copy_field_description(Types::SavedReplyType, :id)
def resolve(id:)
- raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled?
-
saved_reply = authorized_find!(id)
result = ::Users::SavedReplies::DestroyService.new(saved_reply: saved_reply).execute
present_result(result)
diff --git a/app/graphql/mutations/saved_replies/update.rb b/app/graphql/mutations/saved_replies/update.rb
index d9368de7547..f5dc81614d2 100644
--- a/app/graphql/mutations/saved_replies/update.rb
+++ b/app/graphql/mutations/saved_replies/update.rb
@@ -20,8 +20,6 @@ module Mutations
description: copy_field_description(Types::SavedReplyType, :content)
def resolve(id:, name:, content:)
- raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled?
-
saved_reply = authorized_find!(id)
result = ::Users::SavedReplies::UpdateService.new(saved_reply: saved_reply, name: name, content: content).execute
present_result(result)
diff --git a/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb
index 51a1afdd5ab..2d722b02bf1 100644
--- a/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb
+++ b/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from Resolvers::Analytics::CycleAnalytics::BaseCountResolver)
+# rubocop:disable Graphql/ResolverType -- inherited from Resolvers::Analytics::CycleAnalytics::BaseCountResolver
module Resolvers
module Analytics
module CycleAnalytics
diff --git a/app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb
index fd20800ee16..32b884df84f 100644
--- a/app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb
+++ b/app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from Resolvers::Analytics::CycleAnalytics::BaseIssueResolver)
+# rubocop:disable Graphql/ResolverType -- inherited from Resolvers::Analytics::CycleAnalytics::BaseIssueResolver
module Resolvers
module Analytics
module CycleAnalytics
diff --git a/app/graphql/resolvers/ci/catalog/resource_resolver.rb b/app/graphql/resolvers/ci/catalog/resource_resolver.rb
new file mode 100644
index 00000000000..4b722bd3ec7
--- /dev/null
+++ b/app/graphql/resolvers/ci/catalog/resource_resolver.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ module Catalog
+ class ResourceResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ authorize :read_code
+
+ type ::Types::Ci::Catalog::ResourceType, null: true
+
+ argument :id, ::Types::GlobalIDType[::Ci::Catalog::Resource],
+ required: false,
+ description: 'CI/CD Catalog resource global ID.'
+
+ argument :full_path, GraphQL::Types::ID,
+ required: false,
+ description: 'CI/CD Catalog resource full path.'
+
+ def ready?(**args)
+ unless args[:id].present? ^ args[:full_path].present?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ "Exactly one of 'id' or 'full_path' arguments is required."
+ end
+
+ super
+ end
+
+ def resolve(id: nil, full_path: nil)
+ if full_path.present?
+ project = Project.find_by_full_path(full_path)
+ authorize!(project)
+
+ raise_resource_not_available_error! unless project.catalog_resource
+
+ project.catalog_resource
+ else
+ catalog_resource = ::Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(id))
+ authorize!(catalog_resource&.project)
+
+ catalog_resource
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/catalog/resources_resolver.rb b/app/graphql/resolvers/ci/catalog/resources_resolver.rb
new file mode 100644
index 00000000000..c6904dcd7f6
--- /dev/null
+++ b/app/graphql/resolvers/ci/catalog/resources_resolver.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ module Catalog
+ class ResourcesResolver < BaseResolver
+ include LooksAhead
+
+ type ::Types::Ci::Catalog::ResourceType.connection_type, null: true
+
+ argument :scope, ::Types::Ci::Catalog::ResourceScopeEnum,
+ required: false,
+ default_value: :all,
+ description: 'Scope of the returned catalog resources.'
+
+ argument :search, GraphQL::Types::String,
+ required: false,
+ description: 'Search term to filter the catalog resources by name or description.'
+
+ argument :sort, ::Types::Ci::Catalog::ResourceSortEnum,
+ required: false,
+ description: 'Sort catalog resources by given criteria.'
+
+ # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/429636
+ argument :project_path, GraphQL::Types::ID,
+ required: false,
+ description: 'Project with the namespace catalog.'
+
+ def resolve_with_lookahead(scope:, project_path: nil, search: nil, sort: nil)
+ if project_path.present?
+ project = Project.find_by_full_path(project_path)
+
+ apply_lookahead(
+ ::Ci::Catalog::Listing
+ .new(context[:current_user])
+ .resources(namespace: project.root_namespace, sort: sort, search: search)
+ )
+ elsif scope == :all
+ apply_lookahead(::Ci::Catalog::Listing.new(context[:current_user]).resources(sort: sort, search: search))
+ end
+ end
+
+ private
+
+ def preloads
+ {
+ web_path: { project: { namespace: :route } },
+ readme_html: { project: :route }
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/catalog/versions_resolver.rb b/app/graphql/resolvers/ci/catalog/versions_resolver.rb
new file mode 100644
index 00000000000..046adeb7a67
--- /dev/null
+++ b/app/graphql/resolvers/ci/catalog/versions_resolver.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ module Catalog
+ class VersionsResolver < ::Resolvers::ReleasesResolver
+ type Types::ReleaseType.connection_type, null: true
+
+ # This allows a maximum of 1 call to the field that uses this resolver. If the
+ # field is evaluated on more than one node, it causes performance degradation.
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+
+ private
+
+ def get_project
+ object.respond_to?(:project) ? object.project : object
+ end
+
+ # Override the aliased method in ReleasesResolver
+ alias_method :project, :get_project
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb
index 3289f1d0056..9121c413b1f 100644
--- a/app/graphql/resolvers/ci/runners_resolver.rb
+++ b/app/graphql/resolvers/ci/runners_resolver.rb
@@ -41,6 +41,17 @@ module Resolvers
required: false,
description: 'Filter by upgrade status.'
+ argument :creator_id, ::Types::GlobalIDType[::User].as('UserID'),
+ required: false,
+ description: 'Filter runners by creator ID.'
+
+ argument :version_prefix, GraphQL::Types::String,
+ required: false,
+ description: "Filter runners by version. Runners that contain runner managers with the version at " \
+ "the start of the search term are returned. For example, the search term '14.' returns " \
+ "runner managers with versions '14.11.1' and '14.2.3'.",
+ alpha: { milestone: '16.6' }
+
def resolve_with_lookahead(**args)
apply_lookahead(
::Ci::RunnersFinder
@@ -68,6 +79,9 @@ module Resolvers
upgrade_status: params[:upgrade_status],
search: params[:search],
sort: params[:sort]&.to_s,
+ creator_id:
+ params[:creator_id] ? ::GitlabSchema.parse_gid(params[:creator_id], expected_type: ::User).model_id : nil,
+ version_prefix: params[:version_prefix],
preload: false # we'll handle preloading ourselves
}.compact
.merge(parent_param)
diff --git a/app/graphql/resolvers/concerns/caching_array_resolver.rb b/app/graphql/resolvers/concerns/caching_array_resolver.rb
index 15bf9a90e46..f678e02533d 100644
--- a/app/graphql/resolvers/concerns/caching_array_resolver.rb
+++ b/app/graphql/resolvers/concerns/caching_array_resolver.rb
@@ -132,7 +132,7 @@ module CachingArrayResolver
model_class.arel_table[Arel.star]
end
- # rubocop: disable Graphql/Descriptions (false positive!)
+ # rubocop: disable Graphql/Descriptions -- false positive
def query_limit
field&.max_page_size.presence || context.schema.default_max_page_size
end
diff --git a/app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb b/app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb
index ecb105a64d0..1982b458143 100644
--- a/app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb
+++ b/app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb
@@ -17,7 +17,12 @@ module WorkItems
argument :state,
Types::IssuableStateEnum,
required: false,
- description: 'Current state of the work item.'
+ description: 'Current state of the work item.',
+ prepare: ->(state, _ctx) {
+ return state unless state == 'locked'
+
+ raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE
+ }
argument :types,
[Types::IssueTypeEnum],
as: :issue_types,
diff --git a/app/graphql/resolvers/container_repository_tags_resolver.rb b/app/graphql/resolvers/container_repository_tags_resolver.rb
index 55a83dd49da..bc5006ae06c 100644
--- a/app/graphql/resolvers/container_repository_tags_resolver.rb
+++ b/app/graphql/resolvers/container_repository_tags_resolver.rb
@@ -14,21 +14,61 @@ module Resolvers
required: false,
default_value: nil
+ alias_method :container_repository, :object
+
def resolve(sort:, **filters)
- result = tags
+ if container_repository.migrated? && Feature.enabled?(:use_repository_list_tags_on_graphql, container_repository.project)
+ page_size = [filters[:first], filters[:last]].map(&:to_i).max
+
+ result = container_repository.tags_page(
+ before: filters[:before],
+ last: filters[:after],
+ sort: map_sort_field(sort),
+ name: filters[:name],
+ page_size: page_size
+ )
- if filters[:name]
- result = tags.filter do |tag|
- tag.name.include?(filters[:name])
+ Gitlab::Graphql::ExternallyPaginatedArray.new(
+ parse_pagination_cursor(result, :previous),
+ parse_pagination_cursor(result, :next),
+ *result[:tags]
+ )
+ else
+ result = tags
+
+ if filters[:name]
+ result = tags.filter do |tag|
+ tag.name.include?(filters[:name])
+ end
end
- end
- result = sort_tags(result, sort) if sort
- result
+ result = sort_tags(result, sort) if sort
+ result
+ end
end
private
+ def parse_pagination_cursor(result, direction)
+ pagination_uri = result.dig(:pagination, direction, :uri)
+
+ return unless pagination_uri
+
+ query_params = CGI.parse(pagination_uri.query)
+ key = direction == :previous ? 'before' : 'last'
+
+ query_params[key]&.first
+ end
+
+ def map_sort_field(sort)
+ return unless sort
+
+ sort_field, direction = sort.to_s.split('_')
+ return sort_field if direction == 'asc'
+
+ "-#{sort_field}"
+ end
+
def sort_tags(to_be_sorted, sort)
raise StandardError unless Types::ContainerRepositoryTagsSortEnum.enum.include?(sort)
@@ -41,7 +81,7 @@ module Resolvers
end
def tags
- object.tags
+ container_repository.tags
rescue Faraday::Error
raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, "Can't connect to the Container Registry. If this error persists, please review the troubleshooting documentation."
end
diff --git a/app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb b/app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb
index 83bb144017c..133b86623f1 100644
--- a/app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb
+++ b/app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb
@@ -16,16 +16,12 @@ module Resolvers
def resolve(**args)
return { egress_nodes: [] } unless Feature.enabled?(:data_transfer_monitoring, group)
- results = if Feature.enabled?(:data_transfer_monitoring_mock_data, group)
- ::DataTransfer::MockedTransferFinder.new.execute
- else
- ::DataTransfer::GroupDataTransferFinder.new(
- group: group,
- from: args[:from],
- to: args[:to],
- user: current_user
- ).execute.map(&:attributes)
- end
+ results = ::DataTransfer::GroupDataTransferFinder.new(
+ group: group,
+ from: args[:from],
+ to: args[:to],
+ user: current_user
+ ).execute.map(&:attributes)
{ egress_nodes: results.to_a }
end
diff --git a/app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb b/app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb
index c3296f7d4c3..d711f837251 100644
--- a/app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb
+++ b/app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb
@@ -16,16 +16,12 @@ module Resolvers
def resolve(**args)
return { egress_nodes: [] } unless Feature.enabled?(:data_transfer_monitoring, project.group)
- results = if Feature.enabled?(:data_transfer_monitoring_mock_data, project.group)
- ::DataTransfer::MockedTransferFinder.new.execute
- else
- ::DataTransfer::ProjectDataTransferFinder.new(
- project: project,
- from: args[:from],
- to: args[:to],
- user: current_user
- ).execute
- end
+ results = ::DataTransfer::ProjectDataTransferFinder.new(
+ project: project,
+ from: args[:from],
+ to: args[:to],
+ user: current_user
+ ).execute
{ egress_nodes: results }
end
diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb
index 5e0fb27bafa..5a6a3d678b9 100644
--- a/app/graphql/resolvers/group_issues_resolver.rb
+++ b/app/graphql/resolvers/group_issues_resolver.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from Issues::BaseParentResolver)
+# rubocop:disable Graphql/ResolverType -- inherited from Issues::BaseParentResolver
module Resolvers
class GroupIssuesResolver < Issues::BaseParentResolver
def self.issuable_collection_name
diff --git a/app/graphql/resolvers/issues/base_parent_resolver.rb b/app/graphql/resolvers/issues/base_parent_resolver.rb
index 6308e56f049..78ef4132baf 100644
--- a/app/graphql/resolvers/issues/base_parent_resolver.rb
+++ b/app/graphql/resolvers/issues/base_parent_resolver.rb
@@ -7,8 +7,13 @@ module Resolvers
include ::Issues::SortArguments
argument :state, Types::IssuableStateEnum,
- required: false,
- description: 'Current state of this issue.'
+ required: false,
+ description: 'Current state of this issue.',
+ prepare: ->(state, _ctx) {
+ return state unless state == 'locked'
+
+ raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE
+ }
# see app/graphql/types/issue_connection.rb
type 'Types::IssueConnection', null: true
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index 34f14eee0e5..bc0e7334303 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -14,7 +14,12 @@ module Resolvers
description: 'Whether to include issues from archived projects. Defaults to `false`.'
argument :state, Types::IssuableStateEnum,
required: false,
- description: 'Current state of this issue.'
+ description: 'Current state of this issue.',
+ prepare: ->(state, _ctx) {
+ return state unless state == 'locked'
+
+ raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE
+ }
# see app/graphql/types/issue_connection.rb
type 'Types::IssueConnection', null: true
diff --git a/app/graphql/resolvers/namespaces/work_items_resolver.rb b/app/graphql/resolvers/namespaces/work_items_resolver.rb
index 6985a7a898a..671788668b1 100644
--- a/app/graphql/resolvers/namespaces/work_items_resolver.rb
+++ b/app/graphql/resolvers/namespaces/work_items_resolver.rb
@@ -2,7 +2,7 @@
module Resolvers
module Namespaces
- # rubocop:disable Graphql/ResolverType (inherited from Resolvers::WorkItemsResolver)
+ # rubocop:disable Graphql/ResolverType -- inherited from Resolvers::WorkItemsResolver
class WorkItemsResolver < ::Resolvers::WorkItemsResolver
def ready?(**args)
return false if Feature.disabled?(:namespace_level_work_items, resource_parent)
diff --git a/app/graphql/resolvers/packages_base_resolver.rb b/app/graphql/resolvers/packages_base_resolver.rb
index 7d153d16910..7e5d89a7897 100644
--- a/app/graphql/resolvers/packages_base_resolver.rb
+++ b/app/graphql/resolvers/packages_base_resolver.rb
@@ -19,6 +19,12 @@ module Resolvers
required: false,
default_value: nil
+ argument :package_version, GraphQL::Types::String,
+ description: 'Filter a package by version. If used in combination with `include_versionless`,
+ then no versionless packages are returned.',
+ required: false,
+ default_value: nil
+
argument :status, Types::Packages::PackageStatusEnum,
description: 'Filter a package by status.',
required: false,
diff --git a/app/graphql/resolvers/project_issues_resolver.rb b/app/graphql/resolvers/project_issues_resolver.rb
index f869d8f11c6..2bc610e8266 100644
--- a/app/graphql/resolvers/project_issues_resolver.rb
+++ b/app/graphql/resolvers/project_issues_resolver.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from Issues::BaseParentResolver)
+# rubocop:disable Graphql/ResolverType -- inherited from Issues::BaseParentResolver
module Resolvers
class ProjectIssuesResolver < Issues::BaseParentResolver
accept_release_tag
diff --git a/app/graphql/resolvers/project_members_resolver.rb b/app/graphql/resolvers/project_members_resolver.rb
index e889b47c000..a27183438cd 100644
--- a/app/graphql/resolvers/project_members_resolver.rb
+++ b/app/graphql/resolvers/project_members_resolver.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from MembersResolver)
+
+# rubocop:disable Graphql/ResolverType -- inherited from MembersResolver
module Resolvers
class ProjectMembersResolver < MembersResolver
@@ -17,3 +18,4 @@ module Resolvers
end
end
end
+# rubocop:enable Graphql/ResolverType
diff --git a/app/graphql/resolvers/project_milestones_resolver.rb b/app/graphql/resolvers/project_milestones_resolver.rb
index 567a55aa09b..cb4e9a5cdf7 100644
--- a/app/graphql/resolvers/project_milestones_resolver.rb
+++ b/app/graphql/resolvers/project_milestones_resolver.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from MilestonesResolver)
module Resolvers
class ProjectMilestonesResolver < MilestonesResolver
diff --git a/app/graphql/resolvers/projects/snippets_resolver.rb b/app/graphql/resolvers/projects/snippets_resolver.rb
index 448918be2f5..9ab9db21e89 100644
--- a/app/graphql/resolvers/projects/snippets_resolver.rb
+++ b/app/graphql/resolvers/projects/snippets_resolver.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from ResolvesSnippets)
+
+# rubocop:disable Graphql/ResolverType -- inherited from ResolvesSnippets
module Resolvers
module Projects
@@ -27,3 +28,4 @@ module Resolvers
end
end
end
+# rubocop:enable Graphql/ResolverType
diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb
index 8dd409a8173..450caa9aff6 100644
--- a/app/graphql/resolvers/projects_resolver.rb
+++ b/app/graphql/resolvers/projects_resolver.rb
@@ -3,6 +3,7 @@
module Resolvers
class ProjectsResolver < BaseResolver
include ProjectSearchArguments
+ include LooksAhead
type Types::ProjectType.connection_type, null: true
@@ -10,6 +11,10 @@ module Resolvers
required: false,
description: 'Filter projects by IDs.'
+ argument :full_paths, [GraphQL::Types::String],
+ required: false,
+ description: 'Filter projects by full paths. You cannot provide more than 50 full paths.'
+
argument :sort, GraphQL::Types::String,
required: false,
description: "Sort order of results. Format: `<field_name>_<sort_direction>`, " \
@@ -23,19 +28,48 @@ module Resolvers
required: false,
description: "Return only projects with merge requests enabled."
- def resolve(**args)
- ProjectsFinder
+ def resolve_with_lookahead(**args)
+ validate_args!(args)
+
+ projects = ProjectsFinder
.new(current_user: current_user, params: finder_params(args), project_ids_relation: parse_gids(args[:ids]))
.execute
+
+ apply_lookahead(projects)
end
private
+ def validate_args!(args)
+ return unless args[:full_paths].present? && args[:full_paths].length > 50
+
+ raise Gitlab::Graphql::Errors::ArgumentError, 'You cannot provide more than 50 full_paths'
+ end
+
+ def unconditional_includes
+ [:creator, :group, :invited_groups, :project_setting]
+ end
+
+ def preloads
+ {
+ full_path: [:route],
+ topics: [:topics],
+ import_status: [:import_state],
+ service_desk_address: [:project_feature, :service_desk_setting],
+ jira_import_status: [:jira_imports],
+ container_repositories: [:container_repositories],
+ container_repositories_count: [:container_repositories],
+ web_url: { namespace: [:route] },
+ is_catalog_resource: [:catalog_resource]
+ }
+ end
+
def finder_params(args)
{
**project_finder_params(args),
with_issues_enabled: args[:with_issues_enabled],
- with_merge_requests_enabled: args[:with_merge_requests_enabled]
+ with_merge_requests_enabled: args[:with_merge_requests_enabled],
+ full_paths: args[:full_paths]
}
end
@@ -44,3 +78,5 @@ module Resolvers
end
end
end
+
+Resolvers::ProjectsResolver.prepend_mod_with('Resolvers::ProjectsResolver')
diff --git a/app/graphql/resolvers/saved_reply_resolver.rb b/app/graphql/resolvers/saved_reply_resolver.rb
index 96bbc139c96..1a5f2c9be78 100644
--- a/app/graphql/resolvers/saved_reply_resolver.rb
+++ b/app/graphql/resolvers/saved_reply_resolver.rb
@@ -11,8 +11,6 @@ module Resolvers
description: 'ID of a saved reply.'
def resolve(id:)
- return unless Feature.enabled?(:saved_replies, current_user)
-
saved_reply = ::Users::SavedReply.find_saved_reply(user_id: current_user.id, id: id.model_id)
return unless saved_reply
diff --git a/app/graphql/resolvers/snippets_resolver.rb b/app/graphql/resolvers/snippets_resolver.rb
index 90f5f2cb534..759cc61a8a7 100644
--- a/app/graphql/resolvers/snippets_resolver.rb
+++ b/app/graphql/resolvers/snippets_resolver.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from ResolvesSnippets)
+
+# rubocop:disable Graphql/ResolverType -- inherited from ResolvesSnippets
module Resolvers
class SnippetsResolver < BaseResolver
@@ -45,3 +46,4 @@ module Resolvers
end
end
end
+# rubocop:enable Graphql/ResolverType
diff --git a/app/graphql/resolvers/users/frecent_groups_resolver.rb b/app/graphql/resolvers/users/frecent_groups_resolver.rb
new file mode 100644
index 00000000000..2fc757e31ab
--- /dev/null
+++ b/app/graphql/resolvers/users/frecent_groups_resolver.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Users
+ class FrecentGroupsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type [Types::GroupType], null: true
+
+ def resolve
+ return unless current_user.present?
+
+ if Feature.disabled?(:frecent_namespaces_suggestions, current_user)
+ raise_resource_not_available_error!("'frecent_namespaces_suggestions' feature flag is disabled")
+ end
+
+ return unless Feature.enabled?(:frecent_namespaces_suggestions, current_user)
+
+ ::Users::GroupVisit.frecent_groups(user_id: current_user.id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/users/frecent_projects_resolver.rb b/app/graphql/resolvers/users/frecent_projects_resolver.rb
new file mode 100644
index 00000000000..397d4ca0cfd
--- /dev/null
+++ b/app/graphql/resolvers/users/frecent_projects_resolver.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Users
+ class FrecentProjectsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type [Types::ProjectType], null: true
+
+ def resolve
+ return unless current_user.present?
+
+ if Feature.disabled?(:frecent_namespaces_suggestions, current_user)
+ raise_resource_not_available_error!("'frecent_namespaces_suggestions' feature flag is disabled")
+ end
+
+ ::Users::ProjectVisit.frecent_projects(user_id: current_user.id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/users/organizations_resolver.rb b/app/graphql/resolvers/users/organizations_resolver.rb
new file mode 100644
index 00000000000..ffc1a141eb6
--- /dev/null
+++ b/app/graphql/resolvers/users/organizations_resolver.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Users
+ class OrganizationsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::Organizations::OrganizationType.connection_type, null: true
+
+ authorize :read_user_organizations
+ authorizes_object!
+
+ def resolve(**args)
+ ::Organizations::UserOrganizationsFinder.new(current_user, object, args).execute
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/users/snippets_resolver.rb b/app/graphql/resolvers/users/snippets_resolver.rb
index 75bba8debab..ea5f6b7b8c9 100644
--- a/app/graphql/resolvers/users/snippets_resolver.rb
+++ b/app/graphql/resolvers/users/snippets_resolver.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Graphql/ResolverType (inherited from ResolvesSnippets)
+
+# rubocop:disable Graphql/ResolverType -- inherited from ResolvesSnippets
module Resolvers
module Users
@@ -27,3 +28,4 @@ module Resolvers
end
end
end
+# rubocop:enable Graphql/ResolverType
diff --git a/app/graphql/resolvers/work_items/linked_items_resolver.rb b/app/graphql/resolvers/work_items/linked_items_resolver.rb
index 108d5d41b62..f2ff1205d3a 100644
--- a/app/graphql/resolvers/work_items/linked_items_resolver.rb
+++ b/app/graphql/resolvers/work_items/linked_items_resolver.rb
@@ -3,6 +3,8 @@
module Resolvers
module WorkItems
class LinkedItemsResolver < BaseResolver
+ prepend ::WorkItems::LookAheadPreloads
+
alias_method :linked_items_widget, :object
argument :filter, Types::WorkItems::RelatedLinkTypeEnum,
@@ -13,30 +15,28 @@ module Resolvers
type Types::WorkItems::LinkedItemType.connection_type, null: true
- def resolve(filter: nil)
- related_work_items(filter).map do |related_work_item|
- {
- link_id: related_work_item.issue_link_id,
- link_type: related_work_item.issue_link_type,
- link_created_at: related_work_item.issue_link_created_at,
- link_updated_at: related_work_item.issue_link_updated_at,
- work_item: related_work_item
- }
- end
+ def resolve_with_lookahead(**args)
+ apply_lookahead(related_work_items(args))
end
private
- def related_work_items(type)
- return [] unless work_item.resource_parent.linked_work_items_feature_flag_enabled?
+ def related_work_items(args)
+ return WorkItem.none unless work_item.resource_parent.linked_work_items_feature_flag_enabled?
- work_item.linked_work_items(current_user, preload: { project: [:project_feature, :group] }, link_type: type)
+ offset_pagination(
+ work_item.linked_work_items(authorize: false, link_type: args[:filter])
+ )
end
def work_item
linked_items_widget.work_item
end
strong_memoize_attr :work_item
+
+ def node_selection(selection = lookahead)
+ super.selection(:work_item)
+ end
end
end
end
diff --git a/app/graphql/types/abuse_report_type.rb b/app/graphql/types/abuse_report_type.rb
index 012e709cdb5..2532530cfa9 100644
--- a/app/graphql/types/abuse_report_type.rb
+++ b/app/graphql/types/abuse_report_type.rb
@@ -3,9 +3,18 @@
module Types
class AbuseReportType < BaseObject
graphql_name 'AbuseReport'
+
+ implements Types::Notes::NoteableInterface
+
description 'An abuse report'
+
authorize :read_abuse_report
+ expose_permissions Types::PermissionTypes::AbuseReport
+
+ field :id, Types::GlobalIDType[::AbuseReport],
+ null: false, description: 'Global ID of the abuse report.'
+
field :labels, ::Types::LabelType.connection_type,
null: true, description: 'Labels of the abuse report.'
end
diff --git a/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb b/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb
new file mode 100644
index 00000000000..16ce9b82718
--- /dev/null
+++ b/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Types
+ module Analytics
+ module CycleAnalytics
+ class ValueStreamType < BaseObject
+ graphql_name 'ValueStream'
+
+ authorize :read_cycle_analytics
+
+ field :id,
+ type: ::Types::GlobalIDType[::Analytics::CycleAnalytics::ValueStream],
+ null: false,
+ description: "ID of the value stream."
+
+ field :name,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Name of the value stream.'
+
+ field :namespace, Types::NamespaceType,
+ null: false,
+ description: 'Namespace the value stream belongs to.'
+
+ field :project, Types::ProjectType,
+ null: true,
+ description: 'Project the value stream belongs to, returns empty if it belongs to a group.',
+ alpha: { milestone: '15.6' }
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb
index cda7fa4a5df..3b4223c3ba1 100644
--- a/app/graphql/types/base_argument.rb
+++ b/app/graphql/types/base_argument.rb
@@ -9,29 +9,7 @@ module Types
def initialize(*args, **kwargs, &block)
@doc_reference = kwargs.delete(:see)
- # our custom addition `nullable` which allows us to declare
- # an argument that must be provided, even if its value is null.
- # When `required: true` then required arguments must not be null.
- @gl_required = !!kwargs[:required]
- @gl_nullable = kwargs[:required] == :nullable
-
- # Only valid if an argument is also required.
- if @gl_nullable
- # Since the framework asserts that "required" means "cannot be null"
- # we have to switch off "required" but still do the check in `ready?` behind the scenes
- kwargs[:required] = false
- end
-
super(*args, **kwargs, &block)
end
-
- def accepts?(value)
- # if the argument is declared as required, it must be included
- return false if @gl_required && value == :not_given
- # if the argument is declared as required, the value can only be null IF it is also nullable.
- return false if @gl_required && value.nil? && !@gl_nullable
-
- true
- end
end
end
diff --git a/app/graphql/types/base_input_object.rb b/app/graphql/types/base_input_object.rb
index 90a29b0cfb8..d14da9ac878 100644
--- a/app/graphql/types/base_input_object.rb
+++ b/app/graphql/types/base_input_object.rb
@@ -3,5 +3,7 @@
module Types
class BaseInputObject < GraphQL::Schema::InputObject
prepend Gitlab::Graphql::CopyFieldDescription
+
+ argument_class ::Types::BaseArgument
end
end
diff --git a/app/graphql/types/ci/catalog/resource_scope_enum.rb b/app/graphql/types/ci/catalog/resource_scope_enum.rb
new file mode 100644
index 00000000000..b825c3a7925
--- /dev/null
+++ b/app/graphql/types/ci/catalog/resource_scope_enum.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Catalog
+ class ResourceScopeEnum < BaseEnum
+ graphql_name 'CiCatalogResourceScope'
+ description 'Values for scoping catalog resources'
+
+ value 'ALL', 'All catalog resources visible to the current user.', value: :all
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/catalog/resource_sort_enum.rb b/app/graphql/types/ci/catalog/resource_sort_enum.rb
new file mode 100644
index 00000000000..bb0b5a6e0eb
--- /dev/null
+++ b/app/graphql/types/ci/catalog/resource_sort_enum.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Catalog
+ class ResourceSortEnum < BaseEnum
+ graphql_name 'CiCatalogResourceSort'
+ description 'Values for sorting catalog resources'
+
+ value 'NAME_ASC', 'Name by ascending order.', value: :name_asc
+ value 'NAME_DESC', 'Name by descending order.', value: :name_desc
+ value 'LATEST_RELEASED_AT_ASC', 'Latest release date by ascending order.', value: :latest_released_at_asc
+ value 'LATEST_RELEASED_AT_DESC', 'Latest release date by descending order.', value: :latest_released_at_desc
+ value 'CREATED_ASC', 'Created date by ascending order.', value: :created_at_asc
+ value 'CREATED_DESC', 'Created date by descending order.', value: :created_at_desc
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/catalog/resource_type.rb b/app/graphql/types/ci/catalog/resource_type.rb
new file mode 100644
index 00000000000..119313ae52b
--- /dev/null
+++ b/app/graphql/types/ci/catalog/resource_type.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Catalog
+ # rubocop: disable Graphql/AuthorizeTypes
+ class ResourceType < BaseObject
+ graphql_name 'CiCatalogResource'
+
+ connection_type_class Types::CountableConnectionType
+
+ field :open_issues_count, GraphQL::Types::Int, null: false,
+ description: 'Count of open issues that belong to the the catalog resource.',
+ alpha: { milestone: '16.3' }
+
+ field :open_merge_requests_count, GraphQL::Types::Int, null: false,
+ description: 'Count of open merge requests that belong to the the catalog resource.',
+ alpha: { milestone: '16.3' }
+
+ field :id, GraphQL::Types::ID, null: false, description: 'ID of the catalog resource.',
+ alpha: { milestone: '15.11' }
+
+ field :name, GraphQL::Types::String, null: true, description: 'Name of the catalog resource.',
+ alpha: { milestone: '15.11' }
+
+ field :description, GraphQL::Types::String, null: true, description: 'Description of the catalog resource.',
+ alpha: { milestone: '15.11' }
+
+ field :icon, GraphQL::Types::String, null: true, description: 'Icon for the catalog resource.',
+ method: :avatar_path, alpha: { milestone: '15.11' }
+
+ field :web_path, GraphQL::Types::String, null: true, description: 'Web path of the catalog resource.',
+ alpha: { milestone: '16.1' }
+
+ field :versions, Types::ReleaseType.connection_type, null: true,
+ description: 'Versions of the catalog resource. This field can only be ' \
+ 'resolved for one catalog resource in any single request.',
+ resolver: Resolvers::Ci::Catalog::VersionsResolver,
+ alpha: { milestone: '16.2' }
+
+ field :latest_version, Types::ReleaseType, null: true, description: 'Latest version of the catalog resource.',
+ alpha: { milestone: '16.1' }
+
+ field :latest_released_at, Types::TimeType, null: true,
+ description: "Release date of the catalog resource's latest version.",
+ alpha: { milestone: '16.5' }
+
+ field :star_count, GraphQL::Types::Int, null: false,
+ description: 'Number of times the catalog resource has been starred.',
+ alpha: { milestone: '16.1' }
+
+ field :forks_count, GraphQL::Types::Int, null: false, calls_gitaly: true,
+ description: 'Number of times the catalog resource has been forked.',
+ alpha: { milestone: '16.1' }
+
+ field :root_namespace, Types::NamespaceType, null: true,
+ description: 'Root namespace of the catalog resource.',
+ alpha: { milestone: '16.1' }
+
+ markdown_field :readme_html, null: false,
+ alpha: { milestone: '16.1' }
+
+ def open_issues_count
+ BatchLoader::GraphQL.wrap(object.project.open_issues_count)
+ end
+
+ def open_merge_requests_count
+ BatchLoader::GraphQL.wrap(object.project.open_merge_requests_count)
+ end
+
+ def web_path
+ ::Gitlab::Routing.url_helpers.project_path(object.project)
+ end
+
+ def latest_version
+ BatchLoader::GraphQL.for(object.project).batch do |projects, loader|
+ latest_releases = ReleasesFinder.new(projects, current_user, latest: true).execute
+
+ latest_releases.index_by(&:project).each do |project, latest_release|
+ loader.call(project, latest_release)
+ end
+ end
+ end
+
+ def forks_count
+ BatchLoader::GraphQL.wrap(object.forks_count)
+ end
+
+ def root_namespace
+ BatchLoader::GraphQL.for(object.project_id).batch do |project_ids, loader|
+ projects = Project.id_in(project_ids)
+
+ # This preloader uses traversal_ids to obtain Group-type root namespaces.
+ # It also preloads each project's immediate parent namespace, which effectively
+ # preloads the User-type root namespaces since they cannot be nested (parent == root).
+ Preloaders::ProjectRootAncestorPreloader.new(projects, :group).execute
+ root_namespaces = projects.map(&:root_ancestor)
+
+ # NamespaceType requires the `:read_namespace` ability. We must preload the policy for
+ # Group-type namespaces to avoid N+1 queries caused by the authorization requests.
+ group_root_namespaces = root_namespaces.select { |n| n.type == ::Group.sti_name }
+ Preloaders::GroupPolicyPreloader.new(group_root_namespaces, current_user).execute
+
+ # For User-type namespaces, the authorization request requires preloading the owner objects.
+ user_root_namespaces = root_namespaces.select { |n| n.type == ::Namespaces::UserNamespace.sti_name }
+ ActiveRecord::Associations::Preloader.new(records: user_root_namespaces, associations: :owner).call
+
+ projects.each { |project| loader.call(project.id, project.root_ancestor) }
+ end
+ end
+
+ def readme_html_resolver
+ markdown_context = context.to_h.dup.merge(project: object.project)
+ ::MarkupHelper.markdown(object.project.repository.readme&.data, markdown_context)
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+ end
+end
diff --git a/app/graphql/types/ci/pipeline_status_enum.rb b/app/graphql/types/ci/pipeline_status_enum.rb
index c8e031e18ea..17cf48bb5cf 100644
--- a/app/graphql/types/ci/pipeline_status_enum.rb
+++ b/app/graphql/types/ci/pipeline_status_enum.rb
@@ -7,6 +7,7 @@ module Types
created: 'Pipeline has been created.',
waiting_for_resource: 'A resource (for example, a runner) that the pipeline requires to run is unavailable.',
preparing: 'Pipeline is preparing to run.',
+ waiting_for_callback: 'Pipeline is waiting for an external action.',
pending: 'Pipeline has not started running yet.',
running: 'Pipeline is running.',
failed: 'At least one stage of the pipeline failed.',
diff --git a/app/graphql/types/container_registry/protection/rule_access_level_enum.rb b/app/graphql/types/container_registry/protection/rule_access_level_enum.rb
new file mode 100644
index 00000000000..31e8cbe2e49
--- /dev/null
+++ b/app/graphql/types/container_registry/protection/rule_access_level_enum.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module ContainerRegistry
+ module Protection
+ class RuleAccessLevelEnum < BaseEnum
+ graphql_name 'ContainerRegistryProtectionRuleAccessLevel'
+ description 'Access level of a container registry protection rule resource'
+
+ ::ContainerRegistry::Protection::Rule.push_protected_up_to_access_levels.each_key do |access_level_key|
+ value access_level_key.upcase, value: access_level_key.to_s,
+ description: "#{access_level_key.capitalize} access."
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/container_registry/protection/rule_type.rb b/app/graphql/types/container_registry/protection/rule_type.rb
new file mode 100644
index 00000000000..387f0202d2d
--- /dev/null
+++ b/app/graphql/types/container_registry/protection/rule_type.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Types
+ module ContainerRegistry
+ module Protection
+ class RuleType < ::Types::BaseObject
+ graphql_name 'ContainerRegistryProtectionRule'
+ description 'A container registry protection rule designed to prevent users with a certain ' \
+ 'access level or lower from altering the container registry.'
+
+ authorize :admin_container_image
+
+ field :id,
+ ::Types::GlobalIDType[::ContainerRegistry::Protection::Rule],
+ null: false,
+ description: 'ID of the container registry protection rule.'
+
+ field :container_path_pattern,
+ GraphQL::Types::String,
+ null: false,
+ description:
+ 'Container repository path pattern protected by the protection rule. ' \
+ 'For example `@my-scope/my-container-*`. Wildcard character `*` allowed.'
+
+ field :push_protected_up_to_access_level,
+ Types::ContainerRegistry::Protection::RuleAccessLevelEnum,
+ null: false,
+ description:
+ 'Max GitLab access level to prevent from pushing container images to the container registry. ' \
+ 'For example `DEVELOPER`, `MAINTAINER`, `OWNER`.'
+
+ field :delete_protected_up_to_access_level,
+ Types::ContainerRegistry::Protection::RuleAccessLevelEnum,
+ null: false,
+ description:
+ 'Max GitLab access level to prevent from pushing container images to the container registry. ' \
+ 'For example `DEVELOPER`, `MAINTAINER`, `OWNER`.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/container_repository_details_type.rb b/app/graphql/types/container_repository_details_type.rb
index 1ee9e76a1c8..b043a7c9d8d 100644
--- a/app/graphql/types/container_repository_details_type.rb
+++ b/app/graphql/types/container_repository_details_type.rb
@@ -13,7 +13,8 @@ module Types
null: true,
description: 'Tags of the container repository.',
max_page_size: 20,
- resolver: Resolvers::ContainerRepositoryTagsResolver
+ resolver: Resolvers::ContainerRepositoryTagsResolver,
+ connection_extension: Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension
field :size,
GraphQL::Types::Float,
diff --git a/app/graphql/types/data_transfer/project_data_transfer_type.rb b/app/graphql/types/data_transfer/project_data_transfer_type.rb
index 36afa20194e..363b675209d 100644
--- a/app/graphql/types/data_transfer/project_data_transfer_type.rb
+++ b/app/graphql/types/data_transfer/project_data_transfer_type.rb
@@ -13,7 +13,6 @@ module Types
def total_egress(parent:)
return unless Feature.enabled?(:data_transfer_monitoring, parent.group)
- return 40_000_000 if Feature.enabled?(:data_transfer_monitoring_mock_data, parent.group)
object[:egress_nodes].sum('repository_egress + artifacts_egress + packages_egress + registry_egress')
end
diff --git a/app/graphql/types/group_member_type.rb b/app/graphql/types/group_member_type.rb
index 2745853c9bb..d494c55369d 100644
--- a/app/graphql/types/group_member_type.rb
+++ b/app/graphql/types/group_member_type.rb
@@ -11,11 +11,11 @@ module Types
implements MemberInterface
field :group, Types::GroupType, null: true,
- description: 'Group that a User is a member of.'
+ description: 'Group that a user is a member of.'
field :notification_email,
resolver: Resolvers::GroupMembers::NotificationEmailResolver,
- description: "Group notification email for User. Only available for admins."
+ description: "Group notification email for user. Only available for admins."
def group
Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.source_id).find
diff --git a/app/graphql/types/issuable_state_enum.rb b/app/graphql/types/issuable_state_enum.rb
index 5a1b11b3bdc..8e3ed1d4bc8 100644
--- a/app/graphql/types/issuable_state_enum.rb
+++ b/app/graphql/types/issuable_state_enum.rb
@@ -1,10 +1,15 @@
# frozen_string_literal: true
+# DO NOT use this ENUM with issues. We need to define a new enum in places where we
+# need to filter by state. locked is not a valid state filter for issues. More info in
+# https://gitlab.com/gitlab-org/gitlab/-/issues/420667#note_1605900474
module Types
class IssuableStateEnum < BaseEnum
graphql_name 'IssuableState'
description 'State of a GitLab issue or merge request'
+ INVALID_LOCKED_MESSAGE = 'locked is not a valid state filter for issues.'
+
value 'opened', description: 'In open state.'
value 'closed', description: 'In closed state.'
value 'locked', description: 'Discussion has been locked.'
diff --git a/app/graphql/types/merge_request_review_state_enum.rb b/app/graphql/types/merge_request_review_state_enum.rb
index 45f97758425..c7c82de2906 100644
--- a/app/graphql/types/merge_request_review_state_enum.rb
+++ b/app/graphql/types/merge_request_review_state_enum.rb
@@ -5,7 +5,11 @@ module Types
graphql_name 'MergeRequestReviewState'
description 'State of a review of a GitLab merge request.'
- from_rails_enum(::MergeRequestReviewer.states,
- description: "The merge request is %{name}.")
+ value 'UNREVIEWED', value: 'unreviewed',
+ description: 'Awaiting review from merge request reviewer.'
+ value 'REVIEWED', value: 'reviewed',
+ description: 'Merge request reviewer has reviewed.'
+ value 'REQUESTED_CHANGES', value: 'requested_changes',
+ description: 'Merge request reviewer has requested changes.'
end
end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index e6625e44508..9dca82f1750 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -106,7 +106,8 @@ module Types
null: false,
description: 'Status of all mergeability checks of the merge request.',
method: :all_mergeability_checks_results,
- alpha: { milestone: '16.5' }
+ alpha: { milestone: '16.5' },
+ calls_gitaly: true
field :mergeable_discussions_state, GraphQL::Types::Boolean, null: true,
calls_gitaly: true,
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 3af7140aed3..e1bd1f603ad 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -106,6 +106,7 @@ module Types
mount_mutation Mutations::Notes::Update::ImageDiffNote
mount_mutation Mutations::Notes::RepositionImageDiffNote
mount_mutation Mutations::Notes::Destroy
+ mount_mutation Mutations::Organizations::Create, alpha: { milestone: '16.6' }
mount_mutation Mutations::Projects::SyncFork, calls_gitaly: true, alpha: { milestone: '15.9' }
mount_mutation Mutations::Releases::Create
mount_mutation Mutations::Releases::Update
@@ -134,33 +135,36 @@ module Types
mount_mutation Mutations::DesignManagement::Move
mount_mutation Mutations::DesignManagement::Update
mount_mutation Mutations::ContainerExpirationPolicies::Update
+ mount_mutation Mutations::ContainerRegistry::Protection::Rule::Create, alpha: { milestone: '16.6' }
mount_mutation Mutations::ContainerRepositories::Destroy
mount_mutation Mutations::ContainerRepositories::DestroyTags
+ mount_mutation Mutations::Ci::Catalog::Resources::Create, alpha: { milestone: '15.11' }
+ mount_mutation Mutations::Ci::Catalog::Resources::Unpublish, alpha: { milestone: '16.6' }
+ mount_mutation Mutations::Ci::Job::Cancel
+ mount_mutation Mutations::Ci::Job::Play
+ mount_mutation Mutations::Ci::Job::Retry
+ mount_mutation Mutations::Ci::Job::ArtifactsDestroy
+ mount_mutation Mutations::Ci::Job::Unschedule
+ mount_mutation Mutations::Ci::JobTokenScope::AddProject
+ mount_mutation Mutations::Ci::JobArtifact::BulkDestroy, alpha: { milestone: '15.10' }
+ mount_mutation Mutations::Ci::JobArtifact::Destroy
+ mount_mutation Mutations::Ci::JobTokenScope::RemoveProject
mount_mutation Mutations::Ci::Pipeline::Cancel
mount_mutation Mutations::Ci::Pipeline::Destroy
mount_mutation Mutations::Ci::Pipeline::Retry
+ mount_mutation Mutations::Ci::PipelineSchedule::Create
mount_mutation Mutations::Ci::PipelineSchedule::Delete
- mount_mutation Mutations::Ci::PipelineSchedule::TakeOwnership
mount_mutation Mutations::Ci::PipelineSchedule::Play
- mount_mutation Mutations::Ci::PipelineSchedule::Create
+ mount_mutation Mutations::Ci::PipelineSchedule::TakeOwnership
mount_mutation Mutations::Ci::PipelineSchedule::Update
mount_mutation Mutations::Ci::PipelineTrigger::Create, alpha: { milestone: '16.3' }
- mount_mutation Mutations::Ci::PipelineTrigger::Update, alpha: { milestone: '16.3' }
mount_mutation Mutations::Ci::PipelineTrigger::Delete, alpha: { milestone: '16.3' }
+ mount_mutation Mutations::Ci::PipelineTrigger::Update, alpha: { milestone: '16.3' }
mount_mutation Mutations::Ci::ProjectCiCdSettingsUpdate
- mount_mutation Mutations::Ci::Job::ArtifactsDestroy
- mount_mutation Mutations::Ci::Job::Play
- mount_mutation Mutations::Ci::Job::Retry
- mount_mutation Mutations::Ci::Job::Cancel
- mount_mutation Mutations::Ci::Job::Unschedule
- mount_mutation Mutations::Ci::JobArtifact::Destroy
- mount_mutation Mutations::Ci::JobArtifact::BulkDestroy, alpha: { milestone: '15.10' }
- mount_mutation Mutations::Ci::JobTokenScope::AddProject
- mount_mutation Mutations::Ci::JobTokenScope::RemoveProject
+ mount_mutation Mutations::Ci::Runner::BulkDelete, alpha: { milestone: '15.3' }
mount_mutation Mutations::Ci::Runner::Create, alpha: { milestone: '15.10' }
- mount_mutation Mutations::Ci::Runner::Update
mount_mutation Mutations::Ci::Runner::Delete
- mount_mutation Mutations::Ci::Runner::BulkDelete, alpha: { milestone: '15.3' }
+ mount_mutation Mutations::Ci::Runner::Update
mount_mutation Mutations::Ci::RunnersRegistrationToken::Reset
mount_mutation Mutations::Namespace::PackageSettings::Update
mount_mutation Mutations::Groups::Update
@@ -171,6 +175,7 @@ module Types
extensions: [::Gitlab::Graphql::Limit::FieldCallCount => { limit: 1 }]
mount_mutation Mutations::Packages::DestroyFile
mount_mutation Mutations::Packages::Protection::Rule::Create, alpha: { milestone: '16.5' }
+ mount_mutation Mutations::Packages::Protection::Rule::Delete, alpha: { milestone: '16.6' }
mount_mutation Mutations::Packages::DestroyFiles
mount_mutation Mutations::Packages::Cleanup::Policy::Update
mount_mutation Mutations::Echo
diff --git a/app/graphql/types/namespace/package_settings_type.rb b/app/graphql/types/namespace/package_settings_type.rb
index 61240243b1f..6c6144f2357 100644
--- a/app/graphql/types/namespace/package_settings_type.rb
+++ b/app/graphql/types/namespace/package_settings_type.rb
@@ -20,21 +20,18 @@ module Types
field :maven_duplicates_allowed, GraphQL::Types::Boolean,
null: false,
description: 'Indicates whether duplicate Maven packages are allowed for this namespace.'
- field :nuget_duplicate_exception_regex, Types::UntrustedRegexp,
- null: true,
- description: 'When nuget_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. ' \
- 'Error is raised if `nuget_duplicates_option` feature flag is disabled.'
- field :nuget_duplicates_allowed, GraphQL::Types::Boolean,
- null: false,
- description: 'Indicates whether duplicate NuGet packages are allowed for this namespace. ' \
- 'Error is raised if `nuget_duplicates_option` feature flag is disabled.'
-
field :maven_package_requests_forwarding, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether Maven package forwarding is allowed for this namespace.'
field :npm_package_requests_forwarding, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether npm package forwarding is allowed for this namespace.'
+ field :nuget_duplicate_exception_regex, Types::UntrustedRegexp,
+ null: true,
+ description: 'When nuget_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. '
+ field :nuget_duplicates_allowed, GraphQL::Types::Boolean,
+ null: false,
+ description: 'Indicates whether duplicate NuGet packages are allowed for this namespace. '
field :pypi_package_requests_forwarding, GraphQL::Types::Boolean,
null: true,
description: 'Indicates whether PyPI package forwarding is allowed for this namespace.'
diff --git a/app/graphql/types/notes/noteable_interface.rb b/app/graphql/types/notes/noteable_interface.rb
index 9971511d6ce..7c75f213e24 100644
--- a/app/graphql/types/notes/noteable_interface.rb
+++ b/app/graphql/types/notes/noteable_interface.rb
@@ -21,6 +21,8 @@ module Types
Types::DesignManagement::DesignType
when ::AlertManagement::Alert
Types::AlertManagement::AlertType
+ when AbuseReport
+ Types::AbuseReportType
else
raise "Unknown GraphQL type for #{object}"
end
diff --git a/app/graphql/types/organizations/organization_type.rb b/app/graphql/types/organizations/organization_type.rb
index cae0ef2232e..e7ba8de527c 100644
--- a/app/graphql/types/organizations/organization_type.rb
+++ b/app/graphql/types/organizations/organization_type.rb
@@ -33,6 +33,10 @@ module Types
null: false,
description: 'Path of the organization.',
alpha: { milestone: '16.4' }
+ field :web_url, GraphQL::Types::String,
+ null: false,
+ description: 'Web URL of the organization.',
+ alpha: { milestone: '16.6' }
end
end
end
diff --git a/app/graphql/types/organizations/organization_user_badge_type.rb b/app/graphql/types/organizations/organization_user_badge_type.rb
new file mode 100644
index 00000000000..f4e18676dd1
--- /dev/null
+++ b/app/graphql/types/organizations/organization_user_badge_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Organizations
+ # rubocop: disable Graphql/AuthorizeTypes -- Already authorized in parent OrganizationUserType.
+ class OrganizationUserBadgeType < BaseObject
+ graphql_name 'OrganizationUserBadge'
+ description 'An organization user badge.'
+
+ field :text,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Badge text.'
+
+ field :variant,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Badge variant.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/organizations/organization_user_type.rb b/app/graphql/types/organizations/organization_user_type.rb
index 41924586f38..ce036c7dd4a 100644
--- a/app/graphql/types/organizations/organization_user_type.rb
+++ b/app/graphql/types/organizations/organization_user_type.rb
@@ -13,7 +13,7 @@ module Types
alias_method :organization_user, :object
field :badges,
- [GraphQL::Types::String],
+ [::Types::Organizations::OrganizationUserBadgeType],
null: true,
description: 'Badges describing the user within the organization.',
alpha: { milestone: '16.4' }
@@ -29,7 +29,7 @@ module Types
alpha: { milestone: '16.4' }
def badges
- user_badges_in_admin_section(organization_user.user).pluck(:text) # rubocop:disable CodeReuse/ActiveRecord
+ user_badges_in_admin_section(organization_user.user)
end
end
end
diff --git a/app/graphql/types/packages/package_base_type.rb b/app/graphql/types/packages/package_base_type.rb
index aa580d48709..5102e4ebcd5 100644
--- a/app/graphql/types/packages/package_base_type.rb
+++ b/app/graphql/types/packages/package_base_type.rb
@@ -10,11 +10,19 @@ module Types
authorize :read_package
+ expose_permissions Types::PermissionTypes::Package
+
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 :can_destroy, GraphQL::Types::Boolean,
+ null: false,
+ deprecated: {
+ reason: 'Superseded by `user_permissions` field. See `Types::PermissionTypes::Package` type',
+ milestone: '16.6'
+ },
+ description: 'Whether the user can destroy the package.'
field :created_at, Types::TimeType, null: false, description: 'Date of creation.'
field :metadata, Types::Packages::MetadataType,
null: true,
diff --git a/app/graphql/types/packages/protection/rule_type.rb b/app/graphql/types/packages/protection/rule_type.rb
index 1e969d39ce2..e2ea2d89d2d 100644
--- a/app/graphql/types/packages/protection/rule_type.rb
+++ b/app/graphql/types/packages/protection/rule_type.rb
@@ -10,6 +10,11 @@ module Types
authorize :admin_package
+ field :id,
+ ::Types::GlobalIDType[::Packages::Protection::Rule],
+ null: false,
+ description: 'ID of the package protection rule.'
+
field :package_name_pattern,
GraphQL::Types::String,
null: false,
diff --git a/app/graphql/types/packages/pypi/metadatum_type.rb b/app/graphql/types/packages/pypi/metadatum_type.rb
index 63452d8ab6e..8ccdb592c52 100644
--- a/app/graphql/types/packages/pypi/metadatum_type.rb
+++ b/app/graphql/types/packages/pypi/metadatum_type.rb
@@ -9,8 +9,17 @@ module Types
authorize :read_package
+ field :author_email, GraphQL::Types::String, null: true,
+ description: 'Author email address(es) in RFC-822 format.'
+ field :description, GraphQL::Types::String, null: true,
+ description: 'Longer description that can run to several paragraphs.'
+ field :description_content_type, GraphQL::Types::String, null: true,
+ description: 'Markup syntax used in the description field.'
field :id, ::Types::GlobalIDType[::Packages::Pypi::Metadatum], null: false, description: 'ID of the metadatum.'
+ field :keywords, GraphQL::Types::String, null: true, description: 'List of keywords, separated by commas.'
+ field :metadata_version, GraphQL::Types::String, null: true, description: 'Metadata version.'
field :required_python, GraphQL::Types::String, null: true, description: 'Required Python version of the Pypi package.'
+ field :summary, GraphQL::Types::String, null: true, description: 'One-line summary of the description.'
end
end
end
diff --git a/app/graphql/types/permission_types/abuse_report.rb b/app/graphql/types/permission_types/abuse_report.rb
new file mode 100644
index 00000000000..abd5d545d02
--- /dev/null
+++ b/app/graphql/types/permission_types/abuse_report.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ module PermissionTypes
+ class AbuseReport < BasePermissionType
+ graphql_name 'AbuseReportPermissions'
+
+ abilities :read_abuse_report, :create_note
+ end
+ end
+end
diff --git a/app/graphql/types/permission_types/base_permission_type.rb b/app/graphql/types/permission_types/base_permission_type.rb
index d45c61f489b..3c0e68bdaf2 100644
--- a/app/graphql/types/permission_types/base_permission_type.rb
+++ b/app/graphql/types/permission_types/base_permission_type.rb
@@ -21,7 +21,7 @@ module Types
kword_args = kword_args.reverse_merge(
name: name,
type: GraphQL::Types::Boolean,
- description: "Indicates the user can perform `#{name}` on this resource",
+ description: "If `true`, the user can perform `#{name}` on this resource",
null: false)
field(**kword_args, &block) # rubocop:disable Graphql/Descriptions
diff --git a/app/graphql/types/permission_types/ci/job.rb b/app/graphql/types/permission_types/ci/job.rb
index c9a85317e67..35904fb1fc3 100644
--- a/app/graphql/types/permission_types/ci/job.rb
+++ b/app/graphql/types/permission_types/ci/job.rb
@@ -8,6 +8,7 @@ module Types
abilities :read_job_artifacts, :read_build
ability_field :update_build, calls_gitaly: true
+ ability_field :cancel_build, calls_gitaly: true
end
end
end
diff --git a/app/graphql/types/permission_types/ci/pipeline.rb b/app/graphql/types/permission_types/ci/pipeline.rb
index cfd68380005..94adbf7c59b 100644
--- a/app/graphql/types/permission_types/ci/pipeline.rb
+++ b/app/graphql/types/permission_types/ci/pipeline.rb
@@ -8,6 +8,7 @@ module Types
abilities :admin_pipeline, :destroy_pipeline
ability_field :update_pipeline, calls_gitaly: true
+ ability_field :cancel_pipeline, calls_gitaly: true
end
end
end
diff --git a/app/graphql/types/permission_types/package.rb b/app/graphql/types/permission_types/package.rb
new file mode 100644
index 00000000000..debde3a1a8e
--- /dev/null
+++ b/app/graphql/types/permission_types/package.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ module PermissionTypes
+ class Package < BasePermissionType
+ graphql_name 'PackagePermissions'
+
+ ability_field :destroy_package,
+ description: 'If `true`, the user can perform `destroy_package` on this resource'
+ end
+ end
+end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 95caefc3825..ec87f133843 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -641,6 +641,12 @@ module Types
resolver: Resolvers::AutocompleteUsersResolver,
description: 'Search users for autocompletion'
+ field :detailed_import_status,
+ ::Types::Projects::DetailedImportStatusType,
+ null: true,
+ description: 'Detailed import status of the project.',
+ method: :import_state
+
def timelog_categories
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
end
diff --git a/app/graphql/types/projects/detailed_import_status_type.rb b/app/graphql/types/projects/detailed_import_status_type.rb
new file mode 100644
index 00000000000..9cba176e097
--- /dev/null
+++ b/app/graphql/types/projects/detailed_import_status_type.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Types
+ module Projects
+ class DetailedImportStatusType < BaseObject
+ graphql_name 'DetailedImportStatus'
+ description 'Details of the import status of a project.'
+
+ authorize :read_project
+
+ field :id, ::Types::GlobalIDType[::ProjectImportState],
+ description: 'ID of the import state.'
+
+ field :status, GraphQL::Types::String,
+ description: 'Current status of the import.'
+
+ field :url, GraphQL::Types::String,
+ description: 'Import url.'
+
+ field :last_error, GraphQL::Types::String,
+ description: 'Last error of the import.',
+ null: true,
+ authorize: :read_import_error
+
+ field :last_update_at, Types::TimeType,
+ description: 'Time of the last update.'
+
+ field :last_update_started_at, Types::TimeType,
+ description: 'Time of the start of the last update.'
+
+ field :last_successful_update_at, Types::TimeType,
+ description: 'Time of the last successful update.'
+
+ def url
+ object.project.safe_import_url
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index d185007f05b..173e877d86c 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -21,6 +21,20 @@ module Types
required: true, description: 'Global ID of the CI stage.'
end
+ field :ci_catalog_resources,
+ ::Types::Ci::Catalog::ResourceType.connection_type,
+ null: true,
+ alpha: { milestone: '15.11' },
+ description: 'All CI/CD Catalog resources under a common namespace, visible to an authorized user',
+ resolver: ::Resolvers::Ci::Catalog::ResourcesResolver
+
+ field :ci_catalog_resource,
+ ::Types::Ci::Catalog::ResourceType,
+ null: true,
+ alpha: { milestone: '16.1' },
+ description: 'A single CI/CD Catalog resource visible to an authorized user',
+ resolver: ::Resolvers::Ci::Catalog::ResourceResolver
+
field :ci_variables,
Types::Ci::InstanceVariableType.connection_type,
null: true,
@@ -41,6 +55,14 @@ module Types
null: false,
description: 'Fields related to design management.'
field :echo, resolver: Resolvers::EchoResolver
+ field :frecent_groups, [Types::GroupType],
+ resolver: Resolvers::Users::FrecentGroupsResolver,
+ description: "A user's frecently visited groups. Requires the `frecent_namespaces_suggestions` feature flag to be enabled.",
+ alpha: { milestone: '16.6' }
+ field :frecent_projects, [Types::ProjectType],
+ resolver: Resolvers::Users::FrecentProjectsResolver,
+ description: "A user's frecently visited projects. Requires the `frecent_namespaces_suggestions` feature flag to be enabled.",
+ alpha: { milestone: '16.6' }
field :gitpod_enabled, GraphQL::Types::Boolean,
null: true,
description: "Whether Gitpod is enabled in application settings."
diff --git a/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb b/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb
index fb7d722069f..7dd47611a2e 100644
--- a/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb
+++ b/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb
@@ -3,10 +3,9 @@
module Types
module Security
module CodequalityReportsComparer
- # rubocop: disable Graphql/AuthorizeTypes (The resolver authorizes the request)
+ # rubocop: disable Graphql/AuthorizeTypes -- The resolver authorizes the request
class DegradationType < BaseObject
graphql_name 'CodequalityReportsComparerReportDegradation'
-
description 'Represents a degradation on the compared codequality report.'
field :description, GraphQL::Types::String,
diff --git a/app/graphql/types/security/codequality_reports_comparer/report_generation_status_enum.rb b/app/graphql/types/security/codequality_reports_comparer/report_generation_status_enum.rb
new file mode 100644
index 00000000000..dace3aec97c
--- /dev/null
+++ b/app/graphql/types/security/codequality_reports_comparer/report_generation_status_enum.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ module Security
+ module CodequalityReportsComparer
+ class ReportGenerationStatusEnum < BaseEnum
+ graphql_name 'CodequalityReportsComparerReportGenerationStatus'
+ description 'Represents the generation status of the compared codequality report.'
+
+ value 'PARSED', value: :parsed, description: 'Report was generated.'
+ value 'PARSING', value: :parsing, description: 'Report is being generated.'
+ value 'ERROR', value: :error, description: 'An error happened while generating the report.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/security/codequality_reports_comparer/report_type.rb b/app/graphql/types/security/codequality_reports_comparer/report_type.rb
index 8a41160141a..d20c9dd9ab6 100644
--- a/app/graphql/types/security/codequality_reports_comparer/report_type.rb
+++ b/app/graphql/types/security/codequality_reports_comparer/report_type.rb
@@ -3,7 +3,7 @@
module Types
module Security
module CodequalityReportsComparer
- # rubocop: disable Graphql/AuthorizeTypes (Parent node applies authorization)
+ # rubocop: disable Graphql/AuthorizeTypes -- Parent node applies authorization
class ReportType < BaseObject
graphql_name 'CodequalityReportsComparerReport'
diff --git a/app/graphql/types/security/codequality_reports_comparer/status_enum.rb b/app/graphql/types/security/codequality_reports_comparer/status_enum.rb
index 9cab2664db8..fdccfdc7e44 100644
--- a/app/graphql/types/security/codequality_reports_comparer/status_enum.rb
+++ b/app/graphql/types/security/codequality_reports_comparer/status_enum.rb
@@ -4,11 +4,11 @@ module Types
module Security
module CodequalityReportsComparer
class StatusEnum < BaseEnum
- graphql_name 'CodequalityReportsComparerReportStatus'
- description 'Report comparison status'
+ graphql_name 'CodequalityReportsComparerStatus'
+ description 'Represents the state of the code quality report.'
- value 'SUCCESS', value: 'success', description: 'Report successfully generated.'
- value 'FAILED', value: 'failed', description: 'Report failed to generate.'
+ value 'SUCCESS', value: 'success', description: 'No degradations found in the head pipeline report.'
+ value 'FAILED', value: 'failed', description: 'Report generated and there are new code quality degradations.'
value 'NOT_FOUND', value: 'not_found', description: 'Head report or base report not found.'
end
end
diff --git a/app/graphql/types/security/codequality_reports_comparer/summary_type.rb b/app/graphql/types/security/codequality_reports_comparer/summary_type.rb
index cd4a594c193..43037be5245 100644
--- a/app/graphql/types/security/codequality_reports_comparer/summary_type.rb
+++ b/app/graphql/types/security/codequality_reports_comparer/summary_type.rb
@@ -3,7 +3,7 @@
module Types
module Security
module CodequalityReportsComparer
- # rubocop: disable Graphql/AuthorizeTypes (The resolver authorizes the request)
+ # rubocop: disable Graphql/AuthorizeTypes -- The resolver authorizes the request
class SummaryType < BaseObject
graphql_name 'CodequalityReportsComparerReportSummary'
diff --git a/app/graphql/types/security/codequality_reports_comparer_type.rb b/app/graphql/types/security/codequality_reports_comparer_type.rb
index 8088bf84627..32fe8c12330 100644
--- a/app/graphql/types/security/codequality_reports_comparer_type.rb
+++ b/app/graphql/types/security/codequality_reports_comparer_type.rb
@@ -2,12 +2,17 @@
module Types
module Security
- # rubocop: disable Graphql/AuthorizeTypes (The resolver authorizes the request)
+ # rubocop: disable Graphql/AuthorizeTypes -- The resolver authorizes the request
class CodequalityReportsComparerType < BaseObject
graphql_name 'CodequalityReportsComparer'
description 'Represents reports comparison for code quality.'
+ field :status,
+ type: CodequalityReportsComparer::ReportGenerationStatusEnum,
+ null: true,
+ description: 'Compared codequality report generation status.'
+
field :report,
type: CodequalityReportsComparer::ReportType,
null: true,
diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb
index 47d486265b0..040711b5f58 100644
--- a/app/graphql/types/user_interface.rb
+++ b/app/graphql/types/user_interface.rb
@@ -71,6 +71,11 @@ module Types
type: GraphQL::Types::String,
null: false,
description: 'Web path of the user.'
+ field :organizations,
+ resolver: Resolvers::Users::OrganizationsResolver,
+ null: true,
+ alpha: { milestone: '16.6' },
+ description: 'Organizations where the user has access.'
field :group_memberships,
type: Types::GroupMemberType.connection_type,
null: true,
@@ -134,13 +139,11 @@ module Types
field :saved_replies,
Types::SavedReplyType.connection_type,
null: true,
- description: 'Saved replies authored by the user. ' \
- 'Will not return saved replies if `saved_replies` feature flag is disabled.'
+ description: 'Saved replies authored by the user.'
field :saved_reply,
resolver: Resolvers::SavedReplyResolver,
- description: 'Saved reply authored by the user. ' \
- 'Will not return saved reply if `saved_replies` feature flag is disabled.'
+ description: 'Saved reply authored by the user.'
field :gitpod_enabled, GraphQL::Types::Boolean, null: true,
description: 'Whether Gitpod is enabled at the user level.'
@@ -197,6 +200,11 @@ module Types
null: true,
description: 'Timestamp of when the user was created.'
+ field :last_activity_on,
+ type: Types::DateType,
+ null: true,
+ description: 'Date the user last performed any actions.'
+
field :pronouns,
type: ::GraphQL::Types::String,
null: true,
diff --git a/app/graphql/types/work_items/linked_item_type.rb b/app/graphql/types/work_items/linked_item_type.rb
index a4dbeed7480..1b989d78091 100644
--- a/app/graphql/types/work_items/linked_item_type.rb
+++ b/app/graphql/types/work_items/linked_item_type.rb
@@ -2,21 +2,29 @@
module Types
module WorkItems
- # rubocop:disable Graphql/AuthorizeTypes
class LinkedItemType < BaseObject
graphql_name 'LinkedWorkItemType'
+ authorize :read_work_item
+
field :link_created_at, Types::TimeType,
- description: 'Timestamp the link was created.', null: false
+ description: 'Timestamp the link was created.', null: false,
+ method: :issue_link_created_at
field :link_id, ::Types::GlobalIDType[::WorkItems::RelatedWorkItemLink],
- description: 'Global ID of the link.', null: false
+ description: 'Global ID of the link.', null: false,
+ method: :issue_link_id
field :link_type, GraphQL::Types::String,
- description: 'Type of link.', null: false
+ description: 'Type of link.', null: false,
+ method: :issue_link_type
field :link_updated_at, Types::TimeType,
- description: 'Timestamp the link was updated.', null: false
+ description: 'Timestamp the link was updated.', null: false,
+ method: :issue_link_updated_at
field :work_item, Types::WorkItemType,
- description: 'Linked work item.', null: false
+ description: 'Linked work item.', null: true
+
+ def work_item
+ object
+ end
end
- # rubocop:enable Graphql/AuthorizeTypes
end
end
diff --git a/app/graphql/types/work_items/widgets/linked_items_type.rb b/app/graphql/types/work_items/widgets/linked_items_type.rb
index 2611c2456c5..c541a12a050 100644
--- a/app/graphql/types/work_items/widgets/linked_items_type.rb
+++ b/app/graphql/types/work_items/widgets/linked_items_type.rb
@@ -13,6 +13,7 @@ module Types
field :linked_items, Types::WorkItems::LinkedItemType.connection_type,
null: true, complexity: 5,
alpha: { milestone: '16.3' },
+ extras: [:lookahead],
description: 'Linked items for the work item. Returns `null` ' \
'if `linked_work_items` feature flag is disabled.',
resolver: Resolvers::WorkItems::LinkedItemsResolver