summaryrefslogtreecommitdiff
path: root/app/graphql
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 12:26:25 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 12:26:25 +0000
commita09983ae35713f5a2bbb100981116d31ce99826e (patch)
tree2ee2af7bd104d57086db360a7e6d8c9d5d43667a /app/graphql
parent18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff)
downloadgitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/mutations/alert_management/alerts/todo/create.rb30
-rw-r--r--app/graphql/mutations/alert_management/base.rb5
-rw-r--r--app/graphql/mutations/alert_management/update_alert_status.rb4
-rw-r--r--app/graphql/mutations/award_emojis/add.rb2
-rw-r--r--app/graphql/mutations/award_emojis/remove.rb2
-rw-r--r--app/graphql/mutations/award_emojis/toggle.rb2
-rw-r--r--app/graphql/mutations/base_mutation.rb2
-rw-r--r--app/graphql/mutations/concerns/mutations/resolves_issuable.rb29
-rw-r--r--app/graphql/mutations/container_expiration_policies/update.rb10
-rw-r--r--app/graphql/mutations/issues/set_locked.rb26
-rw-r--r--app/graphql/mutations/jira_import/start.rb9
-rw-r--r--app/graphql/mutations/merge_requests/update.rb39
-rw-r--r--app/graphql/mutations/notes/create/base.rb8
-rw-r--r--app/graphql/mutations/snippets/create.rb26
-rw-r--r--app/graphql/mutations/snippets/update.rb18
-rw-r--r--app/graphql/mutations/todos/mark_all_done.rb6
-rw-r--r--app/graphql/mutations/todos/restore_many.rb8
-rw-r--r--app/graphql/resolvers/base_resolver.rb5
-rw-r--r--app/graphql/resolvers/ci_configuration/sast_resolver.rb17
-rw-r--r--app/graphql/resolvers/concerns/resolves_merge_requests.rb28
-rw-r--r--app/graphql/resolvers/environments_resolver.rb2
-rw-r--r--app/graphql/resolvers/issues_resolver.rb17
-rw-r--r--app/graphql/resolvers/last_commit_resolver.rb2
-rw-r--r--app/graphql/resolvers/milestone_resolver.rb6
-rw-r--r--app/graphql/resolvers/packages_resolver.rb19
-rw-r--r--app/graphql/resolvers/projects/jira_projects_resolver.rb42
-rw-r--r--app/graphql/resolvers/projects_resolver.rb2
-rw-r--r--app/graphql/resolvers/release_resolver.rb2
-rw-r--r--app/graphql/resolvers/releases_resolver.rb2
-rw-r--r--app/graphql/types/alert_management/alert_sort_enum.rb8
-rw-r--r--app/graphql/types/alert_management/alert_type.rb6
-rw-r--r--app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb25
-rw-r--r--app/graphql/types/ci_configuration/sast/entity_type.rb34
-rw-r--r--app/graphql/types/ci_configuration/sast/options_entity_type.rb19
-rw-r--r--app/graphql/types/ci_configuration/sast/type.rb22
-rw-r--r--app/graphql/types/container_expiration_policy_type.rb4
-rw-r--r--app/graphql/types/deprecated_mutations.rb19
-rw-r--r--app/graphql/types/diff_stats_summary_type.rb25
-rw-r--r--app/graphql/types/diff_stats_type.rb19
-rw-r--r--app/graphql/types/error_tracking/sentry_detailed_error_type.rb8
-rw-r--r--app/graphql/types/error_tracking/sentry_error_collection_type.rb2
-rw-r--r--app/graphql/types/global_id_type.rb64
-rw-r--r--app/graphql/types/issue_connection_type.rb13
-rw-r--r--app/graphql/types/issue_type.rb4
-rw-r--r--app/graphql/types/jira_user_type.rb6
-rw-r--r--app/graphql/types/jira_users_mapping_input_type.rb18
-rw-r--r--app/graphql/types/merge_request_type.rb26
-rw-r--r--app/graphql/types/milestone_stats_type.rb16
-rw-r--r--app/graphql/types/milestone_type.rb11
-rw-r--r--app/graphql/types/mutation_type.rb4
-rw-r--r--app/graphql/types/namespace_type.rb2
-rw-r--r--app/graphql/types/notes/note_type.rb6
-rw-r--r--app/graphql/types/package_type.rb16
-rw-r--r--app/graphql/types/package_type_enum.rb9
-rw-r--r--app/graphql/types/project_statistics_type.rb2
-rw-r--r--app/graphql/types/project_type.rb19
-rw-r--r--app/graphql/types/projects/services/jira_service_type.rb2
-rw-r--r--app/graphql/types/query_type.rb4
-rw-r--r--app/graphql/types/release_asset_link_type.rb (renamed from app/graphql/types/release_link_type.rb)7
-rw-r--r--app/graphql/types/release_asset_link_type_enum.rb (renamed from app/graphql/types/release_link_type_enum.rb)4
-rw-r--r--app/graphql/types/release_assets_type.rb5
-rw-r--r--app/graphql/types/release_links_type.rb23
-rw-r--r--app/graphql/types/release_source_type.rb3
-rw-r--r--app/graphql/types/release_type.rb14
-rw-r--r--app/graphql/types/root_storage_statistics_type.rb1
-rw-r--r--app/graphql/types/todo_target_enum.rb1
-rw-r--r--app/graphql/types/tree/blob_type.rb2
-rw-r--r--app/graphql/types/untrusted_regexp.rb22
68 files changed, 722 insertions, 143 deletions
diff --git a/app/graphql/mutations/alert_management/alerts/todo/create.rb b/app/graphql/mutations/alert_management/alerts/todo/create.rb
new file mode 100644
index 00000000000..3dba96e43f1
--- /dev/null
+++ b/app/graphql/mutations/alert_management/alerts/todo/create.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Mutations
+ module AlertManagement
+ module Alerts
+ module Todo
+ class Create < Base
+ graphql_name 'AlertTodoCreate'
+
+ def resolve(args)
+ alert = authorized_find!(project_path: args[:project_path], iid: args[:iid])
+ result = ::AlertManagement::Alerts::Todo::CreateService.new(alert, current_user).execute
+
+ prepare_response(result)
+ end
+
+ private
+
+ def prepare_response(result)
+ {
+ alert: result.payload[:alert],
+ todo: result.payload[:todo],
+ errors: result.error? ? [result.message] : []
+ }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/alert_management/base.rb b/app/graphql/mutations/alert_management/base.rb
index 7fcca63db51..0de4b9409e4 100644
--- a/app/graphql/mutations/alert_management/base.rb
+++ b/app/graphql/mutations/alert_management/base.rb
@@ -18,6 +18,11 @@ module Mutations
null: true,
description: "The alert after mutation"
+ field :todo,
+ Types::TodoType,
+ null: true,
+ description: "The todo after mutation"
+
field :issue,
Types::IssueType,
null: true,
diff --git a/app/graphql/mutations/alert_management/update_alert_status.rb b/app/graphql/mutations/alert_management/update_alert_status.rb
index d820124d26f..ed61555fbd6 100644
--- a/app/graphql/mutations/alert_management/update_alert_status.rb
+++ b/app/graphql/mutations/alert_management/update_alert_status.rb
@@ -19,8 +19,8 @@ module Mutations
private
def update_status(alert, status)
- ::AlertManagement::UpdateAlertStatusService
- .new(alert, current_user, status)
+ ::AlertManagement::Alerts::UpdateService
+ .new(alert, current_user, status: status)
.execute
end
diff --git a/app/graphql/mutations/award_emojis/add.rb b/app/graphql/mutations/award_emojis/add.rb
index 85f3eb065bb..856fdd5fb14 100644
--- a/app/graphql/mutations/award_emojis/add.rb
+++ b/app/graphql/mutations/award_emojis/add.rb
@@ -3,7 +3,7 @@
module Mutations
module AwardEmojis
class Add < Base
- graphql_name 'AddAwardEmoji'
+ graphql_name 'AwardEmojiAdd'
def resolve(args)
awardable = authorized_find!(id: args[:awardable_id])
diff --git a/app/graphql/mutations/award_emojis/remove.rb b/app/graphql/mutations/award_emojis/remove.rb
index f8a3d0ce390..c654688c6dc 100644
--- a/app/graphql/mutations/award_emojis/remove.rb
+++ b/app/graphql/mutations/award_emojis/remove.rb
@@ -3,7 +3,7 @@
module Mutations
module AwardEmojis
class Remove < Base
- graphql_name 'RemoveAwardEmoji'
+ graphql_name 'AwardEmojiRemove'
def resolve(args)
awardable = authorized_find!(id: args[:awardable_id])
diff --git a/app/graphql/mutations/award_emojis/toggle.rb b/app/graphql/mutations/award_emojis/toggle.rb
index 22eab4812a1..a7714e695d2 100644
--- a/app/graphql/mutations/award_emojis/toggle.rb
+++ b/app/graphql/mutations/award_emojis/toggle.rb
@@ -3,7 +3,7 @@
module Mutations
module AwardEmojis
class Toggle < Base
- graphql_name 'ToggleAwardEmoji'
+ graphql_name 'AwardEmojiToggle'
field :toggledOn, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates the status of the emoji. ' \
diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb
index 33f3f33a440..68e7853a9b1 100644
--- a/app/graphql/mutations/base_mutation.rb
+++ b/app/graphql/mutations/base_mutation.rb
@@ -7,6 +7,8 @@ module Mutations
ERROR_MESSAGE = 'You cannot perform write operations on a read-only instance'
+ field_class ::Types::BaseField
+
field :errors, [GraphQL::STRING_TYPE],
null: false,
description: 'Errors encountered during execution of the mutation.'
diff --git a/app/graphql/mutations/concerns/mutations/resolves_issuable.rb b/app/graphql/mutations/concerns/mutations/resolves_issuable.rb
index 13a56f2e709..0fe2d09de6d 100644
--- a/app/graphql/mutations/concerns/mutations/resolves_issuable.rb
+++ b/app/graphql/mutations/concerns/mutations/resolves_issuable.rb
@@ -9,30 +9,31 @@ module Mutations
end
def resolve_issuable(type:, parent_path:, iid:)
- parent = resolve_issuable_parent(type, parent_path)
- key = type == :merge_request ? :iids : :iid
- args = { key => iid.to_s }
+ parent = ::Gitlab::Graphql::Lazy.force(resolve_issuable_parent(type, parent_path))
+ return unless parent.present?
- resolver = issuable_resolver(type, parent, context)
- ready, early_return = resolver.ready?(**args)
-
- return early_return unless ready
-
- resolver.resolve(**args)
+ finder = issuable_finder(type, iids: [iid])
+ Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).find_all.first
end
private
- def issuable_resolver(type, parent, context)
- resolver_class = "Resolvers::#{type.to_s.classify.pluralize}Resolver".constantize
-
- resolver_class.single.new(object: parent, context: context, field: nil)
+ def issuable_finder(type, args)
+ case type
+ when :merge_request
+ MergeRequestsFinder.new(current_user, args)
+ when :issue
+ IssuesFinder.new(current_user, args)
+ else
+ raise "Unsupported type: #{type}"
+ end
end
def resolve_issuable_parent(type, parent_path)
+ return unless parent_path.present?
return unless type == :issue || type == :merge_request
- resolve_project(full_path: parent_path) if parent_path.present?
+ resolve_project(full_path: parent_path)
end
end
end
diff --git a/app/graphql/mutations/container_expiration_policies/update.rb b/app/graphql/mutations/container_expiration_policies/update.rb
index c210571c6ca..4bff04bb705 100644
--- a/app/graphql/mutations/container_expiration_policies/update.rb
+++ b/app/graphql/mutations/container_expiration_policies/update.rb
@@ -34,6 +34,16 @@ module Mutations
required: false,
description: copy_field_description(Types::ContainerExpirationPolicyType, :keep_n)
+ argument :name_regex,
+ Types::UntrustedRegexp,
+ required: false,
+ description: copy_field_description(Types::ContainerExpirationPolicyType, :name_regex)
+
+ argument :name_regex_keep,
+ Types::UntrustedRegexp,
+ required: false,
+ description: copy_field_description(Types::ContainerExpirationPolicyType, :name_regex_keep)
+
field :container_expiration_policy,
Types::ContainerExpirationPolicyType,
null: true,
diff --git a/app/graphql/mutations/issues/set_locked.rb b/app/graphql/mutations/issues/set_locked.rb
new file mode 100644
index 00000000000..63a8483067a
--- /dev/null
+++ b/app/graphql/mutations/issues/set_locked.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class SetLocked < Base
+ graphql_name 'IssueSetLocked'
+
+ argument :locked,
+ GraphQL::BOOLEAN_TYPE,
+ required: true,
+ description: 'Whether or not to lock discussion on the issue'
+
+ def resolve(project_path:, iid:, locked:)
+ issue = authorized_find!(project_path: project_path, iid: iid)
+
+ ::Issues::UpdateService.new(issue.project, current_user, discussion_locked: locked)
+ .execute(issue)
+
+ {
+ issue: issue,
+ errors: errors_on_object(issue)
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/jira_import/start.rb b/app/graphql/mutations/jira_import/start.rb
index 3df26d33711..eda28059272 100644
--- a/app/graphql/mutations/jira_import/start.rb
+++ b/app/graphql/mutations/jira_import/start.rb
@@ -21,12 +21,17 @@ module Mutations
argument :jira_project_name, GraphQL::STRING_TYPE,
required: false,
description: 'Project name of the importer Jira project'
+ argument :users_mapping,
+ [Types::JiraUsersMappingInputType],
+ required: false,
+ description: 'The mapping of Jira to GitLab users'
- def resolve(project_path:, jira_project_key:)
+ def resolve(project_path:, jira_project_key:, users_mapping:)
project = authorized_find!(full_path: project_path)
+ mapping = users_mapping.to_ary.map { |map| map.to_hash }
service_response = ::JiraImport::StartImportService
- .new(context[:current_user], project, jira_project_key)
+ .new(context[:current_user], project, jira_project_key, mapping)
.execute
jira_import = service_response.success? ? service_response.payload[:import_data] : nil
diff --git a/app/graphql/mutations/merge_requests/update.rb b/app/graphql/mutations/merge_requests/update.rb
new file mode 100644
index 00000000000..b583fdfca9b
--- /dev/null
+++ b/app/graphql/mutations/merge_requests/update.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Mutations
+ module MergeRequests
+ class Update < Base
+ graphql_name 'MergeRequestUpdate'
+
+ description 'Update attributes of a merge request'
+
+ argument :title, GraphQL::STRING_TYPE,
+ required: false,
+ description: copy_field_description(Types::MergeRequestType, :title)
+
+ argument :target_branch, GraphQL::STRING_TYPE,
+ required: false,
+ description: copy_field_description(Types::MergeRequestType, :target_branch)
+
+ argument :description, GraphQL::STRING_TYPE,
+ required: false,
+ description: copy_field_description(Types::MergeRequestType, :description)
+
+ def resolve(args)
+ merge_request = authorized_find!(args.slice(:project_path, :iid))
+ attributes = args.slice(:title, :description, :target_branch).compact
+
+ ::MergeRequests::UpdateService
+ .new(merge_request.project, current_user, attributes)
+ .execute(merge_request)
+
+ errors = errors_on_object(merge_request)
+
+ {
+ merge_request: merge_request.reset,
+ errors: errors
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb
index cf9f74a63d8..f081eac368e 100644
--- a/app/graphql/mutations/notes/create/base.rb
+++ b/app/graphql/mutations/notes/create/base.rb
@@ -18,6 +18,11 @@ module Mutations
required: true,
description: copy_field_description(Types::Notes::NoteType, :body)
+ argument :confidential,
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'The confidentiality flag of a note. Default is false.'
+
def resolve(args)
noteable = authorized_find!(id: args[:noteable_id])
@@ -40,7 +45,8 @@ module Mutations
def create_note_params(noteable, args)
{
noteable: noteable,
- note: args[:body]
+ note: args[:body],
+ confidential: args[:confidential]
}
end
end
diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb
index e1022358c09..89c21486a74 100644
--- a/app/graphql/mutations/snippets/create.rb
+++ b/app/graphql/mutations/snippets/create.rb
@@ -21,7 +21,7 @@ module Mutations
description: 'File name of the snippet'
argument :content, GraphQL::STRING_TYPE,
- required: true,
+ required: false,
description: 'Content of the snippet'
argument :description, GraphQL::STRING_TYPE,
@@ -40,6 +40,10 @@ module Mutations
required: false,
description: 'The paths to files uploaded in the snippet description'
+ argument :files, [Types::Snippets::FileInputType],
+ description: "The snippet files to create",
+ required: false
+
def resolve(args)
project_path = args.delete(:project_path)
@@ -49,13 +53,9 @@ module Mutations
raise_resource_not_available_error!
end
- # We need to rename `uploaded_files` into `files` because
- # it's the expected key param
- args[:files] = args.delete(:uploaded_files)
-
service_response = ::Snippets::CreateService.new(project,
- context[:current_user],
- args).execute
+ context[:current_user],
+ create_params(args)).execute
snippet = service_response.payload[:snippet]
@@ -82,6 +82,18 @@ module Mutations
def can_create_personal_snippet?
Ability.allowed?(context[:current_user], :create_snippet)
end
+
+ def create_params(args)
+ args.tap do |create_args|
+ # We need to rename `files` into `snippet_actions` because
+ # it's the expected key param
+ create_args[:snippet_actions] = create_args.delete(:files)&.map(&:to_h)
+
+ # We need to rename `uploaded_files` into `files` because
+ # it's the expected key param
+ create_args[:files] = create_args.delete(:uploaded_files)
+ end
+ end
end
end
end
diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb
index b6bdcb9b67b..8890158b0df 100644
--- a/app/graphql/mutations/snippets/update.rb
+++ b/app/graphql/mutations/snippets/update.rb
@@ -30,12 +30,16 @@ module Mutations
description: 'The visibility level of the snippet',
required: false
+ argument :files, [Types::Snippets::FileInputType],
+ description: 'The snippet files to update',
+ required: false
+
def resolve(args)
snippet = authorized_find!(id: args.delete(:id))
result = ::Snippets::UpdateService.new(snippet.project,
- context[:current_user],
- args).execute(snippet)
+ context[:current_user],
+ update_params(args)).execute(snippet)
snippet = result.payload[:snippet]
{
@@ -47,7 +51,15 @@ module Mutations
private
def ability_name
- "update"
+ 'update'
+ end
+
+ def update_params(args)
+ args.tap do |update_args|
+ # We need to rename `files` into `snippet_actions` because
+ # it's the expected key param
+ update_args[:snippet_actions] = update_args.delete(:files)&.map(&:to_h)
+ end
end
end
end
diff --git a/app/graphql/mutations/todos/mark_all_done.rb b/app/graphql/mutations/todos/mark_all_done.rb
index d30d1bcbcf0..8b53658ddd5 100644
--- a/app/graphql/mutations/todos/mark_all_done.rb
+++ b/app/graphql/mutations/todos/mark_all_done.rb
@@ -10,8 +10,13 @@ module Mutations
field :updated_ids,
[GraphQL::ID_TYPE],
null: false,
+ deprecated: { reason: 'Use todos', milestone: '13.2' },
description: 'Ids of the updated todos'
+ field :todos, [::Types::TodoType],
+ null: false,
+ description: 'Updated todos'
+
def resolve
authorize!(current_user)
@@ -19,6 +24,7 @@ module Mutations
{
updated_ids: map_to_global_ids(updated_ids),
+ todos: Todo.id_in(updated_ids),
errors: []
}
end
diff --git a/app/graphql/mutations/todos/restore_many.rb b/app/graphql/mutations/todos/restore_many.rb
index e95651b232f..c5e2750768c 100644
--- a/app/graphql/mutations/todos/restore_many.rb
+++ b/app/graphql/mutations/todos/restore_many.rb
@@ -14,7 +14,12 @@ module Mutations
field :updated_ids, [GraphQL::ID_TYPE],
null: false,
- description: 'The ids of the updated todo items'
+ description: 'The ids of the updated todo items',
+ deprecated: { reason: 'Use todos', milestone: '13.2' }
+
+ field :todos, [::Types::TodoType],
+ null: false,
+ description: 'Updated todos'
def resolve(ids:)
check_update_amount_limit!(ids)
@@ -24,6 +29,7 @@ module Mutations
{
updated_ids: gids_of(updated_ids),
+ todos: Todo.id_in(updated_ids),
errors: errors_on_objects(todos)
}
end
diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb
index 7daff68c069..791c6eab42f 100644
--- a/app/graphql/resolvers/base_resolver.rb
+++ b/app/graphql/resolvers/base_resolver.rb
@@ -83,5 +83,10 @@ module Resolvers
def current_user
context[:current_user]
end
+
+ # Overridden in sub-classes (see .single, .last)
+ def select_result(results)
+ results
+ end
end
end
diff --git a/app/graphql/resolvers/ci_configuration/sast_resolver.rb b/app/graphql/resolvers/ci_configuration/sast_resolver.rb
new file mode 100644
index 00000000000..e8c42076ea2
--- /dev/null
+++ b/app/graphql/resolvers/ci_configuration/sast_resolver.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require "json"
+
+module Resolvers
+ module CiConfiguration
+ class SastResolver < BaseResolver
+ SAST_UI_SCHEMA_PATH = 'app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json'
+
+ type ::Types::CiConfiguration::Sast::Type, null: true
+
+ def resolve(**args)
+ Gitlab::Json.parse(File.read(Rails.root.join(SAST_UI_SCHEMA_PATH)))
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
index a2140728a27..7ed88be52b9 100644
--- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb
+++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
@@ -11,16 +11,10 @@ module ResolvesMergeRequests
end
def resolve_with_lookahead(**args)
- args[:iids] = Array.wrap(args[:iids]) if args[:iids]
- args.compact!
+ mr_finder = MergeRequestsFinder.new(current_user, args.compact)
+ finder = Gitlab::Graphql::Loaders::IssuableLoader.new(project, mr_finder)
- if project && args.keys == [:iids]
- batch_load_merge_requests(args[:iids])
- else
- args[:project_id] ||= project
-
- apply_lookahead(MergeRequestsFinder.new(current_user, args).execute)
- end.then(&(single? ? :first : :itself))
+ select_result(finder.batching_find_all { |query| apply_lookahead(query) })
end
def ready?(**args)
@@ -35,22 +29,6 @@ module ResolvesMergeRequests
private
- def batch_load_merge_requests(iids)
- iids.map { |iid| batch_load(iid) }.select(&:itself) # .compact doesn't work on BatchLoader
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def batch_load(iid)
- BatchLoader::GraphQL.for(iid.to_s).batch(key: project) do |iids, loader, args|
- query = args[:key].merge_requests.where(iid: iids)
-
- apply_lookahead(query).each do |mr|
- loader.call(mr.iid.to_s, mr)
- end
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
def unconditional_includes
[:target_project]
end
diff --git a/app/graphql/resolvers/environments_resolver.rb b/app/graphql/resolvers/environments_resolver.rb
index 4e9a17f1e17..1b916a89796 100644
--- a/app/graphql/resolvers/environments_resolver.rb
+++ b/app/graphql/resolvers/environments_resolver.rb
@@ -8,7 +8,7 @@ module Resolvers
argument :search, GraphQL::STRING_TYPE,
required: false,
- description: 'Search query'
+ description: 'Search query for environment name'
argument :states, [GraphQL::STRING_TYPE],
required: false,
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index f103da07666..9d0535a208f 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -44,7 +44,7 @@ module Resolvers
description: 'Issues closed after this date'
argument :search, GraphQL::STRING_TYPE,
required: false,
- description: 'Search query for finding issues by title or description'
+ description: 'Search query for issue title or description'
argument :sort, Types::IssueSortEnum,
description: 'Sort issues by this criteria',
required: false,
@@ -63,18 +63,13 @@ module Resolvers
parent = object.respond_to?(:sync) ? object.sync : object
return Issue.none if parent.nil?
- if parent.is_a?(Group)
- args[:group_id] = parent.id
- else
- args[:project_id] = parent.id
- end
-
# Will need to be be made group & namespace aware with
# https://gitlab.com/gitlab-org/gitlab-foss/issues/54520
- args[:iids] ||= [args[:iid]].compact
- args[:attempt_project_search_optimizations] = args[:search].present?
+ args[:iids] ||= [args.delete(:iid)].compact if args[:iid]
+ args[:attempt_project_search_optimizations] = true if args[:search].present?
- issues = IssuesFinder.new(context[:current_user], args).execute
+ finder = IssuesFinder.new(current_user, args)
+ issues = Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all
if non_stable_cursor_sort?(args[:sort])
# Certain complex sorts are not supported by the stable cursor pagination yet.
@@ -97,3 +92,5 @@ module Resolvers
end
end
end
+
+Resolvers::IssuesResolver.prepend_if_ee('::EE::Resolvers::IssuesResolver')
diff --git a/app/graphql/resolvers/last_commit_resolver.rb b/app/graphql/resolvers/last_commit_resolver.rb
index 7a433d6556f..dd89c322617 100644
--- a/app/graphql/resolvers/last_commit_resolver.rb
+++ b/app/graphql/resolvers/last_commit_resolver.rb
@@ -9,7 +9,7 @@ module Resolvers
def resolve(**args)
# Ensure merge commits can be returned by sending nil to Gitaly instead of '/'
path = tree.path == '/' ? nil : tree.path
- commit = Gitlab::Git::Commit.last_for_path(tree.repository, tree.sha, path)
+ commit = Gitlab::Git::Commit.last_for_path(tree.repository, tree.sha, path, literal_pathspec: true)
::Commit.new(commit, tree.repository.project) if commit
end
diff --git a/app/graphql/resolvers/milestone_resolver.rb b/app/graphql/resolvers/milestone_resolver.rb
index 6c6513e0ee4..bcfbc63c31f 100644
--- a/app/graphql/resolvers/milestone_resolver.rb
+++ b/app/graphql/resolvers/milestone_resolver.rb
@@ -52,7 +52,7 @@ module Resolvers
end
def group_parameters(args)
- return { group_ids: parent.id } unless include_descendants?(args)
+ return { group_ids: parent.id } unless args[:include_descendants].present?
{
group_ids: parent.self_and_descendants.public_or_visible_to_user(current_user).select(:id),
@@ -60,10 +60,6 @@ module Resolvers
}
end
- def include_descendants?(args)
- args[:include_descendants].present? && Feature.enabled?(:group_milestone_descendants, parent)
- end
-
def group_projects
GroupProjectsFinder.new(
group: parent,
diff --git a/app/graphql/resolvers/packages_resolver.rb b/app/graphql/resolvers/packages_resolver.rb
new file mode 100644
index 00000000000..519fb87183e
--- /dev/null
+++ b/app/graphql/resolvers/packages_resolver.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class PackagesResolver < BaseResolver
+ type Types::PackageType, null: true
+
+ def resolve(**args)
+ return unless packages_available?
+
+ ::Packages::PackagesFinder.new(object).execute
+ end
+
+ private
+
+ def packages_available?
+ ::Gitlab.config.packages.enabled
+ end
+ end
+end
diff --git a/app/graphql/resolvers/projects/jira_projects_resolver.rb b/app/graphql/resolvers/projects/jira_projects_resolver.rb
index a8c3768df41..2dc712128cc 100644
--- a/app/graphql/resolvers/projects/jira_projects_resolver.rb
+++ b/app/graphql/resolvers/projects/jira_projects_resolver.rb
@@ -13,11 +13,10 @@ module Resolvers
def resolve(name: nil, **args)
authorize!(project)
- response, start_cursor, end_cursor = jira_projects(name: name, **compute_pagination_params(args))
- end_cursor = nil if !!response.payload[:is_last]
+ response = jira_projects(name: name)
if response.success?
- Gitlab::Graphql::ExternallyPaginatedArray.new(start_cursor, end_cursor, *response.payload[:projects])
+ response.payload[:projects]
else
raise Gitlab::Graphql::Errors::BaseError, response.message
end
@@ -35,41 +34,10 @@ module Resolvers
jira_service&.project
end
- def compute_pagination_params(params)
- after_cursor = Base64.decode64(params[:after].to_s)
- before_cursor = Base64.decode64(params[:before].to_s)
+ def jira_projects(name:)
+ args = { query: name }.compact
- # differentiate between 0 cursor and nil or invalid cursor that decodes into zero.
- after_index = after_cursor.to_i == 0 && after_cursor != "0" ? nil : after_cursor.to_i
- before_index = before_cursor.to_i == 0 && before_cursor != "0" ? nil : before_cursor.to_i
-
- if after_index.present? && before_index.present?
- if after_index >= before_index
- { start_at: 0, limit: 0 }
- else
- { start_at: after_index + 1, limit: before_index - after_index - 1 }
- end
- elsif after_index.present?
- { start_at: after_index + 1, limit: nil }
- elsif before_index.present?
- { start_at: 0, limit: before_index - 1 }
- else
- { start_at: 0, limit: nil }
- end
- end
-
- def jira_projects(name:, start_at:, limit:)
- args = { query: name, start_at: start_at, limit: limit }.compact
-
- response = Jira::Requests::Projects.new(project.jira_service, args).execute
-
- return [response, nil, nil] if response.error?
-
- projects = response.payload[:projects]
- start_cursor = start_at == 0 ? nil : Base64.encode64((start_at - 1).to_s)
- end_cursor = Base64.encode64((start_at + projects.size - 1).to_s)
-
- [response, start_cursor, end_cursor]
+ Jira::Requests::Projects::ListService.new(project.jira_service, args).execute
end
end
end
diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb
index 068546cd39f..f75f591b381 100644
--- a/app/graphql/resolvers/projects_resolver.rb
+++ b/app/graphql/resolvers/projects_resolver.rb
@@ -10,7 +10,7 @@ module Resolvers
argument :search, GraphQL::STRING_TYPE,
required: false,
- description: 'Search criteria'
+ description: 'Search query for project name, path, or description'
def resolve(**args)
ProjectsFinder
diff --git a/app/graphql/resolvers/release_resolver.rb b/app/graphql/resolvers/release_resolver.rb
index 9bae8b8cd13..1edcc8c70b5 100644
--- a/app/graphql/resolvers/release_resolver.rb
+++ b/app/graphql/resolvers/release_resolver.rb
@@ -15,6 +15,8 @@ module Resolvers
end
def resolve(tag_name:)
+ return unless Feature.enabled?(:graphql_release_data, project, default_enabled: true)
+
ReleasesFinder.new(
project,
current_user,
diff --git a/app/graphql/resolvers/releases_resolver.rb b/app/graphql/resolvers/releases_resolver.rb
index b2afbb92684..85892c2abeb 100644
--- a/app/graphql/resolvers/releases_resolver.rb
+++ b/app/graphql/resolvers/releases_resolver.rb
@@ -12,6 +12,8 @@ module Resolvers
end
def resolve(**args)
+ return unless Feature.enabled?(:graphql_release_data, project, default_enabled: true)
+
ReleasesFinder.new(
project,
current_user
diff --git a/app/graphql/types/alert_management/alert_sort_enum.rb b/app/graphql/types/alert_management/alert_sort_enum.rb
index 3faac9ce53c..51e7bef0a7f 100644
--- a/app/graphql/types/alert_management/alert_sort_enum.rb
+++ b/app/graphql/types/alert_management/alert_sort_enum.rb
@@ -16,10 +16,10 @@ module Types
value 'UPDATED_TIME_DESC', 'Created time by descending order', value: :updated_at_desc
value 'EVENT_COUNT_ASC', 'Events count by ascending order', value: :event_count_asc
value 'EVENT_COUNT_DESC', 'Events count by descending order', value: :event_count_desc
- value 'SEVERITY_ASC', 'Severity by ascending order', value: :severity_asc
- value 'SEVERITY_DESC', 'Severity by descending order', value: :severity_desc
- value 'STATUS_ASC', 'Status by ascending order', value: :status_asc
- value 'STATUS_DESC', 'Status by descending order', value: :status_desc
+ value 'SEVERITY_ASC', 'Severity from less critical to more critical', value: :severity_asc
+ value 'SEVERITY_DESC', 'Severity from more critical to less critical', value: :severity_desc
+ value 'STATUS_ASC', 'Status by order: Ignored > Resolved > Acknowledged > Triggered', value: :status_asc
+ value 'STATUS_DESC', 'Status by order: Triggered > Acknowledged > Resolved > Ignored', value: :status_desc
end
end
end
diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb
index 8215ccb152c..089d2426158 100644
--- a/app/graphql/types/alert_management/alert_type.rb
+++ b/app/graphql/types/alert_management/alert_type.rb
@@ -91,6 +91,12 @@ module Types
null: true,
description: 'Assignees of the alert'
+ field :metrics_dashboard_url,
+ GraphQL::STRING_TYPE,
+ null: true,
+ description: 'URL for metrics embed for the alert',
+ resolve: -> (alert, _args, _context) { alert.present.metrics_dashboard_url }
+
def notes
object.ordered_notes
end
diff --git a/app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb b/app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb
new file mode 100644
index 00000000000..ccd1c7dd0eb
--- /dev/null
+++ b/app/graphql/types/ci_configuration/sast/analyzers_entity_type.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Types
+ module CiConfiguration
+ module Sast
+ # rubocop: disable Graphql/AuthorizeTypes
+ class AnalyzersEntityType < BaseObject
+ graphql_name 'SastCiConfigurationAnalyzersEntity'
+ description 'Represents an analyzer entity in SAST CI configuration'
+
+ field :name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the analyzer.'
+
+ field :label, GraphQL::STRING_TYPE, null: true,
+ description: 'Analyzer label used in the config UI.'
+
+ field :enabled, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Indicates whether an analyzer is enabled.'
+
+ field :description, GraphQL::STRING_TYPE, null: true,
+ description: 'Analyzer description that is displayed on the form.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci_configuration/sast/entity_type.rb b/app/graphql/types/ci_configuration/sast/entity_type.rb
new file mode 100644
index 00000000000..b61b582ad20
--- /dev/null
+++ b/app/graphql/types/ci_configuration/sast/entity_type.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Types
+ module CiConfiguration
+ module Sast
+ # rubocop: disable Graphql/AuthorizeTypes
+ class EntityType < BaseObject
+ graphql_name 'SastCiConfigurationEntity'
+ description 'Represents an entity in SAST CI configuration'
+
+ field :field, GraphQL::STRING_TYPE, null: true,
+ description: 'CI keyword of entity.'
+
+ field :label, GraphQL::STRING_TYPE, null: true,
+ description: 'Label for entity used in the form.'
+
+ field :type, GraphQL::STRING_TYPE, null: true,
+ description: 'Type of the field value.'
+
+ field :options, ::Types::CiConfiguration::Sast::OptionsEntityType.connection_type, null: true,
+ description: 'Different possible values of the field.'
+
+ field :default_value, GraphQL::STRING_TYPE, null: true,
+ description: 'Default value that is used if value is empty.'
+
+ field :description, GraphQL::STRING_TYPE, null: true,
+ description: 'Entity description that is displayed on the form.'
+
+ field :value, GraphQL::STRING_TYPE, null: true,
+ description: 'Current value of the entity.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci_configuration/sast/options_entity_type.rb b/app/graphql/types/ci_configuration/sast/options_entity_type.rb
new file mode 100644
index 00000000000..86d104a7fda
--- /dev/null
+++ b/app/graphql/types/ci_configuration/sast/options_entity_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module CiConfiguration
+ module Sast
+ # rubocop: disable Graphql/AuthorizeTypes
+ class OptionsEntityType < BaseObject
+ graphql_name 'SastCiConfigurationOptionsEntity'
+ description 'Represents an entity for options in SAST CI configuration'
+
+ field :label, GraphQL::STRING_TYPE, null: true,
+ description: 'Label of option entity.'
+
+ field :value, GraphQL::STRING_TYPE, null: true,
+ description: 'Value of option entity.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci_configuration/sast/type.rb b/app/graphql/types/ci_configuration/sast/type.rb
new file mode 100644
index 00000000000..35d11584ac7
--- /dev/null
+++ b/app/graphql/types/ci_configuration/sast/type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module CiConfiguration
+ module Sast
+ # rubocop: disable Graphql/AuthorizeTypes
+ class Type < BaseObject
+ graphql_name 'SastCiConfiguration'
+ description 'Represents a CI configuration of SAST'
+
+ field :global, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
+ description: 'List of global entities related to SAST configuration.'
+
+ field :pipeline, ::Types::CiConfiguration::Sast::EntityType.connection_type, null: true,
+ description: 'List of pipeline entities related to SAST configuration.'
+
+ field :analyzers, ::Types::CiConfiguration::Sast::AnalyzersEntityType.connection_type, null: true,
+ description: 'List of analyzers entities attached to SAST configuration.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/container_expiration_policy_type.rb b/app/graphql/types/container_expiration_policy_type.rb
index da53dbcbd39..f19aa964377 100644
--- a/app/graphql/types/container_expiration_policy_type.rb
+++ b/app/graphql/types/container_expiration_policy_type.rb
@@ -14,8 +14,8 @@ module Types
field :older_than, Types::ContainerExpirationPolicyOlderThanEnum, null: true, description: 'Tags older that this will expire'
field :cadence, Types::ContainerExpirationPolicyCadenceEnum, null: false, description: 'This container expiration policy schedule'
field :keep_n, Types::ContainerExpirationPolicyKeepEnum, null: true, description: 'Number of tags to retain'
- field :name_regex, GraphQL::STRING_TYPE, null: true, description: 'Tags with names matching this regex pattern will expire'
- field :name_regex_keep, GraphQL::STRING_TYPE, null: true, description: 'Tags with names matching this regex pattern will be preserved'
+ field :name_regex, Types::UntrustedRegexp, null: true, description: 'Tags with names matching this regex pattern will expire'
+ field :name_regex_keep, Types::UntrustedRegexp, null: true, description: 'Tags with names matching this regex pattern will be preserved'
field :next_run_at, Types::TimeType, null: true, description: 'Next time that this container expiration policy will get executed'
end
end
diff --git a/app/graphql/types/deprecated_mutations.rb b/app/graphql/types/deprecated_mutations.rb
new file mode 100644
index 00000000000..a4336fa3ef3
--- /dev/null
+++ b/app/graphql/types/deprecated_mutations.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module DeprecatedMutations
+ extend ActiveSupport::Concern
+
+ prepended do
+ mount_aliased_mutation 'AddAwardEmoji',
+ Mutations::AwardEmojis::Add,
+ deprecated: { reason: 'Use awardEmojiAdd', milestone: '13.2' }
+ mount_aliased_mutation 'RemoveAwardEmoji',
+ Mutations::AwardEmojis::Remove,
+ deprecated: { reason: 'Use awardEmojiRemove', milestone: '13.2' }
+ mount_aliased_mutation 'ToggleAwardEmoji',
+ Mutations::AwardEmojis::Toggle,
+ deprecated: { reason: 'Use awardEmojiToggle', milestone: '13.2' }
+ end
+ end
+end
diff --git a/app/graphql/types/diff_stats_summary_type.rb b/app/graphql/types/diff_stats_summary_type.rb
new file mode 100644
index 00000000000..956400fd21b
--- /dev/null
+++ b/app/graphql/types/diff_stats_summary_type.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ # Types that use DiffStatsType should have their own authorization
+ class DiffStatsSummaryType < BaseObject
+ graphql_name 'DiffStatsSummary'
+
+ description 'Aggregated summary of changes'
+
+ field :additions, GraphQL::INT_TYPE, null: false,
+ description: 'Number of lines added'
+ field :deletions, GraphQL::INT_TYPE, null: false,
+ description: 'Number of lines deleted'
+ field :changes, GraphQL::INT_TYPE, null: false,
+ description: 'Number of lines changed'
+ field :file_count, GraphQL::INT_TYPE, null: false,
+ description: 'Number of files changed'
+
+ def changes
+ object[:additions] + object[:deletions]
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/diff_stats_type.rb b/app/graphql/types/diff_stats_type.rb
new file mode 100644
index 00000000000..6c79a4c389d
--- /dev/null
+++ b/app/graphql/types/diff_stats_type.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ # Types that use DiffStatsType should have their own authorization
+ class DiffStatsType < BaseObject
+ graphql_name 'DiffStats'
+
+ description 'Changes to a single file'
+
+ field :path, GraphQL::STRING_TYPE, null: false,
+ description: 'File path, relative to repository root'
+ field :additions, GraphQL::INT_TYPE, null: false,
+ description: 'Number of lines added to this file'
+ field :deletions, GraphQL::INT_TYPE, null: false,
+ description: 'Number of lines deleted from this file'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/error_tracking/sentry_detailed_error_type.rb b/app/graphql/types/error_tracking/sentry_detailed_error_type.rb
index 124398f28e7..8bdd8afcbff 100644
--- a/app/graphql/types/error_tracking/sentry_detailed_error_type.rb
+++ b/app/graphql/types/error_tracking/sentry_detailed_error_type.rb
@@ -76,9 +76,15 @@ module Types
description: 'Commit the error was last seen'
field :first_release_short_version, GraphQL::STRING_TYPE,
null: true,
- description: 'Release version the error was first seen'
+ description: 'Release short version the error was first seen'
field :last_release_short_version, GraphQL::STRING_TYPE,
null: true,
+ description: 'Release short version the error was last seen'
+ field :first_release_version, GraphQL::STRING_TYPE,
+ null: true,
+ description: 'Release version the error was first seen'
+ field :last_release_version, GraphQL::STRING_TYPE,
+ null: true,
description: 'Release version the error was last seen'
field :gitlab_commit, GraphQL::STRING_TYPE,
null: true,
diff --git a/app/graphql/types/error_tracking/sentry_error_collection_type.rb b/app/graphql/types/error_tracking/sentry_error_collection_type.rb
index 121146133cb..f423fcb1b9f 100644
--- a/app/graphql/types/error_tracking/sentry_error_collection_type.rb
+++ b/app/graphql/types/error_tracking/sentry_error_collection_type.rb
@@ -17,7 +17,7 @@ module Types
resolver: Resolvers::ErrorTracking::SentryErrorsResolver do
argument :search_term,
String,
- description: 'Search term for the Sentry error.',
+ description: 'Search query for the Sentry error details',
required: false
argument :sort,
String,
diff --git a/app/graphql/types/global_id_type.rb b/app/graphql/types/global_id_type.rb
new file mode 100644
index 00000000000..a3964ba83e1
--- /dev/null
+++ b/app/graphql/types/global_id_type.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module Types
+ class GlobalIDType < BaseScalar
+ graphql_name 'GlobalID'
+ description 'A global identifier'
+
+ # @param value [GID]
+ # @return [String]
+ def self.coerce_result(value, _ctx)
+ ::Gitlab::GlobalId.as_global_id(value).to_s
+ end
+
+ # @param value [String]
+ # @return [GID]
+ def self.coerce_input(value, _ctx)
+ gid = GlobalID.parse(value)
+ raise GraphQL::CoercionError, "#{value.inspect} is not a valid Global ID" if gid.nil?
+ raise GraphQL::CoercionError, "#{value.inspect} is not a Gitlab Global ID" unless gid.app == GlobalID.app
+
+ gid
+ end
+
+ # Construct a restricted type, that can only be inhabited by an ID of
+ # a given model class.
+ def self.[](model_class)
+ @id_types ||= {}
+
+ @id_types[model_class] ||= Class.new(self) do
+ graphql_name "#{model_class.name.gsub(/::/, '')}ID"
+ description "Identifier of #{model_class.name}"
+
+ self.define_singleton_method(:to_s) do
+ graphql_name
+ end
+
+ self.define_singleton_method(:inspect) do
+ graphql_name
+ end
+
+ self.define_singleton_method(:coerce_result) do |gid, ctx|
+ global_id = ::Gitlab::GlobalId.as_global_id(gid, model_name: model_class.name)
+
+ if suitable?(global_id)
+ global_id.to_s
+ else
+ raise GraphQL::CoercionError, "Expected a #{model_class.name} ID, got #{global_id}"
+ end
+ end
+
+ self.define_singleton_method(:suitable?) do |gid|
+ gid&.model_class&.ancestors&.include?(model_class)
+ end
+
+ self.define_singleton_method(:coerce_input) do |string, ctx|
+ gid = super(string, ctx)
+ raise GraphQL::CoercionError, "#{string.inspect} does not represent an instance of #{model_class.name}" unless suitable?(gid)
+
+ gid
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/issue_connection_type.rb b/app/graphql/types/issue_connection_type.rb
new file mode 100644
index 00000000000..beed392f01a
--- /dev/null
+++ b/app/graphql/types/issue_connection_type.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class IssueConnectionType < GraphQL::Types::Relay::BaseConnection
+ field :count, Integer, null: false,
+ description: 'Total count of collection'
+
+ def count
+ object.items.size
+ end
+ end
+end
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index 73219ca9e1e..9baa0018999 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -4,6 +4,8 @@ module Types
class IssueType < BaseObject
graphql_name 'Issue'
+ connection_type_class(Types::IssueConnectionType)
+
implements(Types::Notes::NoteableType)
authorize :read_issue
@@ -12,6 +14,8 @@ module Types
present_using IssuePresenter
+ field :id, GraphQL::ID_TYPE, null: false,
+ description: "ID of the issue"
field :iid, GraphQL::ID_TYPE, null: false,
description: "Internal ID of the issue"
field :title, GraphQL::STRING_TYPE, null: false,
diff --git a/app/graphql/types/jira_user_type.rb b/app/graphql/types/jira_user_type.rb
index 8aa21ce669b..999526a920e 100644
--- a/app/graphql/types/jira_user_type.rb
+++ b/app/graphql/types/jira_user_type.rb
@@ -13,7 +13,11 @@ module Types
field :jira_email, GraphQL::STRING_TYPE, null: true,
description: 'Email of the Jira user, returned only for users with public emails'
field :gitlab_id, GraphQL::INT_TYPE, null: true,
- description: 'Id of the matched GitLab user'
+ description: 'ID of the matched GitLab user'
+ field :gitlab_username, GraphQL::STRING_TYPE, null: true,
+ description: 'Username of the matched GitLab user'
+ field :gitlab_name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the matched GitLab user'
end
# rubocop: enable Graphql/AuthorizeTypes
end
diff --git a/app/graphql/types/jira_users_mapping_input_type.rb b/app/graphql/types/jira_users_mapping_input_type.rb
new file mode 100644
index 00000000000..61cf1474493
--- /dev/null
+++ b/app/graphql/types/jira_users_mapping_input_type.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class JiraUsersMappingInputType < BaseInputObject
+ graphql_name 'JiraUsersMappingInputType'
+
+ argument :jira_account_id,
+ GraphQL::STRING_TYPE,
+ required: true,
+ description: 'Jira account id of the user'
+ argument :gitlab_id,
+ GraphQL::INT_TYPE,
+ required: false,
+ description: 'Id of the GitLab user'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index cb4ff7ea0c5..c194b467363 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -54,6 +54,13 @@ module Types
description: 'Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS)'
field :diff_head_sha, GraphQL::STRING_TYPE, null: true,
description: 'Diff head SHA of the merge request'
+ field :diff_stats, [Types::DiffStatsType], null: true, calls_gitaly: true,
+ description: 'Details about which files were changed in this merge request' do
+ argument :path, GraphQL::STRING_TYPE, required: false, description: 'A specific file-path'
+ end
+
+ field :diff_stats_summary, Types::DiffStatsSummaryType, null: true, calls_gitaly: true,
+ description: 'Summary of which files were changed in this merge request'
field :merge_commit_sha, GraphQL::STRING_TYPE, null: true,
description: 'SHA of the merge request commit (set once merged)'
field :user_notes_count, GraphQL::INT_TYPE, null: true,
@@ -134,5 +141,24 @@ module Types
end
field :task_completion_status, Types::TaskCompletionStatus, null: false,
description: Types::TaskCompletionStatus.description
+
+ def diff_stats(path: nil)
+ stats = Array.wrap(object.diff_stats&.to_a)
+
+ if path.present?
+ stats.select { |s| s.path == path }
+ else
+ stats
+ end
+ end
+
+ def diff_stats_summary
+ nil_stats = { additions: 0, deletions: 0, file_count: 0 }
+ return nil_stats unless object.diff_stats.present?
+
+ object.diff_stats.each_with_object(nil_stats) do |status, hash|
+ hash.merge!(additions: status.additions, deletions: status.deletions, file_count: 1) { |_, x, y| x + y }
+ end
+ end
end
end
diff --git a/app/graphql/types/milestone_stats_type.rb b/app/graphql/types/milestone_stats_type.rb
new file mode 100644
index 00000000000..ef533af59e7
--- /dev/null
+++ b/app/graphql/types/milestone_stats_type.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ class MilestoneStatsType < BaseObject
+ graphql_name 'MilestoneStats'
+ description 'Contains statistics about a milestone'
+
+ authorize :read_milestone
+
+ field :total_issues_count, GraphQL::INT_TYPE, null: true,
+ description: 'Total number of issues associated with the milestone'
+
+ field :closed_issues_count, GraphQL::INT_TYPE, null: true,
+ description: 'Number of closed issues associated with the milestone'
+ end
+end
diff --git a/app/graphql/types/milestone_type.rb b/app/graphql/types/milestone_type.rb
index 99bd6e819d6..ca606c9da44 100644
--- a/app/graphql/types/milestone_type.rb
+++ b/app/graphql/types/milestone_type.rb
@@ -9,6 +9,8 @@ module Types
authorize :read_milestone
+ alias_method :milestone, :object
+
field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the milestone'
@@ -47,5 +49,14 @@ module Types
field :subgroup_milestone, GraphQL::BOOLEAN_TYPE, null: false,
description: 'Indicates if milestone is at subgroup level',
method: :subgroup_milestone?
+
+ field :stats, Types::MilestoneStatsType, null: true,
+ description: 'Milestone statistics'
+
+ def stats
+ return unless Feature.enabled?(:graphql_milestone_stats, milestone.project || milestone.group, default_enabled: true)
+
+ milestone
+ end
end
end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 8874c56dfdb..49d51b626b2 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -10,6 +10,7 @@ module Types
mount_mutation Mutations::AlertManagement::CreateAlertIssue
mount_mutation Mutations::AlertManagement::UpdateAlertStatus
mount_mutation Mutations::AlertManagement::Alerts::SetAssignees
+ mount_mutation Mutations::AlertManagement::Alerts::Todo::Create
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
@@ -17,9 +18,11 @@ module Types
mount_mutation Mutations::Commits::Create, calls_gitaly: true
mount_mutation Mutations::Discussions::ToggleResolve
mount_mutation Mutations::Issues::SetConfidential
+ mount_mutation Mutations::Issues::SetLocked
mount_mutation Mutations::Issues::SetDueDate
mount_mutation Mutations::Issues::Update
mount_mutation Mutations::MergeRequests::Create
+ mount_mutation Mutations::MergeRequests::Update
mount_mutation Mutations::MergeRequests::SetLabels
mount_mutation Mutations::MergeRequests::SetLocked
mount_mutation Mutations::MergeRequests::SetMilestone
@@ -56,4 +59,5 @@ module Types
end
end
+::Types::MutationType.prepend(::Types::DeprecatedMutations)
::Types::MutationType.prepend_if_ee('::EE::Types::MutationType')
diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb
index 1714284a5cf..fbdf049b755 100644
--- a/app/graphql/types/namespace_type.rb
+++ b/app/graphql/types/namespace_type.rb
@@ -38,3 +38,5 @@ module Types
resolver: ::Resolvers::NamespaceProjectsResolver
end
end
+
+Types::NamespaceType.prepend_if_ee('EE::Types::NamespaceType')
diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb
index 8755b4ccad5..5d41f0032bd 100644
--- a/app/graphql/types/notes/note_type.rb
+++ b/app/graphql/types/notes/note_type.rb
@@ -27,6 +27,8 @@ module Types
field :system, GraphQL::BOOLEAN_TYPE,
null: false,
description: 'Indicates whether this note was created by the system or by a user'
+ field :system_note_icon_name, GraphQL::STRING_TYPE, null: true,
+ description: 'Name of the icon corresponding to a system note'
field :body, GraphQL::STRING_TYPE,
null: false,
@@ -46,6 +48,10 @@ module Types
field :confidential, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if this note is confidential',
method: :confidential?
+
+ def system_note_icon_name
+ SystemNoteHelper.system_note_icon_name(object) if object.system?
+ end
end
end
end
diff --git a/app/graphql/types/package_type.rb b/app/graphql/types/package_type.rb
new file mode 100644
index 00000000000..0604bf827a5
--- /dev/null
+++ b/app/graphql/types/package_type.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ class PackageType < BaseObject
+ graphql_name 'Package'
+ description 'Represents a package'
+ authorize :read_package
+
+ field :id, GraphQL::ID_TYPE, null: false, description: 'The ID of the package'
+ field :name, GraphQL::STRING_TYPE, null: false, description: 'The name of the package'
+ field :created_at, Types::TimeType, null: false, description: 'The created date'
+ field :updated_at, Types::TimeType, null: false, description: 'The update date'
+ field :version, GraphQL::STRING_TYPE, null: true, description: 'The version of the package'
+ field :package_type, Types::PackageTypeEnum, null: false, description: 'The type of the package'
+ end
+end
diff --git a/app/graphql/types/package_type_enum.rb b/app/graphql/types/package_type_enum.rb
new file mode 100644
index 00000000000..bc03b8f5f8b
--- /dev/null
+++ b/app/graphql/types/package_type_enum.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Types
+ class PackageTypeEnum < BaseEnum
+ ::Packages::Package.package_types.keys.each do |package_type|
+ value package_type.to_s.upcase, "Packages from the #{package_type} package manager", value: package_type.to_s
+ end
+ end
+end
diff --git a/app/graphql/types/project_statistics_type.rb b/app/graphql/types/project_statistics_type.rb
index e1546d31e89..b3916e42e92 100644
--- a/app/graphql/types/project_statistics_type.rb
+++ b/app/graphql/types/project_statistics_type.rb
@@ -21,5 +21,7 @@ module Types
description: 'Packages size of the project'
field :wiki_size, GraphQL::FLOAT_TYPE, null: true,
description: 'Wiki size of the project'
+ field :snippets_size, GraphQL::FLOAT_TYPE, null: true,
+ description: 'Snippets size of the project'
end
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index bbfb7fc4f20..2251a0f4e0c 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -60,6 +60,12 @@ module Types
field :merge_requests_ff_only_enabled, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.'
+ field :service_desk_enabled, GraphQL::BOOLEAN_TYPE, null: true,
+ description: 'Indicates if the project has service desk enabled.'
+
+ field :service_desk_address, GraphQL::STRING_TYPE, null: true,
+ description: 'E-mail address of the service desk.'
+
field :avatar_url, GraphQL::STRING_TYPE, null: true, calls_gitaly: true,
description: 'URL to avatar image file of the project',
resolve: -> (project, args, ctx) do
@@ -153,12 +159,20 @@ module Types
description: 'Environments of the project',
resolver: Resolvers::EnvironmentsResolver
+ field :sast_ci_configuration, ::Types::CiConfiguration::Sast::Type, null: true,
+ description: 'SAST CI configuration for the project',
+ resolver: ::Resolvers::CiConfiguration::SastResolver
+
field :issue,
Types::IssueType,
null: true,
description: 'A single issue of the project',
resolver: Resolvers::IssuesResolver.single
+ field :packages, Types::PackageType.connection_type, null: true,
+ description: 'Packages of the project',
+ resolver: Resolvers::PackagesResolver
+
field :pipelines,
Types::Ci::PipelineType.connection_type,
null: true,
@@ -243,15 +257,14 @@ module Types
Types::ReleaseType.connection_type,
null: true,
description: 'Releases of the project',
- resolver: Resolvers::ReleasesResolver,
- feature_flag: :graphql_release_data
+ resolver: Resolvers::ReleasesResolver
field :release,
Types::ReleaseType,
null: true,
description: 'A single release of the project',
resolver: Resolvers::ReleasesResolver.single,
- feature_flag: :graphql_release_data
+ authorize: :download_code
field :container_expiration_policy,
Types::ContainerExpirationPolicyType,
diff --git a/app/graphql/types/projects/services/jira_service_type.rb b/app/graphql/types/projects/services/jira_service_type.rb
index e81963f752d..8bf85a14cbf 100644
--- a/app/graphql/types/projects/services/jira_service_type.rb
+++ b/app/graphql/types/projects/services/jira_service_type.rb
@@ -15,7 +15,7 @@ module Types
null: true,
connection: false,
extensions: [Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension],
- description: 'List of Jira projects fetched through Jira REST API',
+ description: 'List of all Jira projects fetched through Jira REST API',
resolver: Resolvers::Projects::JiraProjectsResolver
end
end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 362e4004b73..b4cbd96bfdb 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -61,10 +61,6 @@ module Types
description: 'Text to echo back',
resolver: Resolvers::EchoResolver
- field :user, Types::UserType, null: true,
- description: 'Find a user on this instance',
- resolver: Resolvers::UserResolver
-
def design_management
DesignManagementObject.new(nil)
end
diff --git a/app/graphql/types/release_link_type.rb b/app/graphql/types/release_asset_link_type.rb
index 070f14a90df..21f1bd50cff 100644
--- a/app/graphql/types/release_link_type.rb
+++ b/app/graphql/types/release_asset_link_type.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
module Types
- class ReleaseLinkType < BaseObject
- graphql_name 'ReleaseLink'
+ class ReleaseAssetLinkType < BaseObject
+ graphql_name 'ReleaseAssetLink'
+ description 'Represents an asset link associated with a release'
authorize :read_release
@@ -12,7 +13,7 @@ module Types
description: 'Name of the link'
field :url, GraphQL::STRING_TYPE, null: true,
description: 'URL of the link'
- field :link_type, Types::ReleaseLinkTypeEnum, null: true,
+ field :link_type, Types::ReleaseAssetLinkTypeEnum, null: true,
description: 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`'
field :external, GraphQL::BOOLEAN_TYPE, null: true, method: :external?,
description: 'Indicates the link points to an external resource'
diff --git a/app/graphql/types/release_link_type_enum.rb b/app/graphql/types/release_asset_link_type_enum.rb
index b364855833f..01862ada56d 100644
--- a/app/graphql/types/release_link_type_enum.rb
+++ b/app/graphql/types/release_asset_link_type_enum.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module Types
- class ReleaseLinkTypeEnum < BaseEnum
- graphql_name 'ReleaseLinkType'
+ class ReleaseAssetLinkTypeEnum < BaseEnum
+ graphql_name 'ReleaseAssetLinkType'
description 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`'
::Releases::Link.link_types.keys.each do |link_type|
diff --git a/app/graphql/types/release_assets_type.rb b/app/graphql/types/release_assets_type.rb
index 58ad05b5365..d6042bdbc0b 100644
--- a/app/graphql/types/release_assets_type.rb
+++ b/app/graphql/types/release_assets_type.rb
@@ -3,6 +3,7 @@
module Types
class ReleaseAssetsType < BaseObject
graphql_name 'ReleaseAssets'
+ description 'A container for all assets associated with a release'
authorize :read_release
@@ -10,9 +11,9 @@ module Types
present_using ReleasePresenter
- field :assets_count, GraphQL::INT_TYPE, null: true,
+ field :count, GraphQL::INT_TYPE, null: true, method: :assets_count,
description: 'Number of assets of the release'
- field :links, Types::ReleaseLinkType.connection_type, null: true,
+ field :links, Types::ReleaseAssetLinkType.connection_type, null: true,
description: 'Asset links of the release'
field :sources, Types::ReleaseSourceType.connection_type, null: true,
description: 'Sources of the release'
diff --git a/app/graphql/types/release_links_type.rb b/app/graphql/types/release_links_type.rb
new file mode 100644
index 00000000000..f61a16f5b67
--- /dev/null
+++ b/app/graphql/types/release_links_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ class ReleaseLinksType < BaseObject
+ graphql_name 'ReleaseLinks'
+
+ authorize :download_code
+
+ alias_method :release, :object
+
+ present_using ReleasePresenter
+
+ field :self_url, GraphQL::STRING_TYPE, null: true,
+ description: 'HTTP URL of the release'
+ field :merge_requests_url, GraphQL::STRING_TYPE, null: true,
+ description: 'HTTP URL of the merge request page filtered by this release'
+ field :issues_url, GraphQL::STRING_TYPE, null: true,
+ description: 'HTTP URL of the issues page filtered by this release'
+ field :edit_url, GraphQL::STRING_TYPE, null: true,
+ description: "HTTP URL of the release's edit page",
+ authorize: :update_release
+ end
+end
diff --git a/app/graphql/types/release_source_type.rb b/app/graphql/types/release_source_type.rb
index 0ec1ad85a39..891da472116 100644
--- a/app/graphql/types/release_source_type.rb
+++ b/app/graphql/types/release_source_type.rb
@@ -3,8 +3,9 @@
module Types
class ReleaseSourceType < BaseObject
graphql_name 'ReleaseSource'
+ description 'Represents the source code attached to a release in a particular format'
- authorize :read_release_sources
+ authorize :download_code
field :format, GraphQL::STRING_TYPE, null: true,
description: 'Format of the source'
diff --git a/app/graphql/types/release_type.rb b/app/graphql/types/release_type.rb
index 3d8e5a93c68..a0703b96a36 100644
--- a/app/graphql/types/release_type.rb
+++ b/app/graphql/types/release_type.rb
@@ -3,6 +3,7 @@
module Types
class ReleaseType < BaseObject
graphql_name 'Release'
+ description 'Represents a release'
authorize :read_release
@@ -10,10 +11,12 @@ module Types
present_using ReleasePresenter
- field :tag_name, GraphQL::STRING_TYPE, null: false, method: :tag,
- description: 'Name of the tag associated with the release'
+ field :tag_name, GraphQL::STRING_TYPE, null: true, method: :tag,
+ description: 'Name of the tag associated with the release',
+ authorize: :download_code
field :tag_path, GraphQL::STRING_TYPE, null: true,
- description: 'Relative web path to the tag associated with the release'
+ description: 'Relative web path to the tag associated with the release',
+ authorize: :download_code
field :description, GraphQL::STRING_TYPE, null: true,
description: 'Description (also known as "release notes") of the release'
markdown_field :description_html, null: true
@@ -25,6 +28,8 @@ module Types
description: 'Timestamp of when the release was released'
field :assets, Types::ReleaseAssetsType, null: true, method: :itself,
description: 'Assets of the release'
+ field :links, Types::ReleaseLinksType, null: true, method: :itself,
+ description: 'Links of the release'
field :milestones, Types::MilestoneType.connection_type, null: true,
description: 'Milestones associated to the release'
field :evidences, Types::EvidenceType.connection_type, null: true,
@@ -39,8 +44,7 @@ module Types
field :commit, Types::CommitType, null: true,
complexity: 10, calls_gitaly: true,
- description: 'The commit associated with the release',
- authorize: :reporter_access
+ description: 'The commit associated with the release'
def commit
return if release.sha.nil?
diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb
index e2d85aebc48..3acc1d9ca44 100644
--- a/app/graphql/types/root_storage_statistics_type.rb
+++ b/app/graphql/types/root_storage_statistics_type.rb
@@ -12,5 +12,6 @@ module Types
field :build_artifacts_size, GraphQL::FLOAT_TYPE, null: false, description: 'The CI artifacts size in bytes'
field :packages_size, GraphQL::FLOAT_TYPE, null: false, description: 'The packages size in bytes'
field :wiki_size, GraphQL::FLOAT_TYPE, null: false, description: 'The wiki size in bytes'
+ field :snippets_size, GraphQL::FLOAT_TYPE, null: false, description: 'The snippets size in bytes'
end
end
diff --git a/app/graphql/types/todo_target_enum.rb b/app/graphql/types/todo_target_enum.rb
index a377c3aafdc..b797722fef8 100644
--- a/app/graphql/types/todo_target_enum.rb
+++ b/app/graphql/types/todo_target_enum.rb
@@ -6,6 +6,7 @@ module Types
value 'ISSUE', value: 'Issue', description: 'An Issue'
value 'MERGEREQUEST', value: 'MergeRequest', description: 'A MergeRequest'
value 'DESIGN', value: 'DesignManagement::Design', description: 'A Design'
+ value 'ALERT', value: 'AlertManagement::Alert', description: 'An Alert'
end
end
diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb
index 22349203519..36cae756a0d 100644
--- a/app/graphql/types/tree/blob_type.rb
+++ b/app/graphql/types/tree/blob_type.rb
@@ -17,6 +17,8 @@ module Types
resolve: -> (blob, args, ctx) do
Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(blob.repository, blob.id).find
end
+ field :mode, GraphQL::STRING_TYPE, null: true,
+ description: 'Blob mode in numeric format'
# rubocop: enable Graphql/AuthorizeTypes
end
end
diff --git a/app/graphql/types/untrusted_regexp.rb b/app/graphql/types/untrusted_regexp.rb
new file mode 100644
index 00000000000..2c715ab4967
--- /dev/null
+++ b/app/graphql/types/untrusted_regexp.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ class UntrustedRegexp < Types::BaseScalar
+ description 'A regexp containing patterns sourced from user input'
+
+ def self.coerce_input(input_value, _)
+ return unless input_value
+
+ Gitlab::UntrustedRegexp.new(input_value)
+
+ input_value
+ rescue RegexpError => e
+ message = "#{input_value} is an invalid regexp: #{e.message}"
+ raise GraphQL::CoercionError, message
+ end
+
+ def self.coerce_result(ruby_value, _)
+ ruby_value.to_s
+ end
+ end
+end