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-12-19 14:01:45 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-12-19 14:01:45 +0300
commit9297025d0b7ddf095eb618dfaaab2ff8f2018d8b (patch)
tree865198c01d1824a9b098127baa3ab980c9cd2c06 /app/graphql
parent6372471f43ee03c05a7c1f8b0c6ac6b8a7431dbe (diff)
Add latest changes from gitlab-org/gitlab@16-7-stable-eev16.7.0-rc42
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/mutations/achievements/award.rb4
-rw-r--r--app/graphql/mutations/achievements/create.rb4
-rw-r--r--app/graphql/mutations/achievements/delete.rb4
-rw-r--r--app/graphql/mutations/achievements/delete_user_achievement.rb4
-rw-r--r--app/graphql/mutations/achievements/revoke.rb4
-rw-r--r--app/graphql/mutations/achievements/update.rb4
-rw-r--r--app/graphql/mutations/alert_management/http_integration/http_integration_base.rb4
-rw-r--r--app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb4
-rw-r--r--app/graphql/mutations/boards/destroy.rb6
-rw-r--r--app/graphql/mutations/boards/lists/create.rb4
-rw-r--r--app/graphql/mutations/branch_rules/update.rb50
-rw-r--r--app/graphql/mutations/ci/catalog/resources/base.rb19
-rw-r--r--app/graphql/mutations/ci/catalog/resources/create.rb12
-rw-r--r--app/graphql/mutations/ci/catalog/resources/destroy.rb28
-rw-r--r--app/graphql/mutations/ci/catalog/resources/unpublish.rb30
-rw-r--r--app/graphql/mutations/container_registry/protection/rule/create.rb6
-rw-r--r--app/graphql/mutations/container_registry/protection/rule/delete.rb41
-rw-r--r--app/graphql/mutations/container_registry/protection/rule/update.rb67
-rw-r--r--app/graphql/mutations/custom_emoji/destroy.rb6
-rw-r--r--app/graphql/mutations/customer_relations/contacts/create.rb4
-rw-r--r--app/graphql/mutations/customer_relations/organizations/create.rb4
-rw-r--r--app/graphql/mutations/namespace/package_settings/update.rb5
-rw-r--r--app/graphql/mutations/organizations/base.rb20
-rw-r--r--app/graphql/mutations/organizations/create.rb9
-rw-r--r--app/graphql/mutations/organizations/update.rb36
-rw-r--r--app/graphql/mutations/packages/protection/rule/update.rb62
-rw-r--r--app/graphql/mutations/projects/star.rb39
-rw-r--r--app/graphql/mutations/saved_replies/base.rb4
-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/mutations/snippets/base.rb4
-rw-r--r--app/graphql/mutations/user_preferences/update.rb9
-rw-r--r--app/graphql/mutations/work_items/convert.rb4
-rw-r--r--app/graphql/mutations/work_items/delete_task.rb58
-rw-r--r--app/graphql/queries/snippet/snippet.query.graphql1
-rw-r--r--app/graphql/resolvers/analytics/cycle_analytics/base_count_resolver.rb19
-rw-r--r--app/graphql/resolvers/analytics/cycle_analytics/base_issue_resolver.rb19
-rw-r--r--app/graphql/resolvers/analytics/cycle_analytics/base_merge_request_resolver.rb27
-rw-r--r--app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb16
-rw-r--r--app/graphql/resolvers/analytics/cycle_analytics/stages_resolver.rb31
-rw-r--r--app/graphql/resolvers/analytics/cycle_analytics/value_streams_resolver.rb20
-rw-r--r--app/graphql/resolvers/blame_resolver.rb17
-rw-r--r--app/graphql/resolvers/ci/catalog/resource_resolver.rb20
-rw-r--r--app/graphql/resolvers/ci/catalog/resources/versions_resolver.rb25
-rw-r--r--app/graphql/resolvers/ci/catalog/resources_resolver.rb18
-rw-r--r--app/graphql/resolvers/ci/catalog/versions_resolver.rb24
-rw-r--r--app/graphql/resolvers/ci/runner_groups_resolver.rb2
-rw-r--r--app/graphql/resolvers/container_repository_tags_resolver.rb2
-rw-r--r--app/graphql/resolvers/custom_emoji_resolver.rb23
-rw-r--r--app/graphql/resolvers/group_issues_resolver.rb2
-rw-r--r--app/graphql/resolvers/group_milestones_resolver.rb33
-rw-r--r--app/graphql/resolvers/groups_resolver.rb2
-rw-r--r--app/graphql/resolvers/issues/base_parent_resolver.rb3
-rw-r--r--app/graphql/resolvers/issues_resolver.rb3
-rw-r--r--app/graphql/resolvers/kas/agent_connections_resolver.rb8
-rw-r--r--app/graphql/resolvers/ml/model_detail_resolver.rb27
-rw-r--r--app/graphql/resolvers/namespaces/work_item_state_counts_resolver.rb30
-rw-r--r--app/graphql/resolvers/organizations/groups_resolver.rb14
-rw-r--r--app/graphql/resolvers/project_milestones_resolver.rb9
-rw-r--r--app/graphql/resolvers/timelog_resolver.rb76
-rw-r--r--app/graphql/resolvers/users/frecent_groups_resolver.rb6
-rw-r--r--app/graphql/resolvers/users/frecent_projects_resolver.rb4
-rw-r--r--app/graphql/resolvers/work_item_references_resolver.rb71
-rw-r--r--app/graphql/resolvers/work_item_state_counts_resolver.rb16
-rw-r--r--app/graphql/resolvers/work_items/ancestors_resolver.rb8
-rw-r--r--app/graphql/resolvers/work_items/types_resolver.rb22
-rw-r--r--app/graphql/types/abuse_report_type.rb2
-rw-r--r--app/graphql/types/analytics/cycle_analytics/value_stream_type.rb5
-rw-r--r--app/graphql/types/analytics/cycle_analytics/value_streams/stage_event_enum.rb18
-rw-r--r--app/graphql/types/analytics/cycle_analytics/value_streams/stage_type.rb54
-rw-r--r--app/graphql/types/ci/catalog/resource_scope_enum.rb1
-rw-r--r--app/graphql/types/ci/catalog/resource_type.rb51
-rw-r--r--app/graphql/types/ci/catalog/resources/component_type.rb41
-rw-r--r--app/graphql/types/ci/catalog/resources/components/input_type.rb29
-rw-r--r--app/graphql/types/ci/catalog/resources/version_sort_enum.rb14
-rw-r--r--app/graphql/types/ci/catalog/resources/version_type.rb54
-rw-r--r--app/graphql/types/container_registry/protection/rule_type.rb4
-rw-r--r--app/graphql/types/container_repository_tag_type.rb10
-rw-r--r--app/graphql/types/container_repository_type.rb10
-rw-r--r--app/graphql/types/current_user_type.rb12
-rw-r--r--app/graphql/types/group_connection.rb22
-rw-r--r--app/graphql/types/group_type.rb15
-rw-r--r--app/graphql/types/issue_connection.rb22
-rw-r--r--app/graphql/types/issue_type.rb10
-rw-r--r--app/graphql/types/issue_type_enum.rb16
-rw-r--r--app/graphql/types/merge_requests/detailed_merge_status_enum.rb3
-rw-r--r--app/graphql/types/merge_requests/mergeability_check_identifier_enum.rb2
-rw-r--r--app/graphql/types/ml/candidate_links_type.rb20
-rw-r--r--app/graphql/types/ml/candidate_type.rb23
-rw-r--r--app/graphql/types/ml/model_type.rb22
-rw-r--r--app/graphql/types/ml/model_version_links_type.rb17
-rw-r--r--app/graphql/types/ml/model_version_type.rb22
-rw-r--r--app/graphql/types/mutation_type.rb9
-rw-r--r--app/graphql/types/namespace/package_settings_type.rb6
-rw-r--r--app/graphql/types/organizations/organization_type.rb19
-rw-r--r--app/graphql/types/permission_types/abuse_report.rb11
-rw-r--r--app/graphql/types/permission_types/container_repository.rb13
-rw-r--r--app/graphql/types/permission_types/container_repository_tag.rb13
-rw-r--r--app/graphql/types/project_feature_access_level_enum.rb12
-rw-r--r--app/graphql/types/project_feature_access_level_type.rb18
-rw-r--r--app/graphql/types/project_type.rb35
-rw-r--r--app/graphql/types/query_type.rb21
-rw-r--r--app/graphql/types/security/codequality_reports_comparer/degradation_type.rb2
-rw-r--r--app/graphql/types/user_interface.rb4
-rw-r--r--app/graphql/types/user_preferences_type.rb4
-rw-r--r--app/graphql/types/user_type.rb2
-rw-r--r--app/graphql/types/work_item_state_counts_type.rb25
-rw-r--r--app/graphql/types/work_item_type.rb6
-rw-r--r--app/graphql/types/work_items/deleted_task_input_type.rb19
-rw-r--r--app/graphql/types/work_items/type_type.rb20
-rw-r--r--app/graphql/types/work_items/widget_definition_interface.rb39
-rw-r--r--app/graphql/types/work_items/widget_definitions/assignees_type.rb32
-rw-r--r--app/graphql/types/work_items/widget_definitions/generic_type.rb16
-rw-r--r--app/graphql/types/work_items/widget_definitions/hierarchy_type.rb26
-rw-r--r--app/graphql/types/work_items/widgets/assignees_type.rb14
-rw-r--r--app/graphql/types/work_items/widgets/labels_type.rb7
116 files changed, 1477 insertions, 556 deletions
diff --git a/app/graphql/mutations/achievements/award.rb b/app/graphql/mutations/achievements/award.rb
index b486049594d..71a46a04a1c 100644
--- a/app/graphql/mutations/achievements/award.rb
+++ b/app/graphql/mutations/achievements/award.rb
@@ -29,10 +29,6 @@ module Mutations
result = ::Achievements::AwardService.new(current_user, achievement.id, recipient_id).execute
{ user_achievement: result.payload, errors: result.errors }
end
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Achievements::Achievement)
- end
end
end
end
diff --git a/app/graphql/mutations/achievements/create.rb b/app/graphql/mutations/achievements/create.rb
index 310a653c705..497eaee9b70 100644
--- a/app/graphql/mutations/achievements/create.rb
+++ b/app/graphql/mutations/achievements/create.rb
@@ -41,10 +41,6 @@ module Mutations
params: args).execute
{ achievement: result.payload, errors: result.errors }
end
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Namespace)
- end
end
end
end
diff --git a/app/graphql/mutations/achievements/delete.rb b/app/graphql/mutations/achievements/delete.rb
index 0b510b44b4e..fc00261a176 100644
--- a/app/graphql/mutations/achievements/delete.rb
+++ b/app/graphql/mutations/achievements/delete.rb
@@ -24,10 +24,6 @@ module Mutations
result = ::Achievements::DestroyService.new(current_user, achievement).execute
{ achievement: result.payload, errors: result.errors }
end
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Achievements::Achievement)
- end
end
end
end
diff --git a/app/graphql/mutations/achievements/delete_user_achievement.rb b/app/graphql/mutations/achievements/delete_user_achievement.rb
index f1527c2981a..be4c5a1d5e2 100644
--- a/app/graphql/mutations/achievements/delete_user_achievement.rb
+++ b/app/graphql/mutations/achievements/delete_user_achievement.rb
@@ -24,10 +24,6 @@ module Mutations
result = ::Achievements::DestroyUserAchievementService.new(current_user, user_achievement).execute
{ user_achievement: result.payload, errors: result.errors }
end
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Achievements::UserAchievement)
- end
end
end
end
diff --git a/app/graphql/mutations/achievements/revoke.rb b/app/graphql/mutations/achievements/revoke.rb
index 9d21b1c3741..ac5b38cefbf 100644
--- a/app/graphql/mutations/achievements/revoke.rb
+++ b/app/graphql/mutations/achievements/revoke.rb
@@ -24,10 +24,6 @@ module Mutations
result = ::Achievements::RevokeService.new(current_user, user_achievement).execute
{ user_achievement: result.payload, errors: result.errors }
end
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Achievements::UserAchievement)
- end
end
end
end
diff --git a/app/graphql/mutations/achievements/update.rb b/app/graphql/mutations/achievements/update.rb
index 2a9e6580629..8bb95ac41f3 100644
--- a/app/graphql/mutations/achievements/update.rb
+++ b/app/graphql/mutations/achievements/update.rb
@@ -37,10 +37,6 @@ module Mutations
result = ::Achievements::UpdateService.new(current_user, achievement, args).execute
{ achievement: result.payload, errors: result.errors }
end
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Achievements::Achievement)
- end
end
end
end
diff --git a/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb b/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb
index 9434ac1637e..760005ae249 100644
--- a/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb
+++ b/app/graphql/mutations/alert_management/http_integration/http_integration_base.rb
@@ -13,10 +13,6 @@ module Mutations
private
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_class: ::AlertManagement::HttpIntegration)
- end
-
def response(result)
{
integration: result.payload[:integration],
diff --git a/app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb b/app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb
index 28729ec70cd..19fb514d3a5 100644
--- a/app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb
+++ b/app/graphql/mutations/alert_management/prometheus_integration/prometheus_integration_base.rb
@@ -13,10 +13,6 @@ module Mutations
private
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_class: ::Integrations::Prometheus)
- end
-
def response(integration, result)
{
integration: integration,
diff --git a/app/graphql/mutations/boards/destroy.rb b/app/graphql/mutations/boards/destroy.rb
index 61e0c95f8d3..abdffaaeb90 100644
--- a/app/graphql/mutations/boards/destroy.rb
+++ b/app/graphql/mutations/boards/destroy.rb
@@ -26,12 +26,6 @@ module Mutations
errors: response.errors
}
end
-
- private
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Board)
- end
end
end
end
diff --git a/app/graphql/mutations/boards/lists/create.rb b/app/graphql/mutations/boards/lists/create.rb
index 590a905ab7b..a80a6af9e2f 100644
--- a/app/graphql/mutations/boards/lists/create.rb
+++ b/app/graphql/mutations/boards/lists/create.rb
@@ -19,10 +19,6 @@ module Mutations
private
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Board)
- end
-
def create_list(board, params)
create_list_service =
::Boards::Lists::CreateService.new(board.resource_parent, current_user, params)
diff --git a/app/graphql/mutations/branch_rules/update.rb b/app/graphql/mutations/branch_rules/update.rb
new file mode 100644
index 00000000000..c10e11970eb
--- /dev/null
+++ b/app/graphql/mutations/branch_rules/update.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Mutations
+ module BranchRules
+ class Update < BaseMutation
+ graphql_name 'BranchRuleUpdate'
+
+ include FindsProject
+
+ authorize :admin_project
+
+ argument :id, ::Types::GlobalIDType[::ProtectedBranch],
+ required: true,
+ description: 'Global ID of the protected branch.'
+
+ argument :name, GraphQL::Types::String,
+ required: true,
+ description: 'Branch name, with wildcards, for the branch rules.'
+
+ argument :project_path, GraphQL::Types::ID,
+ required: true,
+ description: 'Full path to the project that the branch is associated with.'
+
+ field :branch_rule,
+ Types::Projects::BranchRuleType,
+ null: true,
+ description: 'Branch rule after mutation.'
+
+ def resolve(id:, project_path:, name:)
+ protected_branch = ::Gitlab::Graphql::Lazy.force(GitlabSchema.object_from_id(id,
+ expected_type: ::ProtectedBranch))
+ raise_resource_not_available_error! unless protected_branch
+
+ project = authorized_find!(project_path)
+
+ protected_branch = ::ProtectedBranches::UpdateService.new(project, current_user,
+ { name: name }).execute(protected_branch)
+
+ if protected_branch.errors.empty?
+ {
+ branch_rule: ::Projects::BranchRule.new(project, protected_branch),
+ errors: []
+ }
+ else
+ { errors: errors_on_object(protected_branch) }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/catalog/resources/base.rb b/app/graphql/mutations/ci/catalog/resources/base.rb
new file mode 100644
index 00000000000..4ff245d52ec
--- /dev/null
+++ b/app/graphql/mutations/ci/catalog/resources/base.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module Catalog
+ module Resources
+ class Base < BaseMutation
+ argument :project_path, GraphQL::Types::ID,
+ required: true,
+ description: 'Project path belonging to the catalog resource.'
+
+ 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/create.rb b/app/graphql/mutations/ci/catalog/resources/create.rb
index 7f934e101c8..34d60f780ca 100644
--- a/app/graphql/mutations/ci/catalog/resources/create.rb
+++ b/app/graphql/mutations/ci/catalog/resources/create.rb
@@ -4,13 +4,9 @@ module Mutations
module Ci
module Catalog
module Resources
- class Create < BaseMutation
+ class Create < Base
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:)
@@ -23,12 +19,6 @@ module Mutations
errors: errors
}
end
-
- private
-
- def find_object(project_path:)
- Project.find_by_full_path(project_path)
- end
end
end
end
diff --git a/app/graphql/mutations/ci/catalog/resources/destroy.rb b/app/graphql/mutations/ci/catalog/resources/destroy.rb
new file mode 100644
index 00000000000..d33a8c1ef70
--- /dev/null
+++ b/app/graphql/mutations/ci/catalog/resources/destroy.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module Catalog
+ module Resources
+ class Destroy < Base
+ graphql_name 'CatalogResourcesDestroy'
+
+ authorize :add_catalog_resource
+
+ def resolve(project_path:)
+ project = authorized_find!(project_path: project_path)
+ catalog_resource = project.catalog_resource
+
+ response = ::Ci::Catalog::Resources::DestroyService.new(project, current_user).execute(catalog_resource)
+
+ errors = response.success? ? [] : [response.message]
+
+ {
+ errors: errors
+ }
+ 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
deleted file mode 100644
index e45e9646147..00000000000
--- a/app/graphql/mutations/ci/catalog/resources/unpublish.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# 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/container_registry/protection/rule/create.rb b/app/graphql/mutations/container_registry/protection/rule/create.rb
index cf8416480a2..5b01d13d8cb 100644
--- a/app/graphql/mutations/container_registry/protection/rule/create.rb
+++ b/app/graphql/mutations/container_registry/protection/rule/create.rb
@@ -18,12 +18,12 @@ module Mutations
required: true,
description: 'Full path of the project where a protection rule is located.'
- argument :container_path_pattern,
+ argument :repository_path_pattern,
GraphQL::Types::String,
required: true,
description:
- 'ContainerRegistryname protected by the protection rule. For example `@my-scope/my-container-*`. ' \
- 'Wildcard character `*` allowed.'
+ 'Container repository path pattern protected by the protection rule. ' \
+ 'For example `my-project/my-container-*`. Wildcard character `*` allowed.'
argument :push_protected_up_to_access_level,
Types::ContainerRegistry::Protection::RuleAccessLevelEnum,
diff --git a/app/graphql/mutations/container_registry/protection/rule/delete.rb b/app/graphql/mutations/container_registry/protection/rule/delete.rb
new file mode 100644
index 00000000000..b1673b7c43e
--- /dev/null
+++ b/app/graphql/mutations/container_registry/protection/rule/delete.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ContainerRegistry
+ module Protection
+ module Rule
+ class Delete < ::Mutations::BaseMutation
+ graphql_name 'DeleteContainerRegistryProtectionRule'
+ description 'Deletes a container registry protection rule. ' \
+ 'Available only when feature flag `container_registry_protected_containers` is enabled.'
+
+ authorize :admin_container_image
+
+ argument :id,
+ ::Types::GlobalIDType[::ContainerRegistry::Protection::Rule],
+ required: true,
+ description: 'Global ID of the container registry protection rule to delete.'
+
+ field :container_registry_protection_rule,
+ Types::ContainerRegistry::Protection::RuleType,
+ null: true,
+ description: 'Container registry protection rule that was deleted successfully.'
+
+ def resolve(id:, **_kwargs)
+ if Feature.disabled?(:container_registry_protected_containers)
+ raise_resource_not_available_error!("'container_registry_protected_containers' feature flag is disabled")
+ end
+
+ container_registry_protection_rule = authorized_find!(id: id)
+
+ response = ::ContainerRegistry::Protection::DeleteRuleService.new(container_registry_protection_rule,
+ current_user: current_user).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/container_registry/protection/rule/update.rb b/app/graphql/mutations/container_registry/protection/rule/update.rb
new file mode 100644
index 00000000000..b4464e5b5f4
--- /dev/null
+++ b/app/graphql/mutations/container_registry/protection/rule/update.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module Mutations
+ module ContainerRegistry
+ module Protection
+ module Rule
+ class Update < ::Mutations::BaseMutation
+ graphql_name 'UpdateContainerRegistryProtectionRule'
+ description 'Updates a container registry protection rule to restrict access to project containers. ' \
+ 'You can prevent users without certain roles from altering containers. ' \
+ 'Available only when feature flag `container_registry_protected_containers` is enabled.'
+
+ authorize :admin_container_image
+
+ argument :id,
+ ::Types::GlobalIDType[::ContainerRegistry::Protection::Rule],
+ required: true,
+ description: 'Global ID of the container registry protection rule to be updated.'
+
+ argument :repository_path_pattern,
+ GraphQL::Types::String,
+ required: false,
+ validates: { allow_blank: false },
+ description:
+ 'Container\'s repository path pattern of the protection rule. ' \
+ 'For example, `my-scope/my-project/container-dev-*`. ' \
+ 'Wildcard character `*` allowed.'
+
+ argument :delete_protected_up_to_access_level,
+ Types::ContainerRegistry::Protection::RuleAccessLevelEnum,
+ required: false,
+ validates: { allow_blank: false },
+ description:
+ 'Maximum GitLab access level prevented from deleting a container. ' \
+ 'For example, `DEVELOPER`, `MAINTAINER`, `OWNER`.'
+
+ argument :push_protected_up_to_access_level,
+ Types::ContainerRegistry::Protection::RuleAccessLevelEnum,
+ required: false,
+ validates: { allow_blank: false },
+ description:
+ 'Maximum GitLab access level prevented from pushing a container. ' \
+ '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(id:, **kwargs)
+ container_registry_protection_rule = authorized_find!(id: id)
+
+ if Feature.disabled?(:container_registry_protected_containers, container_registry_protection_rule.project)
+ raise_resource_not_available_error!("'container_registry_protected_containers' feature flag is disabled")
+ end
+
+ response = ::ContainerRegistry::Protection::UpdateRuleService.new(container_registry_protection_rule,
+ current_user: current_user, params: 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/custom_emoji/destroy.rb b/app/graphql/mutations/custom_emoji/destroy.rb
index 64e3f2ed7d3..0dd989ad841 100644
--- a/app/graphql/mutations/custom_emoji/destroy.rb
+++ b/app/graphql/mutations/custom_emoji/destroy.rb
@@ -29,12 +29,6 @@ module Mutations
custom_emoji: custom_emoji
}
end
-
- private
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::CustomEmoji)
- end
end
end
end
diff --git a/app/graphql/mutations/customer_relations/contacts/create.rb b/app/graphql/mutations/customer_relations/contacts/create.rb
index 5b4063fb89a..e3a8eba14c8 100644
--- a/app/graphql/mutations/customer_relations/contacts/create.rb
+++ b/app/graphql/mutations/customer_relations/contacts/create.rb
@@ -43,10 +43,6 @@ module Mutations
result = ::CustomerRelations::Contacts::CreateService.new(group: group, current_user: current_user, params: args).execute
{ contact: result.payload, errors: result.errors }
end
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Group)
- end
end
end
end
diff --git a/app/graphql/mutations/customer_relations/organizations/create.rb b/app/graphql/mutations/customer_relations/organizations/create.rb
index 43c50a9fb30..9be66830640 100644
--- a/app/graphql/mutations/customer_relations/organizations/create.rb
+++ b/app/graphql/mutations/customer_relations/organizations/create.rb
@@ -41,10 +41,6 @@ module Mutations
result = ::CustomerRelations::Organizations::CreateService.new(group: group, current_user: current_user, params: args).execute
{ organization: result.payload, errors: result.errors }
end
-
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Group)
- end
end
end
end
diff --git a/app/graphql/mutations/namespace/package_settings/update.rb b/app/graphql/mutations/namespace/package_settings/update.rb
index 97c16ee79fe..813c5687642 100644
--- a/app/graphql/mutations/namespace/package_settings/update.rb
+++ b/app/graphql/mutations/namespace/package_settings/update.rb
@@ -81,6 +81,11 @@ module Mutations
required: false,
description: copy_field_description(Types::Namespace::PackageSettingsType, :lock_pypi_package_requests_forwarding)
+ argument :nuget_symbol_server_enabled,
+ GraphQL::Types::Boolean,
+ required: false,
+ description: copy_field_description(Types::Namespace::PackageSettingsType, :nuget_symbol_server_enabled)
+
field :package_settings,
Types::Namespace::PackageSettingsType,
null: true,
diff --git a/app/graphql/mutations/organizations/base.rb b/app/graphql/mutations/organizations/base.rb
new file mode 100644
index 00000000000..112eb12f0d7
--- /dev/null
+++ b/app/graphql/mutations/organizations/base.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Organizations
+ class Base < BaseMutation
+ field :organization,
+ ::Types::Organizations::OrganizationType,
+ null: true,
+ description: 'Organization after mutation.'
+
+ argument :description, GraphQL::Types::String,
+ required: false,
+ description: 'Description of the organization.'
+
+ argument :avatar, ApolloUploadServer::Upload,
+ required: false,
+ description: 'Avatar for the organization.'
+ end
+ end
+end
diff --git a/app/graphql/mutations/organizations/create.rb b/app/graphql/mutations/organizations/create.rb
index 0d1b204a4c1..e64c964e8b2 100644
--- a/app/graphql/mutations/organizations/create.rb
+++ b/app/graphql/mutations/organizations/create.rb
@@ -2,16 +2,11 @@
module Mutations
module Organizations
- class Create < BaseMutation
+ class Create < Base
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.'
@@ -28,7 +23,7 @@ module Mutations
params: args
).execute
- { organization: result.payload, errors: result.errors }
+ { organization: result.payload[:organization], errors: result.errors }
end
end
end
diff --git a/app/graphql/mutations/organizations/update.rb b/app/graphql/mutations/organizations/update.rb
new file mode 100644
index 00000000000..929f2605735
--- /dev/null
+++ b/app/graphql/mutations/organizations/update.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Organizations
+ class Update < Base
+ graphql_name 'OrganizationUpdate'
+
+ authorize :admin_organization
+
+ argument :id,
+ Types::GlobalIDType[::Organizations::Organization],
+ required: true,
+ description: 'ID of the organization to mutate.'
+
+ argument :name, GraphQL::Types::String,
+ required: false,
+ description: 'Name for the organization.'
+
+ argument :path, GraphQL::Types::String,
+ required: false,
+ description: 'Path for the organization.'
+
+ def resolve(id:, **args)
+ organization = authorized_find!(id: id)
+
+ result = ::Organizations::UpdateService.new(
+ organization,
+ current_user: current_user,
+ params: args
+ ).execute
+
+ { organization: result.payload[:organization], errors: result.errors }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/packages/protection/rule/update.rb b/app/graphql/mutations/packages/protection/rule/update.rb
new file mode 100644
index 00000000000..dc1f78e6822
--- /dev/null
+++ b/app/graphql/mutations/packages/protection/rule/update.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Packages
+ module Protection
+ module Rule
+ class Update < ::Mutations::BaseMutation
+ graphql_name 'UpdatePackagesProtectionRule'
+ description 'Updates a package protection rule to restrict access to project packages. ' \
+ 'You can prevent users without certain permissions from altering 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 be updated.'
+
+ argument :package_name_pattern,
+ GraphQL::Types::String,
+ required: false,
+ validates: { allow_blank: false },
+ description:
+ 'Package name protected by the protection rule. For example, `@my-scope/my-package-*`. ' \
+ 'Wildcard character `*` allowed.'
+
+ argument :package_type,
+ Types::Packages::Protection::RulePackageTypeEnum,
+ required: false,
+ validates: { allow_blank: false },
+ description: 'Package type protected by the protection rule. For example, `NPM`.'
+
+ argument :push_protected_up_to_access_level,
+ Types::Packages::Protection::RuleAccessLevelEnum,
+ required: false,
+ validates: { allow_blank: false },
+ description:
+ 'Maximum GitLab access level unable to push a package. For example, `DEVELOPER`, `MAINTAINER`, `OWNER`.'
+
+ field :package_protection_rule,
+ Types::Packages::Protection::RuleType,
+ null: true,
+ description: 'Packages protection rule after mutation.'
+
+ def resolve(id:, **kwargs)
+ package_protection_rule = authorized_find!(id: id)
+
+ if Feature.disabled?(:packages_protected_packages, package_protection_rule.project)
+ raise_resource_not_available_error!("'packages_protected_packages' feature flag is disabled")
+ end
+
+ response = ::Packages::Protection::UpdateRuleService.new(package_protection_rule,
+ current_user: current_user, params: kwargs).execute
+
+ { package_protection_rule: response.payload[:package_protection_rule], errors: response.errors }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/projects/star.rb b/app/graphql/mutations/projects/star.rb
new file mode 100644
index 00000000000..e4b64235c9a
--- /dev/null
+++ b/app/graphql/mutations/projects/star.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Projects
+ class Star < BaseMutation
+ graphql_name 'StarProject'
+
+ authorize :read_project
+
+ argument :project_id,
+ ::Types::GlobalIDType[::Project],
+ required: true,
+ description: 'Full path of the project to star or unstar.'
+
+ argument :starred,
+ GraphQL::Types::Boolean,
+ required: true,
+ description: 'Indicates whether to star or unstar the project.'
+
+ field :count,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Number of stars for the project.'
+
+ def resolve(project_id:, starred:)
+ project = authorized_find!(id: project_id)
+
+ if current_user.starred?(project) != starred
+ current_user.toggle_star(project)
+ project.reset
+ end
+
+ {
+ count: project.star_count
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/saved_replies/base.rb b/app/graphql/mutations/saved_replies/base.rb
index 79761645eb7..d89cb2d06f0 100644
--- a/app/graphql/mutations/saved_replies/base.rb
+++ b/app/graphql/mutations/saved_replies/base.rb
@@ -22,10 +22,6 @@ module Mutations
}
end
end
-
- def find_object(id)
- GitlabSchema.find_by_gid(id)
- end
end
end
end
diff --git a/app/graphql/mutations/saved_replies/destroy.rb b/app/graphql/mutations/saved_replies/destroy.rb
index 655ed9cb798..14223c06d0c 100644
--- a/app/graphql/mutations/saved_replies/destroy.rb
+++ b/app/graphql/mutations/saved_replies/destroy.rb
@@ -12,7 +12,7 @@ module Mutations
description: copy_field_description(Types::SavedReplyType, :id)
def resolve(id:)
- saved_reply = authorized_find!(id)
+ saved_reply = authorized_find!(id: id)
result = ::Users::SavedReplies::DestroyService.new(saved_reply: saved_reply).execute
present_result(result)
end
diff --git a/app/graphql/mutations/saved_replies/update.rb b/app/graphql/mutations/saved_replies/update.rb
index f5dc81614d2..72da66736e9 100644
--- a/app/graphql/mutations/saved_replies/update.rb
+++ b/app/graphql/mutations/saved_replies/update.rb
@@ -20,7 +20,7 @@ module Mutations
description: copy_field_description(Types::SavedReplyType, :content)
def resolve(id:, name:, content:)
- saved_reply = authorized_find!(id)
+ saved_reply = authorized_find!(id: id)
result = ::Users::SavedReplies::UpdateService.new(saved_reply: saved_reply, name: name, content: content).execute
present_result(result)
end
diff --git a/app/graphql/mutations/snippets/base.rb b/app/graphql/mutations/snippets/base.rb
index acaa7b80843..01441926396 100644
--- a/app/graphql/mutations/snippets/base.rb
+++ b/app/graphql/mutations/snippets/base.rb
@@ -10,10 +10,6 @@ module Mutations
private
- def find_object(id:)
- GitlabSchema.object_from_id(id, expected_type: ::Snippet)
- end
-
def authorized_resource?(snippet)
return false if snippet.nil?
diff --git a/app/graphql/mutations/user_preferences/update.rb b/app/graphql/mutations/user_preferences/update.rb
index 16c7b37532c..111bd258775 100644
--- a/app/graphql/mutations/user_preferences/update.rb
+++ b/app/graphql/mutations/user_preferences/update.rb
@@ -5,9 +5,17 @@ module Mutations
class Update < BaseMutation
graphql_name 'UserPreferencesUpdate'
+ NON_NULLABLE_ARGS = [
+ :use_web_ide_extension_marketplace,
+ :visibility_pipeline_id_type
+ ].freeze
+
argument :issues_sort, Types::IssueSortEnum,
required: false,
description: 'Sort order for issue lists.'
+ argument :use_web_ide_extension_marketplace, GraphQL::Types::Boolean,
+ required: false,
+ description: 'Whether Web IDE Extension Marketplace is enabled for the user.'
argument :visibility_pipeline_id_type, Types::VisibilityPipelineIdTypeEnum,
required: false,
description: 'Determines whether the pipeline list shows ID or IID.'
@@ -18,6 +26,7 @@ module Mutations
description: 'User preferences after mutation.'
def resolve(**attributes)
+ attributes.delete_if { |key, value| NON_NULLABLE_ARGS.include?(key) && value.nil? }
user_preferences = current_user.user_preference
user_preferences.update(attributes)
diff --git a/app/graphql/mutations/work_items/convert.rb b/app/graphql/mutations/work_items/convert.rb
index b1936027fdc..c4d4927fde2 100644
--- a/app/graphql/mutations/work_items/convert.rb
+++ b/app/graphql/mutations/work_items/convert.rb
@@ -59,10 +59,6 @@ module Mutations
message = format(_('You are not allowed to change the Work Item type to %{name}.'), name: work_item_type.name)
raise_resource_not_available_error! message
end
-
- def find_object(id:)
- GitlabSchema.find_by_gid(id)
- end
end
end
end
diff --git a/app/graphql/mutations/work_items/delete_task.rb b/app/graphql/mutations/work_items/delete_task.rb
deleted file mode 100644
index b13d7e2e3bf..00000000000
--- a/app/graphql/mutations/work_items/delete_task.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-# frozen_string_literal: true
-
-module Mutations
- module WorkItems
- class DeleteTask < BaseMutation
- graphql_name 'WorkItemDeleteTask'
-
- description "Deletes a task in a work item's description."
-
- authorize :update_work_item
-
- argument :id, ::Types::GlobalIDType[::WorkItem],
- required: true,
- description: 'Global ID of the work item.'
- argument :lock_version, GraphQL::Types::Int,
- required: true,
- description: 'Current lock version of the work item containing the task in the description.'
- argument :task_data, ::Types::WorkItems::DeletedTaskInputType,
- required: true,
- description: 'Arguments necessary to delete a task from a work item\'s description.',
- prepare: ->(attributes, _ctx) { attributes.to_h }
-
- field :work_item, Types::WorkItemType,
- null: true,
- description: 'Updated work item.'
-
- def resolve(id:, lock_version:, task_data:)
- work_item = authorized_find!(id: id)
- task_data[:task] = authorized_find_task!(task_data[:id])
-
- result = ::WorkItems::DeleteTaskService.new(
- work_item: work_item,
- current_user: current_user,
- lock_version: lock_version,
- task_params: task_data
- ).execute
-
- response = { errors: result.errors }
- response[:work_item] = work_item if result.success?
-
- response
- end
-
- private
-
- def authorized_find_task!(task_id)
- task = ::Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(task_id))
-
- if current_user.can?(:delete_work_item, task)
- task
- else
- # Fail early if user cannot delete task
- raise_resource_not_available_error!
- end
- end
- end
- end
-end
diff --git a/app/graphql/queries/snippet/snippet.query.graphql b/app/graphql/queries/snippet/snippet.query.graphql
index 8712a6f4b01..8a2b314b994 100644
--- a/app/graphql/queries/snippet/snippet.query.graphql
+++ b/app/graphql/queries/snippet/snippet.query.graphql
@@ -13,6 +13,7 @@ query GetSnippetQuery($ids: [SnippetID!]) {
webUrl
httpUrlToRepo
sshUrlToRepo
+ hidden
blobs {
__typename
hasUnretrievableBlobs
diff --git a/app/graphql/resolvers/analytics/cycle_analytics/base_count_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/base_count_resolver.rb
index 82d38ff89d9..565638903e8 100644
--- a/app/graphql/resolvers/analytics/cycle_analytics/base_count_resolver.rb
+++ b/app/graphql/resolvers/analytics/cycle_analytics/base_count_resolver.rb
@@ -32,6 +32,25 @@ module Resolvers
super
end
+
+ # :project level: no customization, returning the original resolver
+ # :group level: add the project_ids argument
+ def self.[](context = :project)
+ case context
+ when :project
+ self
+ when :group
+ Class.new(self) do
+ argument :project_ids, [GraphQL::Types::ID],
+ required: false,
+ description: 'Project IDs within the group hierarchy.'
+
+ define_method :finder_params do
+ { group_id: object.id, include_subgroups: true }
+ end
+ end
+ end
+ end
end
end
end
diff --git a/app/graphql/resolvers/analytics/cycle_analytics/base_issue_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/base_issue_resolver.rb
index 768265752d5..587077d7fd4 100644
--- a/app/graphql/resolvers/analytics/cycle_analytics/base_issue_resolver.rb
+++ b/app/graphql/resolvers/analytics/cycle_analytics/base_issue_resolver.rb
@@ -25,25 +25,6 @@ module Resolvers
def finder_params
{ project_id: object.project.id }
end
-
- # :project level: no customization, returning the original resolver
- # :group level: add the project_ids argument
- def self.[](context = :project)
- case context
- when :project
- self
- when :group
- Class.new(self) do
- argument :project_ids, [GraphQL::Types::ID],
- required: false,
- description: 'Project IDs within the group hierarchy.'
-
- define_method :finder_params do
- { group_id: object.id, include_subgroups: true }
- end
- end
- end
- end
end
end
end
diff --git a/app/graphql/resolvers/analytics/cycle_analytics/base_merge_request_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/base_merge_request_resolver.rb
new file mode 100644
index 00000000000..81b6f1f4e23
--- /dev/null
+++ b/app/graphql/resolvers/analytics/cycle_analytics/base_merge_request_resolver.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Analytics
+ module CycleAnalytics
+ class BaseMergeRequestResolver < BaseCountResolver
+ type Types::Analytics::CycleAnalytics::MetricType, null: true
+
+ argument :assignee_usernames, [GraphQL::Types::String],
+ required: false,
+ description: 'Usernames of users assigned to the merge request.'
+
+ argument :author_username, GraphQL::Types::String,
+ required: false,
+ description: 'Username of the author of the merge request.'
+
+ argument :milestone_title, GraphQL::Types::String,
+ required: false,
+ description: 'Milestone applied to the merge request.'
+
+ argument :label_names, [GraphQL::Types::String],
+ required: false,
+ description: 'Labels applied to the merge request.'
+ end
+ end
+ end
+end
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 2d722b02bf1..95080110699 100644
--- a/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb
+++ b/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb
@@ -28,22 +28,6 @@ module Resolvers
finder.execute.count
end
-
- # :project level: no customization, returning the original resolver
- # :group level: add the project_ids argument
- def self.[](context = :project)
- case context
- when :project
- self
- when :group
- Class.new(self) do
- argument :project_ids, [GraphQL::Types::ID],
- required: false,
- description: 'Project IDs within the group hierarchy.'
- end
-
- end
- end
end
end
end
diff --git a/app/graphql/resolvers/analytics/cycle_analytics/stages_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/stages_resolver.rb
new file mode 100644
index 00000000000..7f82c3dbada
--- /dev/null
+++ b/app/graphql/resolvers/analytics/cycle_analytics/stages_resolver.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Analytics
+ module CycleAnalytics
+ class StagesResolver < BaseResolver
+ type [Types::Analytics::CycleAnalytics::ValueStreams::StageType], null: true
+
+ def resolve
+ list_stages({ value_stream: object })
+ end
+
+ private
+
+ def list_stages(list_service_params)
+ ::Analytics::CycleAnalytics::Stages::ListService.new(
+ parent: namespace,
+ current_user: current_user,
+ params: list_service_params
+ ).execute[:stages]
+ end
+
+ def namespace
+ object.project.project_namespace
+ end
+ end
+ end
+ end
+end
+
+Resolvers::Analytics::CycleAnalytics::StagesResolver.prepend_mod
diff --git a/app/graphql/resolvers/analytics/cycle_analytics/value_streams_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/value_streams_resolver.rb
new file mode 100644
index 00000000000..f3e3da86169
--- /dev/null
+++ b/app/graphql/resolvers/analytics/cycle_analytics/value_streams_resolver.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Analytics
+ module CycleAnalytics
+ class ValueStreamsResolver < BaseResolver
+ type Types::Analytics::CycleAnalytics::ValueStreamType.connection_type, null: true
+
+ def resolve
+ # FOSS only have default value stream available
+ [
+ ::Analytics::CycleAnalytics::ValueStream.build_default_value_stream(object.project_namespace)
+ ]
+ end
+ end
+ end
+ end
+end
+
+Resolvers::Analytics::CycleAnalytics::ValueStreamsResolver.prepend_mod
diff --git a/app/graphql/resolvers/blame_resolver.rb b/app/graphql/resolvers/blame_resolver.rb
index f8b985e6582..d411d025255 100644
--- a/app/graphql/resolvers/blame_resolver.rb
+++ b/app/graphql/resolvers/blame_resolver.rb
@@ -14,7 +14,7 @@ module Resolvers
argument :to_line, GraphQL::Types::Int,
required: false,
default_value: 1,
- description: 'Range ending on the line. Cannot be less than 1 or less than `to_line`.'
+ description: 'Range ending on the line. Cannot be smaller than `from_line` or greater than `from_line` + 100.'
alias_method :blob, :object
@@ -48,15 +48,18 @@ module Resolvers
end
def validate_line_params!(args)
- if args[:from_line] <= 0 || args[:to_line] <= 0
- raise Gitlab::Graphql::Errors::ArgumentError,
- '`from_line` and `to_line` must be greater than or equal to 1'
- end
+ raise_greater_than_one unless args[:from_line] >= 1
+ raise_greater_than_one unless args[:to_line] >= 1
- return unless args[:from_line] > args[:to_line]
+ return unless args[:to_line] < args[:from_line] || args[:to_line] >= args[:from_line] + 100
raise Gitlab::Graphql::Errors::ArgumentError,
- '`to_line` must be greater than or equal to `from_line`'
+ '`to_line` must be greater than or equal to `from_line` and smaller than `from_line` + 100'
+ end
+
+ def raise_greater_than_one
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ '`from_line` and `to_line` must be greater than or equal to 1'
end
end
end
diff --git a/app/graphql/resolvers/ci/catalog/resource_resolver.rb b/app/graphql/resolvers/ci/catalog/resource_resolver.rb
index 4b722bd3ec7..3ea730a5768 100644
--- a/app/graphql/resolvers/ci/catalog/resource_resolver.rb
+++ b/app/graphql/resolvers/ci/catalog/resource_resolver.rb
@@ -6,8 +6,6 @@ module Resolvers
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],
@@ -28,19 +26,15 @@ module Resolvers
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
+ catalog_resource = if full_path.present?
+ ::Ci::Catalog::Listing.new(current_user).find_resource(full_path: full_path)
+ else
+ ::Ci::Catalog::Listing.new(current_user).find_resource(id: id.model_id)
+ end
- project.catalog_resource
- else
- catalog_resource = ::Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(id))
- authorize!(catalog_resource&.project)
+ raise_resource_not_available_error! unless catalog_resource
- catalog_resource
- end
+ catalog_resource
end
end
end
diff --git a/app/graphql/resolvers/ci/catalog/resources/versions_resolver.rb b/app/graphql/resolvers/ci/catalog/resources/versions_resolver.rb
new file mode 100644
index 00000000000..9332076a493
--- /dev/null
+++ b/app/graphql/resolvers/ci/catalog/resources/versions_resolver.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ module Catalog
+ module Resources
+ class VersionsResolver < BaseResolver
+ type Types::Ci::Catalog::Resources::VersionType.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
+
+ argument :sort, Types::Ci::Catalog::Resources::VersionSortEnum,
+ required: false,
+ description: 'Sort versions by given criteria.'
+
+ def resolve(sort: nil)
+ ::Ci::Catalog::Resources::VersionsFinder.new(object, current_user, sort: sort).execute
+ 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
index c6904dcd7f6..ec415cf25c1 100644
--- a/app/graphql/resolvers/ci/catalog/resources_resolver.rb
+++ b/app/graphql/resolvers/ci/catalog/resources_resolver.rb
@@ -27,17 +27,13 @@ module Resolvers
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
+ 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, scope: scope)
+ )
end
private
diff --git a/app/graphql/resolvers/ci/catalog/versions_resolver.rb b/app/graphql/resolvers/ci/catalog/versions_resolver.rb
deleted file mode 100644
index 046adeb7a67..00000000000
--- a/app/graphql/resolvers/ci/catalog/versions_resolver.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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/runner_groups_resolver.rb b/app/graphql/resolvers/ci/runner_groups_resolver.rb
index c1d9bcbb9bb..2928631c705 100644
--- a/app/graphql/resolvers/ci/runner_groups_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_groups_resolver.rb
@@ -6,7 +6,7 @@ module Resolvers
include Gitlab::Graphql::Authorize::AuthorizeResource
include ResolvesGroups
- type 'Types::GroupConnection', null: true
+ type Types::GroupType.connection_type, null: true
authorize :read_runner
authorizes_object!
diff --git a/app/graphql/resolvers/container_repository_tags_resolver.rb b/app/graphql/resolvers/container_repository_tags_resolver.rb
index bc5006ae06c..50adf98fa07 100644
--- a/app/graphql/resolvers/container_repository_tags_resolver.rb
+++ b/app/graphql/resolvers/container_repository_tags_resolver.rb
@@ -17,7 +17,7 @@ module Resolvers
alias_method :container_repository, :object
def resolve(sort:, **filters)
- if container_repository.migrated? && Feature.enabled?(:use_repository_list_tags_on_graphql, container_repository.project)
+ if container_repository.migrated?
page_size = [filters[:first], filters[:last]].map(&:to_i).max
result = container_repository.tags_page(
diff --git a/app/graphql/resolvers/custom_emoji_resolver.rb b/app/graphql/resolvers/custom_emoji_resolver.rb
new file mode 100644
index 00000000000..1e39fafe486
--- /dev/null
+++ b/app/graphql/resolvers/custom_emoji_resolver.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class CustomEmojiResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ authorizes_object!
+
+ authorize :read_custom_emoji
+
+ argument :include_ancestor_groups,
+ GraphQL::Types::Boolean,
+ required: false,
+ default_value: false,
+ description: 'Includes custom emoji from parent groups.'
+
+ type Types::CustomEmojiType, null: true
+
+ def resolve(**args)
+ Groups::CustomEmojiFinder.new(object, args).execute
+ end
+ end
+end
diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb
index 5a6a3d678b9..360781806a4 100644
--- a/app/graphql/resolvers/group_issues_resolver.rb
+++ b/app/graphql/resolvers/group_issues_resolver.rb
@@ -10,7 +10,7 @@ module Resolvers
include GroupIssuableResolver
before_connection_authorization do |nodes, _|
- projects = nodes.map(&:project)
+ projects = nodes.filter_map(&:project)
ActiveRecord::Associations::Preloader.new(records: projects, associations: project_associations).call
end
diff --git a/app/graphql/resolvers/group_milestones_resolver.rb b/app/graphql/resolvers/group_milestones_resolver.rb
index 9242be7f684..d9a664b6ec2 100644
--- a/app/graphql/resolvers/group_milestones_resolver.rb
+++ b/app/graphql/resolvers/group_milestones_resolver.rb
@@ -2,6 +2,8 @@
module Resolvers
class GroupMilestonesResolver < MilestonesResolver
+ include ::API::Concerns::Milestones::GroupProjectParams
+
argument :include_ancestors, GraphQL::Types::Boolean,
required: false,
description: 'Include milestones from all parent groups.'
@@ -14,36 +16,7 @@ module Resolvers
private
def parent_id_parameters(args)
- include_ancestors = args[:include_ancestors].present?
- include_descendants = args[:include_descendants].present?
- return { group_ids: parent.id } unless include_ancestors || include_descendants
-
- group_ids = if include_ancestors && include_descendants
- parent.self_and_hierarchy
- elsif include_ancestors
- parent.self_and_ancestors
- else
- parent.self_and_descendants
- end
-
- project_ids = if include_descendants
- group_projects.with_issues_or_mrs_available_for_user(current_user)
- else
- nil
- end
-
- {
- group_ids: group_ids.public_or_visible_to_user(current_user).select(:id),
- project_ids: project_ids
- }
- end
-
- def group_projects
- GroupProjectsFinder.new(
- group: parent,
- current_user: current_user,
- options: { include_subgroups: true }
- ).execute
+ group_finder_params(parent, args)
end
def preloads
diff --git a/app/graphql/resolvers/groups_resolver.rb b/app/graphql/resolvers/groups_resolver.rb
index 902b5279364..f31f7368eeb 100644
--- a/app/graphql/resolvers/groups_resolver.rb
+++ b/app/graphql/resolvers/groups_resolver.rb
@@ -4,7 +4,7 @@ module Resolvers
class GroupsResolver < BaseResolver
include ResolvesGroups
- type "Types::GroupConnection", null: true
+ type Types::GroupType.connection_type, null: true
argument :search, GraphQL::Types::String,
required: false,
diff --git a/app/graphql/resolvers/issues/base_parent_resolver.rb b/app/graphql/resolvers/issues/base_parent_resolver.rb
index 78ef4132baf..6b3426c5538 100644
--- a/app/graphql/resolvers/issues/base_parent_resolver.rb
+++ b/app/graphql/resolvers/issues/base_parent_resolver.rb
@@ -15,8 +15,7 @@ module Resolvers
raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE
}
- # see app/graphql/types/issue_connection.rb
- type 'Types::IssueConnection', null: true
+ type Types::IssueType.connection_type, null: true
def resolve_with_lookahead(**args)
return Issue.none if resource_parent.nil?
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index bc0e7334303..1b43903a508 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -21,8 +21,7 @@ module Resolvers
raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE
}
- # see app/graphql/types/issue_connection.rb
- type 'Types::IssueConnection', null: true
+ type Types::IssueType.connection_type, null: true
before_connection_authorization do |nodes, current_user|
projects = nodes.map(&:project)
diff --git a/app/graphql/resolvers/kas/agent_connections_resolver.rb b/app/graphql/resolvers/kas/agent_connections_resolver.rb
index cf1a47aac75..282efb4161e 100644
--- a/app/graphql/resolvers/kas/agent_connections_resolver.rb
+++ b/app/graphql/resolvers/kas/agent_connections_resolver.rb
@@ -12,8 +12,8 @@ module Resolvers
def resolve
return [] unless can_read_connected_agents?
- BatchLoader::GraphQL.for(agent.id).batch(key: project, default_value: []) do |agent_ids, loader|
- agents = get_connected_agents.group_by(&:agent_id).slice(*agent_ids)
+ BatchLoader::GraphQL.for(agent.id).batch(default_value: []) do |agent_ids, loader|
+ agents = get_connected_agents(agent_ids).group_by(&:agent_id)
agents.each do |agent_id, connections|
loader.call(agent_id, connections)
@@ -27,8 +27,8 @@ module Resolvers
current_user.can?(:admin_cluster, project)
end
- def get_connected_agents
- kas_client.get_connected_agents(project: project)
+ def get_connected_agents(agent_ids)
+ kas_client.get_connected_agents_by_agent_ids(agent_ids: agent_ids)
rescue GRPC::BadStatus, Gitlab::Kas::Client::ConfigurationError => e
raise Gitlab::Graphql::Errors::ResourceNotAvailable, e.class.name
end
diff --git a/app/graphql/resolvers/ml/model_detail_resolver.rb b/app/graphql/resolvers/ml/model_detail_resolver.rb
new file mode 100644
index 00000000000..01c025c1d8a
--- /dev/null
+++ b/app/graphql/resolvers/ml/model_detail_resolver.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ml
+ class ModelDetailResolver < Resolvers::BaseResolver
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+
+ type ::Types::Ml::ModelType, null: true
+
+ argument :id, ::Types::GlobalIDType[::Ml::Model],
+ required: true,
+ description: 'ID of the model.'
+
+ def resolve(id:)
+ Gitlab::Graphql::Lazy.with_value(find_object(id: id)) do |ml_model|
+ ml_model if current_user.can?(:read_model_registry, ml_model&.project)
+ end
+ end
+
+ private
+
+ def find_object(id:)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/namespaces/work_item_state_counts_resolver.rb b/app/graphql/resolvers/namespaces/work_item_state_counts_resolver.rb
new file mode 100644
index 00000000000..099b509f77f
--- /dev/null
+++ b/app/graphql/resolvers/namespaces/work_item_state_counts_resolver.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Namespaces
+ class WorkItemStateCountsResolver < WorkItemsResolver
+ type Types::WorkItemStateCountsType, null: true
+
+ def ready?(**args)
+ # The search filter is not supported for work times at the namespace level.
+ # See https://gitlab.com/gitlab-org/gitlab/-/work_items/393126
+ if args[:search]
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'Searching is not available for work items at the namespace level yet'
+ end
+
+ super
+ end
+
+ def resolve(**args)
+ return if resource_parent.nil?
+
+ Gitlab::IssuablesCountForState.new(
+ finder(args),
+ resource_parent,
+ store_in_redis_cache: true
+ )
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/organizations/groups_resolver.rb b/app/graphql/resolvers/organizations/groups_resolver.rb
index 0f50713b9b4..552a14fc13f 100644
--- a/app/graphql/resolvers/organizations/groups_resolver.rb
+++ b/app/graphql/resolvers/organizations/groups_resolver.rb
@@ -25,12 +25,18 @@ module Resolvers
private
+ alias_method :organization, :object
+
def resolve_groups(**args)
- return Group.none if Feature.disabled?(:resolve_organization_groups, context[:current_user])
+ return Group.none if Feature.disabled?(:resolve_organization_groups, current_user)
+
+ extra_args = { organization: organization, include_ancestors: false, all_available: false }
+ groups = GroupsFinder.new(current_user, args.merge(extra_args)).execute
- ::Organizations::GroupsFinder
- .new(organization: object, current_user: context[:current_user], params: args)
- .execute
+ args[:sort] ||= { field: 'name', direction: :asc }
+ field = args[:sort][:field]
+ direction = args[:sort][:direction]
+ groups.sort_by_attribute("#{field}_#{direction}")
end
end
end
diff --git a/app/graphql/resolvers/project_milestones_resolver.rb b/app/graphql/resolvers/project_milestones_resolver.rb
index cb4e9a5cdf7..0a078836a6a 100644
--- a/app/graphql/resolvers/project_milestones_resolver.rb
+++ b/app/graphql/resolvers/project_milestones_resolver.rb
@@ -2,6 +2,8 @@
module Resolvers
class ProjectMilestonesResolver < MilestonesResolver
+ include ::API::Concerns::Milestones::GroupProjectParams
+
argument :include_ancestors, GraphQL::Types::Boolean,
required: false,
description: "Also return milestones in the project's parent group and its ancestors."
@@ -11,12 +13,7 @@ module Resolvers
private
def parent_id_parameters(args)
- return { project_ids: parent.id } unless args[:include_ancestors].present? && parent.group.present?
-
- {
- group_ids: parent.group.self_and_ancestors.select(:id),
- project_ids: parent.id
- }
+ project_finder_params(parent, args)
end
end
end
diff --git a/app/graphql/resolvers/timelog_resolver.rb b/app/graphql/resolvers/timelog_resolver.rb
index 4f52db6801d..c2be582742b 100644
--- a/app/graphql/resolvers/timelog_resolver.rb
+++ b/app/graphql/resolvers/timelog_resolver.rb
@@ -3,6 +3,7 @@
module Resolvers
class TimelogResolver < BaseResolver
include LooksAhead
+ include Gitlab::Graphql::Authorize::AuthorizeResource
type ::Types::TimelogType.connection_type, null: false
@@ -42,21 +43,30 @@ module Resolvers
def resolve_with_lookahead(**args)
validate_args!(object, args)
- timelogs = object&.timelogs || Timelog.all
-
args = parse_datetime_args(args)
- timelogs = apply_user_filter(timelogs, args)
- timelogs = apply_project_filter(timelogs, args)
- timelogs = apply_time_filter(timelogs, args)
- timelogs = apply_group_filter(timelogs, args)
- timelogs = apply_sorting(timelogs, args)
+ timelogs = Timelogs::TimelogsFinder.new(object, finder_params(args)).execute
apply_lookahead(timelogs)
+ rescue ArgumentError => e
+ raise_argument_error(e.message)
+ rescue ActiveRecord::RecordNotFound
+ raise_resource_not_available_error!
end
private
+ def finder_params(args)
+ {
+ username: args[:username],
+ start_time: args[:start_time],
+ end_time: args[:end_time],
+ group_id: args[:group_id]&.model_id,
+ project_id: args[:project_id]&.model_id,
+ sort: args[:sort]
+ }
+ end
+
def preloads
{
note: [:note]
@@ -95,58 +105,6 @@ module Resolvers
args[:start_time] && args[:end_time]
end
- def validate_time_difference!(args)
- return unless end_time_before_start_time?(args)
-
- raise_argument_error('Start argument must be before End argument')
- end
-
- def end_time_before_start_time?(args)
- times_provided?(args) && args[:end_time] < args[:start_time]
- end
-
- def apply_project_filter(timelogs, args)
- return timelogs unless args[:project_id]
-
- timelogs.in_project(args[:project_id].model_id)
- end
-
- def apply_group_filter(timelogs, args)
- return timelogs unless args[:group_id]
-
- group = Group.find_by_id(args[:group_id].model_id)
- timelogs.in_group(group)
- end
-
- def apply_user_filter(timelogs, args)
- return timelogs unless args[:username]
-
- user = UserFinder.new(args[:username]).find_by_username
- timelogs.for_user(user)
- end
-
- def apply_time_filter(timelogs, args)
- return timelogs unless args[:start_time] || args[:end_time]
-
- validate_time_difference!(args)
-
- if args[:start_time]
- timelogs = timelogs.at_or_after(args[:start_time])
- end
-
- if args[:end_time]
- timelogs = timelogs.at_or_before(args[:end_time])
- end
-
- timelogs
- end
-
- def apply_sorting(timelogs, args)
- return timelogs unless args[:sort]
-
- timelogs.sort_by_field(args[:sort])
- end
-
def raise_argument_error(message)
raise Gitlab::Graphql::Errors::ArgumentError, message
end
diff --git a/app/graphql/resolvers/users/frecent_groups_resolver.rb b/app/graphql/resolvers/users/frecent_groups_resolver.rb
index 2fc757e31ab..f6b43297898 100644
--- a/app/graphql/resolvers/users/frecent_groups_resolver.rb
+++ b/app/graphql/resolvers/users/frecent_groups_resolver.rb
@@ -10,12 +10,6 @@ module Resolvers
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
diff --git a/app/graphql/resolvers/users/frecent_projects_resolver.rb b/app/graphql/resolvers/users/frecent_projects_resolver.rb
index 397d4ca0cfd..9508195800a 100644
--- a/app/graphql/resolvers/users/frecent_projects_resolver.rb
+++ b/app/graphql/resolvers/users/frecent_projects_resolver.rb
@@ -10,10 +10,6 @@ module Resolvers
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
diff --git a/app/graphql/resolvers/work_item_references_resolver.rb b/app/graphql/resolvers/work_item_references_resolver.rb
new file mode 100644
index 00000000000..4aa071519db
--- /dev/null
+++ b/app/graphql/resolvers/work_item_references_resolver.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class WorkItemReferencesResolver < BaseResolver
+ prepend ::WorkItems::LookAheadPreloads
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ REFERENCES_LIMIT = 10
+
+ authorize :read_work_item
+
+ type ::Types::WorkItemType.connection_type, null: true
+
+ argument :context_namespace_path, GraphQL::Types::ID,
+ required: false,
+ description: 'Full path of the context namespace (project or group).'
+
+ argument :refs, [GraphQL::Types::String], required: true,
+ description: 'Work item references. Can be either a short reference or URL.'
+
+ def ready?(**args)
+ if args[:refs].size > REFERENCES_LIMIT
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ format(
+ _('Number of references exceeds the limit. ' \
+ 'Please provide no more than %{refs_limit} references at the same time.'),
+ refs_limit: REFERENCES_LIMIT
+ )
+ end
+
+ super
+ end
+
+ def resolve_with_lookahead(context_namespace_path: nil, refs: [])
+ return WorkItem.none if refs.empty?
+
+ @container = authorized_find!(context_namespace_path)
+ # Only ::Project is supported at the moment, future iterations will include ::Group.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/432555
+ return WorkItem.none if container.is_a?(::Group)
+
+ apply_lookahead(find_work_items(refs))
+ end
+
+ private
+
+ attr_reader :container
+
+ # rubocop: disable CodeReuse/ActiveRecord -- #references is not an ActiveRecord method
+ def find_work_items(references)
+ links, short_references = references.partition { |r| r.include?('/work_items/') }
+
+ item_ids = references_extractor(short_references).references(:issue, ids_only: true)
+ item_ids << references_extractor(links).references(:work_item, ids_only: true) if links.any?
+
+ WorkItem.id_in(item_ids.flatten)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def references_extractor(refs)
+ extractor = ::Gitlab::ReferenceExtractor.new(container, context[:current_user])
+ extractor.analyze(refs.join(' '), {})
+
+ extractor
+ end
+
+ def find_object(full_path)
+ Routable.find_by_full_path(full_path)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/work_item_state_counts_resolver.rb b/app/graphql/resolvers/work_item_state_counts_resolver.rb
new file mode 100644
index 00000000000..93551a57694
--- /dev/null
+++ b/app/graphql/resolvers/work_item_state_counts_resolver.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class WorkItemStateCountsResolver < WorkItemsResolver
+ type Types::WorkItemStateCountsType, null: true
+
+ def resolve(**args)
+ return if resource_parent.nil?
+
+ work_item_finder = finder(prepare_finder_params(args))
+ work_item_finder.parent_param = resource_parent
+
+ Gitlab::IssuablesCountForState.new(work_item_finder, resource_parent)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/work_items/ancestors_resolver.rb b/app/graphql/resolvers/work_items/ancestors_resolver.rb
index 33adbfc9c86..efd01700a31 100644
--- a/app/graphql/resolvers/work_items/ancestors_resolver.rb
+++ b/app/graphql/resolvers/work_items/ancestors_resolver.rb
@@ -38,6 +38,8 @@ module Resolvers
end
def preload_resource_parents(work_items)
+ return unless current_user
+
projects = work_items.filter_map(&:project)
namespaces = work_items.filter_map(&:namespace)
group_namespaces = namespaces.select { |n| n.type == ::Group.sti_name }
@@ -46,7 +48,11 @@ module Resolvers
return unless projects.any?
::Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
- ::Preloaders::GroupPolicyPreloader.new(projects.filter_map(&:namespace), current_user).execute
+
+ if group_namespaces.any?
+ ::Preloaders::GroupPolicyPreloader.new(projects.filter_map(&:namespace), current_user).execute
+ end
+
ActiveRecord::Associations::Preloader.new(records: projects, associations: [:namespace]).call
end
diff --git a/app/graphql/resolvers/work_items/types_resolver.rb b/app/graphql/resolvers/work_items/types_resolver.rb
index 2508125d392..11db096b5ba 100644
--- a/app/graphql/resolvers/work_items/types_resolver.rb
+++ b/app/graphql/resolvers/work_items/types_resolver.rb
@@ -3,6 +3,8 @@
module Resolvers
module WorkItems
class TypesResolver < BaseResolver
+ include LooksAhead
+
type Types::WorkItems::TypeType.connection_type, null: true
argument :taskable, ::GraphQL::Types::Boolean,
@@ -10,13 +12,29 @@ module Resolvers
description: 'If `true`, only taskable work item types will be returned.' \
' Argument is experimental and can be removed in the future without notice.'
- def resolve(taskable: nil)
+ def resolve_with_lookahead(taskable: nil)
+ context.scoped_set!(:resource_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
base_scope = base_scope.by_type(:task) if taskable
- base_scope.order_by_name_asc
+ apply_lookahead(base_scope.order_by_name_asc)
+ end
+
+ private
+
+ def preloads
+ {
+ widget_definitions: :enabled_widget_definitions
+ }
+ end
+
+ def nested_preloads
+ {
+ widget_definitions: { allowed_child_types: :allowed_child_types_by_name }
+ }
end
end
end
diff --git a/app/graphql/types/abuse_report_type.rb b/app/graphql/types/abuse_report_type.rb
index 2532530cfa9..dc40800af94 100644
--- a/app/graphql/types/abuse_report_type.rb
+++ b/app/graphql/types/abuse_report_type.rb
@@ -10,8 +10,6 @@ module Types
authorize :read_abuse_report
- expose_permissions Types::PermissionTypes::AbuseReport
-
field :id, Types::GlobalIDType[::AbuseReport],
null: false, description: 'Global ID of the abuse report.'
diff --git a/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb b/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb
index 16ce9b82718..900d2873789 100644
--- a/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb
+++ b/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb
@@ -26,6 +26,11 @@ module Types
null: true,
description: 'Project the value stream belongs to, returns empty if it belongs to a group.',
alpha: { milestone: '15.6' }
+
+ field :stages,
+ null: true,
+ resolver: Resolvers::Analytics::CycleAnalytics::StagesResolver,
+ description: 'Value Stream stages.'
end
end
end
diff --git a/app/graphql/types/analytics/cycle_analytics/value_streams/stage_event_enum.rb b/app/graphql/types/analytics/cycle_analytics/value_streams/stage_event_enum.rb
new file mode 100644
index 00000000000..f7fd1121e4a
--- /dev/null
+++ b/app/graphql/types/analytics/cycle_analytics/value_streams/stage_event_enum.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Types
+ module Analytics
+ module CycleAnalytics
+ module ValueStreams
+ class StageEventEnum < BaseEnum
+ graphql_name 'ValueStreamStageEvent'
+ description 'Stage event identifiers'
+
+ Gitlab::Analytics::CycleAnalytics::StageEvents.to_enum.each do |key, value|
+ value(key.to_s.upcase, description: "#{key.to_s.humanize} event.", value: value)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/analytics/cycle_analytics/value_streams/stage_type.rb b/app/graphql/types/analytics/cycle_analytics/value_streams/stage_type.rb
new file mode 100644
index 00000000000..c8fdf8513be
--- /dev/null
+++ b/app/graphql/types/analytics/cycle_analytics/value_streams/stage_type.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Types
+ module Analytics
+ module CycleAnalytics
+ module ValueStreams
+ # rubocop: disable Graphql/AuthorizeTypes -- # Already authorized in parent value stream type.
+ class StageType < BaseObject
+ graphql_name 'ValueStreamStage'
+
+ field :name,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Name of the stage.'
+
+ field :hidden,
+ GraphQL::Types::Boolean,
+ null: false,
+ description: 'Whether the stage is hidden.'
+
+ field :custom,
+ GraphQL::Types::Boolean,
+ null: false,
+ description: 'Whether the stage is customized.'
+
+ field :start_event_identifier,
+ StageEventEnum,
+ null: false,
+ description: 'Start event identifier.'
+
+ field :end_event_identifier,
+ StageEventEnum,
+ null: false,
+ description: 'End event identifier.'
+
+ def start_event_identifier
+ events_enum[object.start_event_identifier]
+ end
+
+ def end_event_identifier
+ events_enum[object.end_event_identifier]
+ end
+
+ def events_enum
+ Gitlab::Analytics::CycleAnalytics::StageEvents.to_enum.with_indifferent_access
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+ end
+ end
+end
+
+Types::Analytics::CycleAnalytics::ValueStreams::StageType.prepend_mod
diff --git a/app/graphql/types/ci/catalog/resource_scope_enum.rb b/app/graphql/types/ci/catalog/resource_scope_enum.rb
index b825c3a7925..728670ba913 100644
--- a/app/graphql/types/ci/catalog/resource_scope_enum.rb
+++ b/app/graphql/types/ci/catalog/resource_scope_enum.rb
@@ -8,6 +8,7 @@ module Types
description 'Values for scoping catalog resources'
value 'ALL', 'All catalog resources visible to the current user.', value: :all
+ value 'NAMESPACES', 'Catalog resources belonging to authorized namespaces of the user.', value: :namespaces
end
end
end
diff --git a/app/graphql/types/ci/catalog/resource_type.rb b/app/graphql/types/ci/catalog/resource_type.rb
index 119313ae52b..44dec23b347 100644
--- a/app/graphql/types/ci/catalog/resource_type.rb
+++ b/app/graphql/types/ci/catalog/resource_type.rb
@@ -32,13 +32,14 @@ module Types
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,
+ field :versions, Types::Ci::Catalog::Resources::VersionType.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,
+ resolver: Resolvers::Ci::Catalog::Resources::VersionsResolver,
alpha: { milestone: '16.2' }
- field :latest_version, Types::ReleaseType, null: true, description: 'Latest version of the catalog resource.',
+ field :latest_version, Types::Ci::Catalog::Resources::VersionType, null: true,
+ description: 'Latest version of the catalog resource.',
alpha: { milestone: '16.1' }
field :latest_released_at, Types::TimeType, null: true,
@@ -49,14 +50,6 @@ module Types
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' }
@@ -73,42 +66,16 @@ module Types
end
def latest_version
- BatchLoader::GraphQL.for(object.project).batch do |projects, loader|
- latest_releases = ReleasesFinder.new(projects, current_user, latest: true).execute
+ BatchLoader::GraphQL.for(object).batch do |catalog_resources, loader|
+ latest_versions = ::Ci::Catalog::Resources::VersionsFinder.new(
+ catalog_resources, current_user, latest: true).execute
- latest_releases.index_by(&:project).each do |project, latest_release|
- loader.call(project, latest_release)
+ latest_versions.index_by(&:catalog_resource).each do |catalog_resource, latest_version|
+ loader.call(catalog_resource, latest_version)
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)
diff --git a/app/graphql/types/ci/catalog/resources/component_type.rb b/app/graphql/types/ci/catalog/resources/component_type.rb
new file mode 100644
index 00000000000..3b4771446cb
--- /dev/null
+++ b/app/graphql/types/ci/catalog/resources/component_type.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Catalog
+ module Resources
+ # rubocop: disable Graphql/AuthorizeTypes -- Authorization is handled by VersionType
+ class ComponentType < BaseObject
+ graphql_name 'CiCatalogResourceComponent'
+
+ field :id, ::Types::GlobalIDType[::Ci::Catalog::Resources::Component], null: false,
+ description: 'ID of the component.',
+ alpha: { milestone: '16.7' }
+
+ field :name, GraphQL::Types::String, null: true,
+ description: 'Name of the component.',
+ alpha: { milestone: '16.7' }
+
+ field :path, GraphQL::Types::String, null: true,
+ description: 'Path used to include the component.',
+ alpha: { milestone: '16.7' }
+
+ field :inputs, [Types::Ci::Catalog::Resources::Components::InputType], null: true,
+ description: 'Inputs for the component.',
+ alpha: { milestone: '16.7' }
+
+ def inputs
+ object.inputs.map do |key, value|
+ {
+ name: key,
+ required: !value&.key?('default'),
+ default: value&.dig('default')
+ }
+ end
+ end
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+ end
+end
diff --git a/app/graphql/types/ci/catalog/resources/components/input_type.rb b/app/graphql/types/ci/catalog/resources/components/input_type.rb
new file mode 100644
index 00000000000..4b20c564ea7
--- /dev/null
+++ b/app/graphql/types/ci/catalog/resources/components/input_type.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Catalog
+ module Resources
+ module Components
+ # rubocop: disable Graphql/AuthorizeTypes -- Authorization hanlded by ComponentType -> VersionType
+ class InputType < BaseObject
+ graphql_name 'CiCatalogResourceComponentInput'
+
+ field :name, GraphQL::Types::String, null: true,
+ description: 'Name of the input.',
+ alpha: { milestone: '16.7' }
+
+ field :default, GraphQL::Types::String, null: true,
+ description: 'Default value for the input.',
+ alpha: { milestone: '16.7' }
+
+ field :required, GraphQL::Types::Boolean, null: true,
+ description: 'Indicates if an input is required.',
+ alpha: { milestone: '16.7' }
+ end
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+ end
+end
diff --git a/app/graphql/types/ci/catalog/resources/version_sort_enum.rb b/app/graphql/types/ci/catalog/resources/version_sort_enum.rb
new file mode 100644
index 00000000000..c5a5f46605a
--- /dev/null
+++ b/app/graphql/types/ci/catalog/resources/version_sort_enum.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Catalog
+ module Resources
+ class VersionSortEnum < Types::ReleaseSortEnum
+ graphql_name 'CiCatalogResourceVersionSort'
+ description 'Values for sorting catalog resource versions'
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/catalog/resources/version_type.rb b/app/graphql/types/ci/catalog/resources/version_type.rb
new file mode 100644
index 00000000000..689f649afc5
--- /dev/null
+++ b/app/graphql/types/ci/catalog/resources/version_type.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module Catalog
+ module Resources
+ # rubocop: disable Graphql/AuthorizeTypes -- Authorization is handled by Ci::Catalog::Resources::VersionsFinder in the resolver.
+ class VersionType < BaseObject
+ graphql_name 'CiCatalogResourceVersion'
+
+ connection_type_class Types::CountableConnectionType
+
+ field :id, ::Types::GlobalIDType[::Ci::Catalog::Resources::Version], null: false,
+ description: 'Global ID of the version.',
+ alpha: { milestone: '16.7' }
+
+ field :created_at, Types::TimeType, null: true, description: 'Timestamp of when the version was created.',
+ alpha: { milestone: '16.7' }
+
+ field :released_at, Types::TimeType, null: true, description: 'Timestamp of when the version was released.',
+ alpha: { milestone: '16.7' }
+
+ field :tag_name, GraphQL::Types::String, null: true, method: :name,
+ description: 'Name of the tag associated with the version.',
+ alpha: { milestone: '16.7' }
+
+ field :tag_path, GraphQL::Types::String, null: true,
+ description: 'Relative web path to the tag associated with the version.',
+ alpha: { milestone: '16.7' }
+
+ field :author, Types::UserType, null: true, description: 'User that created the version.',
+ alpha: { milestone: '16.7' }
+
+ field :commit, Types::CommitType, null: true, complexity: 10, calls_gitaly: true,
+ description: 'Commit associated with the version.',
+ alpha: { milestone: '16.7' }
+
+ field :components, Types::Ci::Catalog::Resources::ComponentType.connection_type, null: true,
+ description: 'Components belonging to the catalog resource.',
+ alpha: { milestone: '16.7' }
+
+ def author
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find
+ end
+
+ def tag_path
+ Gitlab::Routing.url_helpers.project_tag_path(object.project, object.name)
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ 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
index 387f0202d2d..b80439b4de2 100644
--- a/app/graphql/types/container_registry/protection/rule_type.rb
+++ b/app/graphql/types/container_registry/protection/rule_type.rb
@@ -15,12 +15,12 @@ module Types
null: false,
description: 'ID of the container registry protection rule.'
- field :container_path_pattern,
+ field :repository_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.'
+ 'For example `my-project/my-container-*`. Wildcard character `*` allowed.'
field :push_protected_up_to_access_level,
Types::ContainerRegistry::Protection::RuleAccessLevelEnum,
diff --git a/app/graphql/types/container_repository_tag_type.rb b/app/graphql/types/container_repository_tag_type.rb
index d9665175449..cf8796410d3 100644
--- a/app/graphql/types/container_repository_tag_type.rb
+++ b/app/graphql/types/container_repository_tag_type.rb
@@ -8,7 +8,15 @@ module Types
authorize :read_container_image
- field :can_delete, GraphQL::Types::Boolean, null: false, description: 'Can the current user delete this tag.'
+ expose_permissions Types::PermissionTypes::ContainerRepositoryTag
+
+ field :can_delete, GraphQL::Types::Boolean,
+ null: false,
+ deprecated: {
+ reason: 'Use `userPermissions` field. See `ContainerRepositoryTagPermissions` type',
+ milestone: '16.7'
+ },
+ description: 'Can the current user delete this tag.'
field :created_at, Types::TimeType, null: true, description: 'Timestamp when the tag was created.'
field :digest, GraphQL::Types::String, null: true, description: 'Digest of the tag.'
field :location, GraphQL::Types::String, null: false, description: 'URL of the tag.'
diff --git a/app/graphql/types/container_repository_type.rb b/app/graphql/types/container_repository_type.rb
index dfa599e798c..c2a7eae5f94 100644
--- a/app/graphql/types/container_repository_type.rb
+++ b/app/graphql/types/container_repository_type.rb
@@ -8,7 +8,15 @@ module Types
authorize :read_container_image
- field :can_delete, GraphQL::Types::Boolean, null: false, description: 'Can the current user delete the container repository.'
+ expose_permissions Types::PermissionTypes::ContainerRepository
+
+ field :can_delete, GraphQL::Types::Boolean,
+ null: false,
+ deprecated: {
+ reason: 'Use `userPermissions` field. See `ContainerRepositoryPermissions` type',
+ milestone: '16.7'
+ },
+ description: 'Can the current user delete the container repository.'
field :created_at, Types::TimeType, null: false, description: 'Timestamp when the container repository was created.'
field :expiration_policy_cleanup_status, Types::ContainerRepositoryCleanupStatusEnum, null: true, description: 'Tags cleanup status for the container repository.'
field :expiration_policy_started_at, Types::TimeType, null: true, description: 'Timestamp when the cleanup done by the expiration policy was started on the container repository.'
diff --git a/app/graphql/types/current_user_type.rb b/app/graphql/types/current_user_type.rb
new file mode 100644
index 00000000000..d5ecdeba9e2
--- /dev/null
+++ b/app/graphql/types/current_user_type.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop:disable Graphql/AuthorizeTypes -- This is not necessary because the superclass declares the authorization
+ class CurrentUserType < ::Types::UserType
+ graphql_name 'CurrentUser'
+ description 'The currently authenticated GitLab user.'
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+end
+
+::Types::CurrentUserType.prepend_mod
diff --git a/app/graphql/types/group_connection.rb b/app/graphql/types/group_connection.rb
deleted file mode 100644
index e4332e24302..00000000000
--- a/app/graphql/types/group_connection.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-# Normally this wouldn't be needed and we could use
-#
-# type Types::GroupType.connection_type, null: true
-#
-# in a resolver. However we can end up with cyclic definitions.
-# Running the spec locally can result in errors like
-#
-# NameError: uninitialized constant Types::GroupType
-#
-# or other errors. To fix this, we created this file and use
-#
-# type "Types::GroupConnection", null: true
-#
-# which gives a delayed resolution, and the proper connection type.
-#
-# See gitlab/app/graphql/types/ci/runner_type.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::GroupConnection = Types::GroupType.connection_type
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index 74e7f256b44..7234948033b 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -21,7 +21,8 @@ module Types
field :custom_emoji,
type: Types::CustomEmojiType.connection_type,
null: true,
- description: 'Custom emoji within this namespace.',
+ resolver: Resolvers::CustomEmojiResolver,
+ description: 'Custom emoji in this namespace.',
alpha: { milestone: '13.6' }
field :share_with_group_lock,
@@ -274,6 +275,14 @@ module Types
description: 'Find a work item by IID directly associated with the group. Returns `null` if the ' \
'`namespace_level_work_items` feature flag is disabled.'
+ field :work_item_state_counts,
+ Types::WorkItemStateCountsType,
+ null: true,
+ alpha: { milestone: '16.7' },
+ description: 'Counts of work items by state for the namespace. Returns `null` if the ' \
+ '`namespace_level_work_items` feature flag is disabled.',
+ resolver: Resolvers::Namespaces::WorkItemStateCountsResolver
+
field :autocomplete_users,
null: true,
resolver: Resolvers::AutocompleteUsersResolver,
@@ -330,10 +339,6 @@ module Types
group.dependency_proxy_setting || group.create_dependency_proxy_setting
end
- def custom_emoji
- object.custom_emoji if Feature.enabled?(:custom_emoji)
- end
-
private
def group
diff --git a/app/graphql/types/issue_connection.rb b/app/graphql/types/issue_connection.rb
deleted file mode 100644
index 2f07888b43e..00000000000
--- a/app/graphql/types/issue_connection.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# 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.
-# Running the spec locally can result in errors like
-#
-# NameError: uninitialized constant Resolvers::GroupIssuesResolver
-#
-# 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 7c7d559e05d..76590f95687 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -91,13 +91,13 @@ module Types
description: 'Web URL of the issue.'
field :emails_disabled, GraphQL::Types::Boolean, null: false,
- method: :project_emails_disabled?,
- description: 'Indicates if a project has email notifications disabled: `true` if email notifications are disabled.',
+ method: :parent_emails_disabled?,
+ description: 'Indicates if the parent project or group has email notifications disabled: `true` if email notifications are disabled.',
deprecated: { reason: 'Use `emails_enabled`', milestone: '16.3' }
field :emails_enabled, GraphQL::Types::Boolean, null: false,
- method: :project_emails_enabled?,
- description: 'Indicates if a project has email notifications disabled: `false` if email notifications are disabled.'
+ method: :parent_emails_enabled?,
+ description: 'Indicates if the parent project or group has email notifications disabled: `false` if email notifications are disabled.'
field :human_time_estimate, GraphQL::Types::String, null: true,
description: 'Human-readable time estimate of the issue.'
@@ -162,7 +162,7 @@ module Types
field :timelogs, Types::TimelogType.connection_type, null: false,
description: 'Timelogs on the issue.'
- field :project_id, GraphQL::Types::Int, null: false, method: :project_id,
+ field :project_id, GraphQL::Types::Int, null: true, method: :project_id,
description: 'ID of the issue project.'
field :customer_relations_contacts, Types::CustomerRelations::ContactType.connection_type, null: true,
diff --git a/app/graphql/types/issue_type_enum.rb b/app/graphql/types/issue_type_enum.rb
index d7f587ff03d..491fc3d7fac 100644
--- a/app/graphql/types/issue_type_enum.rb
+++ b/app/graphql/types/issue_type_enum.rb
@@ -9,16 +9,16 @@ module Types
value issue_type.upcase, value: issue_type, description: "#{issue_type.titleize} issue type"
end
- value 'TASK', value: 'task',
- 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' }
+ description: 'Objective issue type. Available only when feature flag `okrs_mvc` is enabled.',
+ alpha: { milestone: '15.6' }
value 'KEY_RESULT', value: 'key_result',
- description: 'Key Result issue type. Available only when feature flag `okrs_mvc` is enabled.',
- alpha: { milestone: '15.7' }
+ description: 'Key Result issue type. Available only when feature flag `okrs_mvc` is enabled.',
+ alpha: { milestone: '15.7' }
+ value 'EPIC', value: 'epic',
+ description: 'Epic issue type. ' \
+ 'Available only when feature flag `namespace_level_work_items` is enabled.',
+ alpha: { milestone: '16.7' }
end
end
diff --git a/app/graphql/types/merge_requests/detailed_merge_status_enum.rb b/app/graphql/types/merge_requests/detailed_merge_status_enum.rb
index e7246068a05..d2d0cfd23a4 100644
--- a/app/graphql/types/merge_requests/detailed_merge_status_enum.rb
+++ b/app/graphql/types/merge_requests/detailed_merge_status_enum.rb
@@ -48,6 +48,9 @@ module Types
value 'PREPARING',
value: :preparing,
description: 'Merge request diff is being created.'
+ value 'JIRA_ASSOCIATION',
+ value: :jira_association_missing,
+ description: 'Either the title or description must reference a Jira issue.'
end
end
end
diff --git a/app/graphql/types/merge_requests/mergeability_check_identifier_enum.rb b/app/graphql/types/merge_requests/mergeability_check_identifier_enum.rb
index ac25c98941c..d5e63d9c9ca 100644
--- a/app/graphql/types/merge_requests/mergeability_check_identifier_enum.rb
+++ b/app/graphql/types/merge_requests/mergeability_check_identifier_enum.rb
@@ -11,7 +11,7 @@ module Types
value identifier.upcase,
value: identifier,
- description: "Mergeability check identifier is #{identifier}."
+ description: check_class.description
end
end
end
diff --git a/app/graphql/types/ml/candidate_links_type.rb b/app/graphql/types/ml/candidate_links_type.rb
new file mode 100644
index 00000000000..f14ab6bbc4a
--- /dev/null
+++ b/app/graphql/types/ml/candidate_links_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Ml
+ # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver
+ class CandidateLinksType < BaseObject
+ graphql_name 'MLCandidateLinks'
+ description 'Represents links to perform actions on the candidate'
+
+ present_using ::Ml::CandidatePresenter
+
+ field :show_path, GraphQL::Types::String,
+ null: true, description: 'Path to the details page of the candidate.', method: :path
+
+ field :artifact_path, GraphQL::Types::String,
+ null: true, description: 'Path to the artifact.', method: :artifact_path
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ml/candidate_type.rb b/app/graphql/types/ml/candidate_type.rb
new file mode 100644
index 00000000000..bee045c47bf
--- /dev/null
+++ b/app/graphql/types/ml/candidate_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ module Ml
+ # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver
+ class CandidateType < ::Types::BaseObject
+ graphql_name 'MlCandidate'
+ description 'Candidate for a model version in the model registry'
+
+ connection_type_class Types::LimitedCountableConnectionType
+
+ field :id, ::Types::GlobalIDType[::Ml::Candidate], null: false, description: 'ID of the candidate.'
+
+ field :name, ::GraphQL::Types::String, null: false, description: 'Name of the candidate.'
+
+ field :created_at, Types::TimeType, null: false, description: 'Date of creation.'
+
+ field :_links, ::Types::Ml::CandidateLinksType, null: false, method: :itself,
+ description: 'Map of links to perform actions on the candidate.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ml/model_type.rb b/app/graphql/types/ml/model_type.rb
new file mode 100644
index 00000000000..ca63918b370
--- /dev/null
+++ b/app/graphql/types/ml/model_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Ml
+ # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver
+ class ModelType < ::Types::BaseObject
+ graphql_name 'MlModel'
+ description 'Machine learning model in the model registry'
+
+ field :id, ::Types::GlobalIDType[::Ml::Model], null: false, description: 'ID of the model.'
+
+ field :name, ::GraphQL::Types::String, null: false, description: 'Name of the model.'
+
+ field :versions, ::Types::Ml::ModelVersionType.connection_type, null: true,
+ description: 'Versions of the model.'
+
+ field :candidates, ::Types::Ml::CandidateType.connection_type, null: true,
+ description: 'Version candidates of the model.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ml/model_version_links_type.rb b/app/graphql/types/ml/model_version_links_type.rb
new file mode 100644
index 00000000000..142f62bfad2
--- /dev/null
+++ b/app/graphql/types/ml/model_version_links_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module Ml
+ # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver
+ class ModelVersionLinksType < BaseObject
+ graphql_name 'MLModelVersionLinks'
+ description 'Represents links to perform actions on the model version'
+
+ present_using ::Ml::ModelVersionPresenter
+
+ field :show_path, GraphQL::Types::String,
+ null: true, description: 'Path to the details page of the model version.', method: :path
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ml/model_version_type.rb b/app/graphql/types/ml/model_version_type.rb
new file mode 100644
index 00000000000..15c36a7a0d8
--- /dev/null
+++ b/app/graphql/types/ml/model_version_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Ml
+ # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver
+ class ModelVersionType < ::Types::BaseObject
+ graphql_name 'MlModelVersion'
+ description 'Version of a machine learning model'
+
+ connection_type_class Types::LimitedCountableConnectionType
+
+ field :id, ::Types::GlobalIDType[::Ml::ModelVersion], null: false, description: 'ID of the model version.'
+
+ field :created_at, Types::TimeType, null: false, description: 'Date of creation.'
+ field :version, ::GraphQL::Types::String, null: false, description: 'Name of the version.'
+
+ field :_links, ::Types::Ml::ModelVersionLinksType, null: false, method: :itself,
+ description: 'Map of links to perform actions on the model version.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index e1bd1f603ad..590bc0ed282 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -107,7 +107,10 @@ module Types
mount_mutation Mutations::Notes::RepositionImageDiffNote
mount_mutation Mutations::Notes::Destroy
mount_mutation Mutations::Organizations::Create, alpha: { milestone: '16.6' }
+ mount_mutation Mutations::Organizations::Update, alpha: { milestone: '16.7' }
mount_mutation Mutations::Projects::SyncFork, calls_gitaly: true, alpha: { milestone: '15.9' }
+ mount_mutation Mutations::Projects::Star, alpha: { milestone: '16.7' }
+ mount_mutation Mutations::BranchRules::Update, alpha: { milestone: '16.7' }
mount_mutation Mutations::Releases::Create
mount_mutation Mutations::Releases::Update
mount_mutation Mutations::Releases::Delete
@@ -136,10 +139,12 @@ module Types
mount_mutation Mutations::DesignManagement::Update
mount_mutation Mutations::ContainerExpirationPolicies::Update
mount_mutation Mutations::ContainerRegistry::Protection::Rule::Create, alpha: { milestone: '16.6' }
+ mount_mutation Mutations::ContainerRegistry::Protection::Rule::Delete, alpha: { milestone: '16.7' }
+ mount_mutation Mutations::ContainerRegistry::Protection::Rule::Update, alpha: { milestone: '16.7' }
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::Catalog::Resources::Destroy, alpha: { milestone: '16.6' }
mount_mutation Mutations::Ci::Job::Cancel
mount_mutation Mutations::Ci::Job::Play
mount_mutation Mutations::Ci::Job::Retry
@@ -176,13 +181,13 @@ module Types
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::Protection::Rule::Update, alpha: { milestone: '16.6' }
mount_mutation Mutations::Packages::DestroyFiles
mount_mutation Mutations::Packages::Cleanup::Policy::Update
mount_mutation Mutations::Echo
mount_mutation Mutations::WorkItems::Create, alpha: { milestone: '15.1' }
mount_mutation Mutations::WorkItems::CreateFromTask, alpha: { milestone: '15.1' }
mount_mutation Mutations::WorkItems::Delete, alpha: { milestone: '15.1' }
- mount_mutation Mutations::WorkItems::DeleteTask, alpha: { milestone: '15.1' }
mount_mutation Mutations::WorkItems::Update, alpha: { milestone: '15.1' }
mount_mutation Mutations::WorkItems::UpdateTask, alpha: { milestone: '15.1' }
mount_mutation Mutations::WorkItems::Export, alpha: { milestone: '15.10' }
diff --git a/app/graphql/types/namespace/package_settings_type.rb b/app/graphql/types/namespace/package_settings_type.rb
index 6c6144f2357..7bf76ae7de5 100644
--- a/app/graphql/types/namespace/package_settings_type.rb
+++ b/app/graphql/types/namespace/package_settings_type.rb
@@ -31,7 +31,7 @@ module Types
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. '
+ 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.'
@@ -58,5 +58,9 @@ module Types
null: false,
method: :pypi_package_requests_forwarding_locked?,
description: 'Indicates whether PyPI package forwarding settings are locked by a parent namespace.'
+
+ field :nuget_symbol_server_enabled, GraphQL::Types::Boolean,
+ null: false,
+ description: 'Indicates wheather the NuGet symbol server is enabled for this namespace.'
end
end
diff --git a/app/graphql/types/organizations/organization_type.rb b/app/graphql/types/organizations/organization_type.rb
index e7ba8de527c..379bf9956a3 100644
--- a/app/graphql/types/organizations/organization_type.rb
+++ b/app/graphql/types/organizations/organization_type.rb
@@ -7,6 +7,16 @@ module Types
authorize :read_organization
+ field :avatar_url,
+ type: GraphQL::Types::String,
+ null: true,
+ description: 'Avatar URL of the organization.',
+ alpha: { milestone: '16.7' }
+ field :description,
+ GraphQL::Types::String,
+ null: true,
+ description: 'Description of the organization.',
+ alpha: { milestone: '16.7' }
field :groups,
Types::GroupType.connection_type,
null: false,
@@ -33,10 +43,17 @@ module Types
null: false,
description: 'Path of the organization.',
alpha: { milestone: '16.4' }
- field :web_url, GraphQL::Types::String,
+ field :web_url,
+ GraphQL::Types::String,
null: false,
description: 'Web URL of the organization.',
alpha: { milestone: '16.6' }
+
+ markdown_field :description_html, null: true, alpha: { milestone: '16.7' }, &:organization_detail
+
+ def avatar_url
+ object.avatar_url(only_path: false)
+ end
end
end
end
diff --git a/app/graphql/types/permission_types/abuse_report.rb b/app/graphql/types/permission_types/abuse_report.rb
deleted file mode 100644
index abd5d545d02..00000000000
--- a/app/graphql/types/permission_types/abuse_report.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# 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/container_repository.rb b/app/graphql/types/permission_types/container_repository.rb
new file mode 100644
index 00000000000..f6a76fb4d94
--- /dev/null
+++ b/app/graphql/types/permission_types/container_repository.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module PermissionTypes
+ class ContainerRepository < BasePermissionType
+ graphql_name 'ContainerRepositoryPermissions'
+
+ ability_field :destroy_container_image,
+ name: 'destroy_container_repository',
+ resolver_method: :destroy_container_image
+ end
+ end
+end
diff --git a/app/graphql/types/permission_types/container_repository_tag.rb b/app/graphql/types/permission_types/container_repository_tag.rb
new file mode 100644
index 00000000000..e2317ccd7d7
--- /dev/null
+++ b/app/graphql/types/permission_types/container_repository_tag.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module PermissionTypes
+ class ContainerRepositoryTag < BasePermissionType
+ graphql_name 'ContainerRepositoryTagPermissions'
+
+ ability_field :destroy_container_image,
+ name: 'destroy_container_repository_tag',
+ resolver_method: :destroy_container_image
+ end
+ end
+end
diff --git a/app/graphql/types/project_feature_access_level_enum.rb b/app/graphql/types/project_feature_access_level_enum.rb
new file mode 100644
index 00000000000..a107dbedc52
--- /dev/null
+++ b/app/graphql/types/project_feature_access_level_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ class ProjectFeatureAccessLevelEnum < BaseEnum
+ graphql_name 'ProjectFeatureAccessLevel'
+ description 'Access level of a project feature'
+
+ value 'DISABLED', value: ProjectFeature::DISABLED, description: 'Not enabled for anyone.'
+ value 'PRIVATE', value: ProjectFeature::PRIVATE, description: 'Enabled only for team members.'
+ value 'ENABLED', value: ProjectFeature::ENABLED, description: 'Enabled for everyone able to access the project.'
+ end
+end
diff --git a/app/graphql/types/project_feature_access_level_type.rb b/app/graphql/types/project_feature_access_level_type.rb
new file mode 100644
index 00000000000..d6d9fdefa70
--- /dev/null
+++ b/app/graphql/types/project_feature_access_level_type.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+# rubocop:disable Graphql/AuthorizeTypes -- It just returns the value of an enum as an integer and a string
+module Types
+ class ProjectFeatureAccessLevelType < Types::BaseObject
+ graphql_name 'ProjectFeatureAccess'
+ description 'Represents the access level required by the user to access a project feature'
+
+ field :integer_value, GraphQL::Types::Int, null: true,
+ description: 'Integer representation of access level.',
+ method: :to_i
+
+ field :string_value, Types::ProjectFeatureAccessLevelEnum, null: true,
+ description: 'String representation of access level.',
+ method: :to_i
+ end
+end
+# rubocop:enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index ec87f133843..8e84605cb05 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -151,6 +151,10 @@ module Types
null: true,
description: 'Number of open issues for the project.'
+ field :open_merge_requests_count, GraphQL::Types::Int,
+ null: true,
+ description: 'Number of open merge requests for the project.'
+
field :allow_merge_on_skipped_pipeline, GraphQL::Types::Boolean,
null: true,
description: 'If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of ' \
@@ -250,6 +254,13 @@ module Types
extras: [:lookahead],
resolver: Resolvers::WorkItemsResolver
+ field :work_item_state_counts,
+ Types::WorkItemStateCountsType,
+ null: true,
+ alpha: { milestone: '16.7' },
+ description: 'Counts of work items by state for the project.',
+ resolver: Resolvers::WorkItemStateCountsResolver
+
field :issue_status_counts,
Types::IssueStatusCountsType,
null: true,
@@ -647,6 +658,11 @@ module Types
description: 'Detailed import status of the project.',
method: :import_state
+ field :value_streams,
+ description: 'Value streams available to the project.',
+ null: true,
+ resolver: Resolvers::Analytics::CycleAnalytics::ValueStreamsResolver
+
def timelog_categories
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
end
@@ -675,6 +691,19 @@ module Types
end
end
+ [:issues, :forking, :merge_requests].each do |feature|
+ field_name = "#{feature}_access_level"
+ feature_name = feature.to_s.tr("_", " ")
+
+ field field_name, Types::ProjectFeatureAccessLevelType,
+ null: true,
+ description: "Access level required for #{feature_name} access."
+
+ define_method field_name do
+ project.project_feature&.access_level(feature)
+ end
+ end
+
markdown_field :description_html, null: true
def avatar_url
@@ -689,6 +718,12 @@ module Types
BatchLoader::GraphQL.wrap(object.open_issues_count) if object.feature_available?(:issues, context[:current_user])
end
+ def open_merge_requests_count
+ return unless object.feature_available?(:merge_requests, context[:current_user])
+
+ BatchLoader::GraphQL.wrap(object.open_merge_requests_count)
+ end
+
def forks_count
BatchLoader::GraphQL.wrap(object.forks_count)
end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 173e877d86c..0e39ff2c030 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -48,7 +48,7 @@ module Types
required: true,
description: 'Global ID of the container repository.'
end
- field :current_user, Types::UserType,
+ field :current_user, Types::CurrentUserType,
null: true,
description: "Get information about current user."
field :design_management, Types::DesignManagementType,
@@ -57,12 +57,10 @@ module Types
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' }
+ description: "A user's frecently visited groups"
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' }
+ description: "A user's frecently visited projects"
field :gitpod_enabled, GraphQL::Types::Boolean,
null: true,
description: "Whether Gitpod is enabled in application settings."
@@ -212,6 +210,19 @@ module Types
description: 'Abuse report labels.',
resolver: Resolvers::AbuseReportLabelsResolver
+ field :ml_model, ::Types::Ml::ModelType,
+ null: true,
+ alpha: { milestone: '16.7' },
+ description: 'Find machine learning models.',
+ resolver: Resolvers::Ml::ModelDetailResolver
+
+ field :work_items_by_reference,
+ null: true,
+ alpha: { milestone: '16.7' },
+ description: 'Find work items by their reference.',
+ extras: [:lookahead],
+ resolver: Resolvers::WorkItemReferencesResolver
+
def design_management
DesignManagementObject.new(nil)
end
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 7dd47611a2e..d4aca0a3792 100644
--- a/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb
+++ b/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb
@@ -35,7 +35,7 @@ module Types
description: 'URL to the file along with line number.'
field :engine_name, GraphQL::Types::String,
- null: false,
+ null: true,
description: 'Code quality plugin that reported the degradation.'
end
# rubocop: enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb
index 040711b5f58..7687da35baa 100644
--- a/app/graphql/types/user_interface.rb
+++ b/app/graphql/types/user_interface.rb
@@ -178,7 +178,7 @@ module Types
field :twitter,
type: ::GraphQL::Types::String,
null: true,
- description: 'Twitter username of the user.'
+ description: 'X (formerly Twitter) username of the user.'
field :discord,
type: ::GraphQL::Types::String,
@@ -229,5 +229,3 @@ module Types
end
end
end
-
-Types::UserInterface.prepend_mod
diff --git a/app/graphql/types/user_preferences_type.rb b/app/graphql/types/user_preferences_type.rb
index 094c7352c96..e9ac3a28a53 100644
--- a/app/graphql/types/user_preferences_type.rb
+++ b/app/graphql/types/user_preferences_type.rb
@@ -14,6 +14,10 @@ module Types
description: 'Determines whether the pipeline list shows ID or IID.',
null: true
+ field :use_web_ide_extension_marketplace, GraphQL::Types::Boolean,
+ description: 'Whether Web IDE Extension Marketplace is enabled for the user.',
+ null: false
+
def issues_sort
object.issues_sort.to_sym
end
diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb
index 87ca5fddf14..c5910236d51 100644
--- a/app/graphql/types/user_type.rb
+++ b/app/graphql/types/user_type.rb
@@ -14,3 +14,5 @@ module Types
present_using UserPresenter
end
end
+
+Types::UserType.prepend_mod
diff --git a/app/graphql/types/work_item_state_counts_type.rb b/app/graphql/types/work_item_state_counts_type.rb
new file mode 100644
index 00000000000..a5fdf542464
--- /dev/null
+++ b/app/graphql/types/work_item_state_counts_type.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes -- Parent node applies authorization
+ class WorkItemStateCountsType < BaseObject
+ graphql_name 'WorkItemStateCountsType'
+ description 'Represents total number of work items for the represented states'
+
+ field :all,
+ GraphQL::Types::Int,
+ null: true,
+ description: 'Number of work items for the project or group.'
+
+ field :closed,
+ GraphQL::Types::Int,
+ null: true,
+ description: 'Number of work items with state CLOSED for the project or group.'
+
+ field :opened,
+ GraphQL::Types::Int,
+ null: true,
+ description: 'Number of work items with state OPENED for the project or group.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/work_item_type.rb b/app/graphql/types/work_item_type.rb
index 103a1c0ec9b..b42684e650b 100644
--- a/app/graphql/types/work_item_type.rb
+++ b/app/graphql/types/work_item_type.rb
@@ -67,6 +67,12 @@ module Types
expose_permissions Types::PermissionTypes::WorkItem
+ def work_item_type
+ context.scoped_set!(:resource_parent, object.resource_parent)
+
+ object.work_item_type
+ end
+
def web_url
Gitlab::UrlBuilder.build(object)
end
diff --git a/app/graphql/types/work_items/deleted_task_input_type.rb b/app/graphql/types/work_items/deleted_task_input_type.rb
deleted file mode 100644
index 92297876c89..00000000000
--- a/app/graphql/types/work_items/deleted_task_input_type.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- module WorkItems
- class DeletedTaskInputType < BaseInputObject
- graphql_name 'WorkItemDeletedTaskInput'
-
- argument :id, ::Types::GlobalIDType[::WorkItem],
- required: true,
- description: 'Global ID of the task referenced in the work item\'s description.'
- argument :line_number_end, GraphQL::Types::Int,
- required: true,
- description: 'Last line in the Markdown source that defines the list item task.'
- argument :line_number_start, GraphQL::Types::Int,
- required: true,
- description: 'First line in the Markdown source that defines the list item task.'
- end
- end
-end
diff --git a/app/graphql/types/work_items/type_type.rb b/app/graphql/types/work_items/type_type.rb
index 4d008a21b9c..b42d73544dc 100644
--- a/app/graphql/types/work_items/type_type.rb
+++ b/app/graphql/types/work_items/type_type.rb
@@ -7,12 +7,20 @@ module Types
authorize :read_work_item_type
- field :icon_name, GraphQL::Types::String, null: true,
- description: 'Icon name of the work item type.'
- field :id, Types::GlobalIDType[::WorkItems::Type], null: false,
- description: 'Global ID of the work item type.'
- field :name, GraphQL::Types::String, null: false,
- description: 'Name of the work item type.'
+ field :icon_name, GraphQL::Types::String,
+ null: true,
+ description: 'Icon name of the work item type.'
+ field :id, Types::GlobalIDType[::WorkItems::Type],
+ null: false,
+ description: 'Global ID of the work item type.'
+ field :name, GraphQL::Types::String,
+ null: false,
+ description: 'Name of the work item type.'
+ field :widget_definitions, [Types::WorkItems::WidgetDefinitionInterface],
+ null: true,
+ description: 'Available widgets for the work item type.',
+ method: :widgets,
+ alpha: { milestone: '16.7' }
end
end
end
diff --git a/app/graphql/types/work_items/widget_definition_interface.rb b/app/graphql/types/work_items/widget_definition_interface.rb
new file mode 100644
index 00000000000..032e78779e7
--- /dev/null
+++ b/app/graphql/types/work_items/widget_definition_interface.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module WidgetDefinitionInterface
+ include Types::BaseInterface
+
+ graphql_name 'WorkItemWidgetDefinition'
+
+ field :type, ::Types::WorkItems::WidgetTypeEnum,
+ null: false,
+ description: 'Widget type.'
+
+ ORPHAN_TYPES = [
+ ::Types::WorkItems::WidgetDefinitions::AssigneesType,
+ ::Types::WorkItems::WidgetDefinitions::GenericType,
+ ::Types::WorkItems::WidgetDefinitions::HierarchyType
+ ].freeze
+
+ def self.ce_orphan_types
+ ORPHAN_TYPES
+ end
+
+ def self.resolve_type(object, _context)
+ if object == ::WorkItems::Widgets::Assignees
+ ::Types::WorkItems::WidgetDefinitions::AssigneesType
+ elsif object == ::WorkItems::Widgets::Hierarchy
+ ::Types::WorkItems::WidgetDefinitions::HierarchyType
+ else
+ ::Types::WorkItems::WidgetDefinitions::GenericType
+ end
+ end
+
+ orphan_types(*ce_orphan_types)
+ end
+ end
+end
+
+Types::WorkItems::WidgetDefinitionInterface.prepend_mod
diff --git a/app/graphql/types/work_items/widget_definitions/assignees_type.rb b/app/graphql/types/work_items/widget_definitions/assignees_type.rb
new file mode 100644
index 00000000000..6f30148e9aa
--- /dev/null
+++ b/app/graphql/types/work_items/widget_definitions/assignees_type.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module WidgetDefinitions
+ # rubocop:disable Graphql/AuthorizeTypes -- Authorization too granular, parent type is authorized
+ class AssigneesType < BaseObject
+ graphql_name 'WorkItemWidgetDefinitionAssignees'
+ description 'Represents an assignees widget definition'
+
+ implements Types::WorkItems::WidgetDefinitionInterface
+
+ field :can_invite_members, GraphQL::Types::Boolean,
+ null: false,
+ description: 'Indicates whether the current user can invite members to the work item\'s parent.'
+
+ def can_invite_members
+ object.can_invite_members?(current_user, resource_parent)
+ end
+
+ private
+
+ def resource_parent
+ context[:resource_parent]
+ end
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+ end
+ end
+end
+
+Types::WorkItems::WidgetDefinitions::AssigneesType.prepend_mod
diff --git a/app/graphql/types/work_items/widget_definitions/generic_type.rb b/app/graphql/types/work_items/widget_definitions/generic_type.rb
new file mode 100644
index 00000000000..f3817ade654
--- /dev/null
+++ b/app/graphql/types/work_items/widget_definitions/generic_type.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module WidgetDefinitions
+ # rubocop:disable Graphql/AuthorizeTypes -- Authorization too granular, parent type is authorized
+ class GenericType < BaseObject
+ graphql_name 'WorkItemWidgetDefinitionGeneric'
+ description 'Represents a generic widget definition'
+
+ implements Types::WorkItems::WidgetDefinitionInterface
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widget_definitions/hierarchy_type.rb b/app/graphql/types/work_items/widget_definitions/hierarchy_type.rb
new file mode 100644
index 00000000000..8bd70050c7c
--- /dev/null
+++ b/app/graphql/types/work_items/widget_definitions/hierarchy_type.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module WidgetDefinitions
+ # rubocop:disable Graphql/AuthorizeTypes -- authorized in work item type entity
+ class HierarchyType < BaseObject
+ graphql_name 'WorkItemWidgetDefinitionHierarchy'
+ description 'Represents a hierarchy widget definition'
+
+ implements Types::WorkItems::WidgetDefinitionInterface
+
+ field :allowed_child_types, Types::WorkItems::TypeType.connection_type,
+ null: true,
+ complexity: 5,
+ extras: [:parent],
+ description: 'Allowed child types for the work item type.'
+
+ def allowed_child_types(parent:)
+ parent.allowed_child_types(cache: true)
+ end
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widgets/assignees_type.rb b/app/graphql/types/work_items/widgets/assignees_type.rb
index 74da3264567..ecc0bc390d3 100644
--- a/app/graphql/types/work_items/widgets/assignees_type.rb
+++ b/app/graphql/types/work_items/widgets/assignees_type.rb
@@ -18,11 +18,21 @@ module Types
field :allows_multiple_assignees, GraphQL::Types::Boolean,
null: true, method: :allows_multiple_assignees?,
- description: 'Indicates whether multiple assignees are allowed.'
+ description: 'Indicates whether multiple assignees are allowed.',
+ deprecated: {
+ milestone: '16.7',
+ replacement: 'workitemWidgetDefinitionAssignees.allowsMultipleAssignees',
+ reason: 'Field moved to workItemType widget definition interface'
+ }
field :can_invite_members, GraphQL::Types::Boolean,
null: false, resolver_method: :can_invite_members?,
- description: 'Indicates whether the current user can invite members to the work item\'s project.'
+ description: 'Indicates whether the current user can invite members to the work item\'s project.',
+ deprecated: {
+ milestone: '16.7',
+ replacement: 'workitemWidgetDefinitionAssignees.canInviteMembers',
+ reason: 'Field moved to workItemType widget definition interface'
+ }
def can_invite_members?
Ability.allowed?(current_user, :admin_project_member, object.work_item.project)
diff --git a/app/graphql/types/work_items/widgets/labels_type.rb b/app/graphql/types/work_items/widgets/labels_type.rb
index 20574b3e3bc..5e5d324341d 100644
--- a/app/graphql/types/work_items/widgets/labels_type.rb
+++ b/app/graphql/types/work_items/widgets/labels_type.rb
@@ -19,7 +19,12 @@ module Types
field :allows_scoped_labels, GraphQL::Types::Boolean,
null: true,
method: :allows_scoped_labels?,
- description: 'Indicates whether a scoped label is allowed.'
+ description: 'Indicates whether a scoped label is allowed.',
+ deprecated: {
+ milestone: '16.7',
+ replacement: 'WorkItemWidgetDefinitionLabels.allowsScopedLabels',
+ reason: 'Field moved to workItemType widget definition interface'
+ }
end
# rubocop:enable Graphql/AuthorizeTypes
end