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:
Diffstat (limited to 'app/graphql')
-rw-r--r--app/graphql/graphql_triggers.rb24
-rw-r--r--app/graphql/mutations/achievements/create.rb4
-rw-r--r--app/graphql/mutations/ci/job_token_scope/add_project.rb15
-rw-r--r--app/graphql/mutations/ci/job_token_scope/remove_project.rb15
-rw-r--r--app/graphql/mutations/ci/pipeline_schedule/update.rb60
-rw-r--r--app/graphql/mutations/ci/project_ci_cd_settings_update.rb4
-rw-r--r--app/graphql/mutations/concerns/mutations/assignable.rb2
-rw-r--r--app/graphql/mutations/concerns/mutations/work_items/widgetable.rb2
-rw-r--r--app/graphql/mutations/issues/bulk_update.rb106
-rw-r--r--app/graphql/mutations/issues/create.rb2
-rw-r--r--app/graphql/mutations/issues/move.rb2
-rw-r--r--app/graphql/mutations/issues/set_confidential.rb2
-rw-r--r--app/graphql/mutations/issues/set_due_date.rb2
-rw-r--r--app/graphql/mutations/issues/set_escalation_status.rb2
-rw-r--r--app/graphql/mutations/issues/set_locked.rb2
-rw-r--r--app/graphql/mutations/issues/set_severity.rb2
-rw-r--r--app/graphql/mutations/issues/update.rb16
-rw-r--r--app/graphql/mutations/merge_requests/set_milestone.rb2
-rw-r--r--app/graphql/mutations/merge_requests/update.rb27
-rw-r--r--app/graphql/mutations/work_items/create.rb2
-rw-r--r--app/graphql/mutations/work_items/delete.rb2
-rw-r--r--app/graphql/mutations/work_items/update.rb35
-rw-r--r--app/graphql/mutations/work_items/update_task.rb2
-rw-r--r--app/graphql/queries/pipelines/get_pipeline_details.query.graphql1
-rw-r--r--app/graphql/resolvers/board_list_issues_resolver.rb2
-rw-r--r--app/graphql/resolvers/board_list_resolver.rb3
-rw-r--r--app/graphql/resolvers/board_lists_resolver.rb2
-rw-r--r--app/graphql/resolvers/ci/runner_jobs_resolver.rb1
-rw-r--r--app/graphql/resolvers/ci/variables_resolver.rb27
-rw-r--r--app/graphql/resolvers/concerns/board_item_filterable.rb6
-rw-r--r--app/graphql/resolvers/concerns/resolves_groups.rb4
-rw-r--r--app/graphql/resolvers/concerns/search_arguments.rb11
-rw-r--r--app/graphql/resolvers/data_transfer_resolver.rb57
-rw-r--r--app/graphql/resolvers/group_releases_resolver.rb30
-rw-r--r--app/graphql/resolvers/groups_resolver.rb19
-rw-r--r--app/graphql/resolvers/issues_resolver.rb3
-rw-r--r--app/graphql/resolvers/namespace_projects_resolver.rb18
-rw-r--r--app/graphql/resolvers/nested_groups_resolver.rb37
-rw-r--r--app/graphql/resolvers/notes/synthetic_note_resolver.rb35
-rw-r--r--app/graphql/resolvers/project_pipeline_schedules_resolver.rb9
-rw-r--r--app/graphql/resolvers/projects/services_resolver.rb1
-rw-r--r--app/graphql/resolvers/projects_resolver.rb18
-rw-r--r--app/graphql/resolvers/releases_resolver.rb2
-rw-r--r--app/graphql/resolvers/saved_reply_resolver.rb23
-rw-r--r--app/graphql/resolvers/work_items_resolver.rb17
-rw-r--r--app/graphql/subscriptions/notes/base.rb27
-rw-r--r--app/graphql/subscriptions/notes/created.rb20
-rw-r--r--app/graphql/subscriptions/notes/deleted.rb23
-rw-r--r--app/graphql/subscriptions/notes/updated.rb9
-rw-r--r--app/graphql/types/achievements/achievement_type.rb5
-rw-r--r--app/graphql/types/base_argument.rb2
-rw-r--r--app/graphql/types/base_enum.rb2
-rw-r--r--app/graphql/types/base_field.rb2
-rw-r--r--app/graphql/types/ci/ci_cd_setting_type.rb5
-rw-r--r--app/graphql/types/ci/code_quality_report_summary_type.rb20
-rw-r--r--app/graphql/types/ci/job_token_scope/direction_enum.rb21
-rw-r--r--app/graphql/types/ci/job_token_scope_type.rb18
-rw-r--r--app/graphql/types/ci/job_type.rb4
-rw-r--r--app/graphql/types/ci/pipeline_type.rb4
-rw-r--r--app/graphql/types/ci/runner_type.rb15
-rw-r--r--app/graphql/types/ci/runner_upgrade_status_enum.rb12
-rw-r--r--app/graphql/types/ci/variable_sort_enum.rb13
-rw-r--r--app/graphql/types/concerns/gitlab_style_deprecations.rb36
-rw-r--r--app/graphql/types/data_transfer/base_type.rb13
-rw-r--r--app/graphql/types/data_transfer/egress_node_type.rb37
-rw-r--r--app/graphql/types/data_transfer/group_data_transfer_type.rb10
-rw-r--r--app/graphql/types/data_transfer/project_data_transfer_type.rb20
-rw-r--r--app/graphql/types/deployment_type.rb4
-rw-r--r--app/graphql/types/group_release_sort_enum.rb15
-rw-r--r--app/graphql/types/group_type.rb15
-rw-r--r--app/graphql/types/issue_type.rb32
-rw-r--r--app/graphql/types/merge_request_type.rb4
-rw-r--r--app/graphql/types/mutation_type.rb2
-rw-r--r--app/graphql/types/notes/deleted_note_type.rb23
-rw-r--r--app/graphql/types/packages/package_details_type.rb6
-rw-r--r--app/graphql/types/permission_types/base_permission_type.rb2
-rw-r--r--app/graphql/types/permission_types/merge_request.rb5
-rw-r--r--app/graphql/types/permission_types/work_item.rb2
-rw-r--r--app/graphql/types/project_type.rb51
-rw-r--r--app/graphql/types/projects/namespace_project_sort_enum.rb1
-rw-r--r--app/graphql/types/projects/service_type.rb1
-rw-r--r--app/graphql/types/projects/service_type_enum.rb1
-rw-r--r--app/graphql/types/projects/services/base_service_type.rb1
-rw-r--r--app/graphql/types/projects/services/jira_project_type.rb1
-rw-r--r--app/graphql/types/projects/services/jira_service_type.rb1
-rw-r--r--app/graphql/types/query_type.rb41
-rw-r--r--app/graphql/types/release_asset_link_type.rb8
-rw-r--r--app/graphql/types/saved_reply_type.rb2
-rw-r--r--app/graphql/types/subscription_type.rb65
-rw-r--r--app/graphql/types/user_interface.rb5
-rw-r--r--app/graphql/types/work_item_id_type.rb4
-rw-r--r--app/graphql/types/work_item_type.rb3
-rw-r--r--app/graphql/types/work_items/widget_type_enum.rb4
93 files changed, 1074 insertions, 201 deletions
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb
index 7f83b62a2ff..89656f1e018 100644
--- a/app/graphql/graphql_triggers.rb
+++ b/app/graphql/graphql_triggers.rb
@@ -29,27 +29,33 @@ module GraphqlTriggers
GitlabSchema.subscriptions.trigger('issuableMilestoneUpdated', { issuable_id: issuable.to_gid }, issuable)
end
+ def self.work_item_note_created(work_item_gid, note_data)
+ GitlabSchema.subscriptions.trigger('workItemNoteCreated', { noteable_id: work_item_gid }, note_data)
+ end
+
+ def self.work_item_note_deleted(work_item_gid, note_data)
+ GitlabSchema.subscriptions.trigger('workItemNoteDeleted', { noteable_id: work_item_gid }, note_data)
+ end
+
+ def self.work_item_note_updated(work_item_gid, note_data)
+ GitlabSchema.subscriptions.trigger('workItemNoteUpdated', { noteable_id: work_item_gid }, note_data)
+ end
+
def self.merge_request_reviewers_updated(merge_request)
GitlabSchema.subscriptions.trigger(
- 'mergeRequestReviewersUpdated',
- { issuable_id: merge_request.to_gid },
- merge_request
+ 'mergeRequestReviewersUpdated', { issuable_id: merge_request.to_gid }, merge_request
)
end
def self.merge_request_merge_status_updated(merge_request)
GitlabSchema.subscriptions.trigger(
- 'mergeRequestMergeStatusUpdated',
- { issuable_id: merge_request.to_gid },
- merge_request
+ 'mergeRequestMergeStatusUpdated', { issuable_id: merge_request.to_gid }, merge_request
)
end
def self.merge_request_approval_state_updated(merge_request)
GitlabSchema.subscriptions.trigger(
- 'mergeRequestApprovalStateUpdated',
- { issuable_id: merge_request.to_gid },
- merge_request
+ 'mergeRequestApprovalStateUpdated', { issuable_id: merge_request.to_gid }, merge_request
)
end
end
diff --git a/app/graphql/mutations/achievements/create.rb b/app/graphql/mutations/achievements/create.rb
index 6cfe6c0e643..310a653c705 100644
--- a/app/graphql/mutations/achievements/create.rb
+++ b/app/graphql/mutations/achievements/create.rb
@@ -28,10 +28,6 @@ module Mutations
required: false,
description: 'Description of or notes for the achievement.'
- argument :revokeable, GraphQL::Types::Boolean,
- required: true,
- description: 'Revokeability for the achievement.'
-
authorize :admin_achievement
def resolve(args)
diff --git a/app/graphql/mutations/ci/job_token_scope/add_project.rb b/app/graphql/mutations/ci/job_token_scope/add_project.rb
index e16c08cb116..6f0f87b47a1 100644
--- a/app/graphql/mutations/ci/job_token_scope/add_project.rb
+++ b/app/graphql/mutations/ci/job_token_scope/add_project.rb
@@ -18,18 +18,23 @@ module Mutations
required: true,
description: 'Project to be added to the CI job token scope.'
+ argument :direction,
+ ::Types::Ci::JobTokenScope::DirectionEnum,
+ required: false,
+ description: 'Direction of access, which defaults to outbound.'
+
field :ci_job_token_scope,
- Types::Ci::JobTokenScopeType,
- null: true,
- description: "CI job token's scope of access."
+ Types::Ci::JobTokenScopeType,
+ null: true,
+ description: "CI job token's access scope."
- def resolve(project_path:, target_project_path:)
+ def resolve(project_path:, target_project_path:, direction: :outbound)
project = authorized_find!(project_path)
target_project = Project.find_by_full_path(target_project_path)
result = ::Ci::JobTokenScope::AddProjectService
.new(project, current_user)
- .execute(target_project)
+ .execute(target_project, direction: direction)
if result.success?
{
diff --git a/app/graphql/mutations/ci/job_token_scope/remove_project.rb b/app/graphql/mutations/ci/job_token_scope/remove_project.rb
index f503b4f2f7a..20e991f5388 100644
--- a/app/graphql/mutations/ci/job_token_scope/remove_project.rb
+++ b/app/graphql/mutations/ci/job_token_scope/remove_project.rb
@@ -18,18 +18,23 @@ module Mutations
required: true,
description: 'Project to be removed from the CI job token scope.'
+ argument :direction,
+ ::Types::Ci::JobTokenScope::DirectionEnum,
+ required: false,
+ description: 'Direction of access, which defaults to outbound.'
+
field :ci_job_token_scope,
- Types::Ci::JobTokenScopeType,
- null: true,
- description: "CI job token's scope of access."
+ Types::Ci::JobTokenScopeType,
+ null: true,
+ description: "CI job token's scope of access."
- def resolve(project_path:, target_project_path:)
+ def resolve(project_path:, target_project_path:, direction: :outbound)
project = authorized_find!(project_path)
target_project = Project.find_by_full_path(target_project_path)
result = ::Ci::JobTokenScope::RemoveProjectService
.new(project, current_user)
- .execute(target_project)
+ .execute(target_project, direction)
if result.success?
{
diff --git a/app/graphql/mutations/ci/pipeline_schedule/update.rb b/app/graphql/mutations/ci/pipeline_schedule/update.rb
new file mode 100644
index 00000000000..a0b5e793ecb
--- /dev/null
+++ b/app/graphql/mutations/ci/pipeline_schedule/update.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module PipelineSchedule
+ class Update < Base
+ graphql_name 'PipelineScheduleUpdate'
+
+ authorize :update_pipeline_schedule
+
+ argument :description, GraphQL::Types::String,
+ required: false,
+ description: 'Description of the pipeline schedule.'
+
+ argument :cron, GraphQL::Types::String,
+ required: false,
+ description: 'Cron expression of the pipeline schedule.'
+
+ argument :cron_timezone, GraphQL::Types::String,
+ required: false,
+ description:
+ <<-STR
+ Cron time zone supported by ActiveSupport::TimeZone.
+ For example: "Pacific Time (US & Canada)" (default: "UTC").
+ STR
+
+ argument :ref, GraphQL::Types::String,
+ required: false,
+ description: 'Ref of the pipeline schedule.'
+
+ argument :active, GraphQL::Types::Boolean,
+ required: false,
+ description: 'Indicates if the pipeline schedule should be active or not.'
+
+ argument :variables, [Mutations::Ci::PipelineSchedule::VariableInputType],
+ required: false,
+ description: 'Variables for the pipeline schedule.'
+
+ field :pipeline_schedule,
+ Types::Ci::PipelineScheduleType,
+ description: 'Updated pipeline schedule.'
+
+ def resolve(id:, variables: [], **pipeline_schedule_attrs)
+ schedule = authorized_find!(id: id)
+
+ params = pipeline_schedule_attrs.merge(variables_attributes: variables.map(&:to_h))
+
+ service_response = ::Ci::PipelineSchedules::UpdateService
+ .new(schedule, current_user, params)
+ .execute
+
+ {
+ pipeline_schedule: schedule,
+ errors: service_response.errors
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
index 934d62e92cf..d214aa46cfc 100644
--- a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
+++ b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb
@@ -27,6 +27,10 @@ module Mutations
description: 'Indicates CI/CD job tokens generated in other projects ' \
'have restricted access to this project.'
+ argument :opt_in_jwt, GraphQL::Types::Boolean,
+ required: false,
+ description: 'When disabled, the JSON Web Token is always available in all jobs in the pipeline.'
+
field :ci_cd_settings,
Types::Ci::CiCdSettingType,
null: false,
diff --git a/app/graphql/mutations/concerns/mutations/assignable.rb b/app/graphql/mutations/concerns/mutations/assignable.rb
index 86f37207a2d..189c926fcc4 100644
--- a/app/graphql/mutations/concerns/mutations/assignable.rb
+++ b/app/graphql/mutations/concerns/mutations/assignable.rb
@@ -33,7 +33,7 @@ module Mutations
def assign!(resource, users, operation_mode)
update_service_class.new(
- project: resource.project,
+ **update_service_class.constructor_container_arg(resource.project),
current_user: current_user,
params: { assignee_ids: assignee_ids(resource, users, operation_mode) }
).execute(resource)
diff --git a/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb b/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb
index 508e1627032..3f32cd51ae7 100644
--- a/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb
+++ b/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb
@@ -7,7 +7,7 @@ module Mutations
def extract_widget_params!(work_item_type, attributes)
# Get the list of widgets for the work item's type to extract only the supported attributes
- widget_keys = ::WorkItems::Type.available_widgets.map(&:api_symbol)
+ widget_keys = ::WorkItems::WidgetDefinition.available_widgets.map(&:api_symbol)
widget_params = attributes.extract!(*widget_keys)
not_supported_keys = widget_params.keys - work_item_type.widgets.map(&:api_symbol)
diff --git a/app/graphql/mutations/issues/bulk_update.rb b/app/graphql/mutations/issues/bulk_update.rb
new file mode 100644
index 00000000000..7f3d5f6ffb2
--- /dev/null
+++ b/app/graphql/mutations/issues/bulk_update.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Issues
+ class BulkUpdate < BaseMutation
+ graphql_name 'IssuesBulkUpdate'
+
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ MAX_ISSUES = 100
+
+ description 'Allows updating several properties for a set of issues. ' \
+ 'Does nothing if the `bulk_update_issues_mutation` feature flag is disabled.'
+
+ argument :parent_id, ::Types::GlobalIDType[::IssueParent],
+ required: true,
+ description: 'Global ID of the parent that the bulk update will be scoped to . ' \
+ 'Example `IssueParentID` are `"gid://gitlab/Project/1"` and `"gid://gitlab/Group/1"`.'
+
+ argument :ids, [::Types::GlobalIDType[::Issue]],
+ required: true,
+ description: 'Global ID array of the issues that will be updated. ' \
+ "IDs that the user can\'t update will be ignored. A max of #{MAX_ISSUES} can be provided."
+
+ argument :assignee_ids, [::Types::GlobalIDType[::User]],
+ required: false,
+ description: 'Global ID array of the users that will be assigned to the given issues. ' \
+ 'Existing assignees will be replaced with the ones on this list.'
+
+ argument :milestone_id, ::Types::GlobalIDType[::Milestone],
+ required: false,
+ description: 'Global ID of the milestone that will be assigned to the issues.'
+
+ field :updated_issue_count, GraphQL::Types::Int,
+ null: true,
+ description: 'Number of issues that were successfully updated.'
+
+ def ready?(**args)
+ if Feature.disabled?(:bulk_update_issues_mutation)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable, '`bulk_update_issues_mutation` feature flag is disabled.'
+ end
+
+ if args[:ids].size > MAX_ISSUES
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ format(_('No more than %{max_issues} issues can be updated at the same time'), max_issues: MAX_ISSUES)
+ end
+
+ super
+ end
+
+ def resolve(ids:, parent_id:, **attributes)
+ parent = find_parent!(parent_id)
+
+ result = Issuable::BulkUpdateService.new(
+ parent,
+ current_user,
+ prepared_params(attributes, ids)
+ ).execute('issue')
+
+ if result.success?
+ { updated_issue_count: result.payload[:count], errors: [] }
+ else
+ { errors: result.errors }
+ end
+ end
+
+ private
+
+ def find_parent!(parent_id)
+ parent = GitlabSchema.find_by_gid(parent_id).sync
+ raise_resource_not_available_error! unless current_user.can?("read_#{parent.to_ability_name}", parent)
+
+ parent
+ end
+
+ def prepared_params(attributes, ids)
+ prepared = { issuable_ids: model_ids_from(ids).uniq }
+
+ global_id_arguments.each do |argument|
+ next unless attributes.key?(argument)
+
+ prepared[argument] = model_ids_from(attributes[argument])
+ end
+
+ prepared.transform_keys(param_mappings)
+ end
+
+ def param_mappings
+ {}
+ end
+
+ def global_id_arguments
+ %i[assignee_ids milestone_id]
+ end
+
+ def model_ids_from(attributes)
+ return if attributes.nil?
+ return attributes.map(&:model_id) if attributes.is_a?(Array)
+
+ attributes.model_id
+ end
+ end
+ end
+end
+
+Mutations::Issues::BulkUpdate.prepend_mod
diff --git a/app/graphql/mutations/issues/create.rb b/app/graphql/mutations/issues/create.rb
index 0389a482822..0c1acdf316e 100644
--- a/app/graphql/mutations/issues/create.rb
+++ b/app/graphql/mutations/issues/create.rb
@@ -83,7 +83,7 @@ module Mutations
params = build_create_issue_params(attributes.merge(author_id: current_user.id), project)
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
- result = ::Issues::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute
+ result = ::Issues::CreateService.new(container: project, current_user: current_user, params: params, spam_params: spam_params).execute
check_spam_action_response!(result[:issue]) if result[:issue]
diff --git a/app/graphql/mutations/issues/move.rb b/app/graphql/mutations/issues/move.rb
index 63bc9dabbf9..ef3f70c78b9 100644
--- a/app/graphql/mutations/issues/move.rb
+++ b/app/graphql/mutations/issues/move.rb
@@ -18,7 +18,7 @@ module Mutations
target_project = resolve_project(full_path: target_project_path).sync
begin
- moved_issue = ::Issues::MoveService.new(project: source_project, current_user: current_user).execute(issue, target_project)
+ moved_issue = ::Issues::MoveService.new(container: source_project, current_user: current_user).execute(issue, target_project)
rescue ::Issues::MoveService::MoveError => e
errors = e.message
end
diff --git a/app/graphql/mutations/issues/set_confidential.rb b/app/graphql/mutations/issues/set_confidential.rb
index b795d66c16f..08578881a13 100644
--- a/app/graphql/mutations/issues/set_confidential.rb
+++ b/app/graphql/mutations/issues/set_confidential.rb
@@ -19,7 +19,7 @@ module Mutations
# spam_params so a check can be performed.
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
- ::Issues::UpdateService.new(project: project, current_user: current_user, params: { confidential: confidential }, spam_params: spam_params)
+ ::Issues::UpdateService.new(container: project, current_user: current_user, params: { confidential: confidential }, spam_params: spam_params)
.execute(issue)
check_spam_action_response!(issue)
diff --git a/app/graphql/mutations/issues/set_due_date.rb b/app/graphql/mutations/issues/set_due_date.rb
index 70b76da4fcb..e361d241083 100644
--- a/app/graphql/mutations/issues/set_due_date.rb
+++ b/app/graphql/mutations/issues/set_due_date.rb
@@ -14,7 +14,7 @@ module Mutations
issue = authorized_find!(project_path: project_path, iid: iid)
project = issue.project
- ::Issues::UpdateService.new(project: project, current_user: current_user, params: { due_date: due_date })
+ ::Issues::UpdateService.new(container: project, current_user: current_user, params: { due_date: due_date })
.execute(issue)
{
diff --git a/app/graphql/mutations/issues/set_escalation_status.rb b/app/graphql/mutations/issues/set_escalation_status.rb
index 4f3fcb4886d..13286034ada 100644
--- a/app/graphql/mutations/issues/set_escalation_status.rb
+++ b/app/graphql/mutations/issues/set_escalation_status.rb
@@ -17,7 +17,7 @@ module Mutations
check_feature_availability!(issue)
::Issues::UpdateService.new(
- project: project,
+ container: project,
current_user: current_user,
params: { escalation_status: { status: status } }
).execute(issue)
diff --git a/app/graphql/mutations/issues/set_locked.rb b/app/graphql/mutations/issues/set_locked.rb
index 93b31350bbf..86ad129f4cb 100644
--- a/app/graphql/mutations/issues/set_locked.rb
+++ b/app/graphql/mutations/issues/set_locked.rb
@@ -13,7 +13,7 @@ module Mutations
def resolve(project_path:, iid:, locked:)
issue = authorized_find!(project_path: project_path, iid: iid)
- ::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: { discussion_locked: locked })
+ ::Issues::UpdateService.new(container: issue.project, current_user: current_user, params: { discussion_locked: locked })
.execute(issue)
{
diff --git a/app/graphql/mutations/issues/set_severity.rb b/app/graphql/mutations/issues/set_severity.rb
index 4a24bfd18ef..68d7fb7d0c0 100644
--- a/app/graphql/mutations/issues/set_severity.rb
+++ b/app/graphql/mutations/issues/set_severity.rb
@@ -15,7 +15,7 @@ module Mutations
issue = authorized_find!(project_path: project_path, iid: iid)
project = issue.project
- ::Issues::UpdateService.new(project: project, current_user: current_user, params: { severity: severity })
+ ::Issues::UpdateService.new(container: project, current_user: current_user, params: { severity: severity })
.execute(issue)
{
diff --git a/app/graphql/mutations/issues/update.rb b/app/graphql/mutations/issues/update.rb
index 6cab1214d24..b5af048dc07 100644
--- a/app/graphql/mutations/issues/update.rb
+++ b/app/graphql/mutations/issues/update.rb
@@ -31,6 +31,10 @@ module Mutations
description: 'Close or reopen an issue.',
required: false
+ argument :time_estimate, GraphQL::Types::String,
+ required: false,
+ description: 'Estimated time to complete the issue, or `0` to remove the current estimate.'
+
def resolve(project_path:, iid:, **args)
issue = authorized_find!(project_path: project_path, iid: iid)
project = issue.project
@@ -38,7 +42,7 @@ module Mutations
args = parse_arguments(args)
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
- ::Issues::UpdateService.new(project: project, current_user: current_user, params: args, spam_params: spam_params).execute(issue)
+ ::Issues::UpdateService.new(container: project, current_user: current_user, params: args, spam_params: spam_params).execute(issue)
{
issue: issue,
@@ -46,11 +50,15 @@ module Mutations
}
end
- def ready?(label_ids: [], add_label_ids: [], remove_label_ids: [], **args)
+ def ready?(label_ids: [], add_label_ids: [], remove_label_ids: [], time_estimate: nil, **args)
if label_ids.any? && (add_label_ids.any? || remove_label_ids.any?)
raise Gitlab::Graphql::Errors::ArgumentError, 'labelIds is mutually exclusive with any of addLabelIds or removeLabelIds'
end
+ if !time_estimate.nil? && Gitlab::TimeTrackingFormatter.parse(time_estimate, keep_zero: true).nil?
+ raise Gitlab::Graphql::Errors::ArgumentError, 'timeEstimate must be formatted correctly, for example `1h 30m`'
+ end
+
super
end
@@ -61,6 +69,10 @@ module Mutations
args[:remove_label_ids] = parse_label_ids(args[:remove_label_ids])
args[:label_ids] = parse_label_ids(args[:label_ids])
+ unless args[:time_estimate].nil?
+ args[:time_estimate] = Gitlab::TimeTrackingFormatter.parse(args[:time_estimate], keep_zero: true)
+ end
+
args
end
diff --git a/app/graphql/mutations/merge_requests/set_milestone.rb b/app/graphql/mutations/merge_requests/set_milestone.rb
index bf40c12aec5..320aa423ce3 100644
--- a/app/graphql/mutations/merge_requests/set_milestone.rb
+++ b/app/graphql/mutations/merge_requests/set_milestone.rb
@@ -17,7 +17,7 @@ module Mutations
merge_request = authorized_find!(project_path: project_path, iid: iid)
project = merge_request.project
- ::MergeRequests::UpdateService.new(project: project, current_user: current_user, params: { milestone: milestone })
+ ::MergeRequests::UpdateService.new(project: project, current_user: current_user, params: { milestone_id: milestone&.id })
.execute(merge_request)
{
diff --git a/app/graphql/mutations/merge_requests/update.rb b/app/graphql/mutations/merge_requests/update.rb
index 0f4923e15a1..da4db7342a3 100644
--- a/app/graphql/mutations/merge_requests/update.rb
+++ b/app/graphql/mutations/merge_requests/update.rb
@@ -24,12 +24,16 @@ module Mutations
as: :state_event,
description: 'Action to perform to change the state.'
+ argument :time_estimate, GraphQL::Types::String,
+ required: false,
+ description: 'Estimated time to complete the merge request, or `0` to remove the current estimate.'
+
def resolve(project_path:, iid:, **args)
merge_request = authorized_find!(project_path: project_path, iid: iid)
- attributes = args.compact
+ args = parse_arguments(args)
::MergeRequests::UpdateService
- .new(project: merge_request.project, current_user: current_user, params: attributes)
+ .new(project: merge_request.project, current_user: current_user, params: args)
.execute(merge_request)
errors = errors_on_object(merge_request)
@@ -39,6 +43,25 @@ module Mutations
errors: errors
}
end
+
+ def ready?(time_estimate: nil, **args)
+ if !time_estimate.nil? && Gitlab::TimeTrackingFormatter.parse(time_estimate, keep_zero: true).nil?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'timeEstimate must be formatted correctly, for example `1h 30m`'
+ end
+
+ super
+ end
+
+ private
+
+ def parse_arguments(args)
+ unless args[:time_estimate].nil?
+ args[:time_estimate] = Gitlab::TimeTrackingFormatter.parse(args[:time_estimate], keep_zero: true)
+ end
+
+ args.compact
+ end
end
end
end
diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb
index a4efffb69c1..9f124de7ab2 100644
--- a/app/graphql/mutations/work_items/create.rb
+++ b/app/graphql/mutations/work_items/create.rb
@@ -48,7 +48,7 @@ module Mutations
widget_params = extract_widget_params!(type, params)
create_result = ::WorkItems::CreateService.new(
- project: project,
+ container: project,
current_user: current_user,
params: params,
spam_params: spam_params,
diff --git a/app/graphql/mutations/work_items/delete.rb b/app/graphql/mutations/work_items/delete.rb
index 4b0067d40d4..ec0244fa65e 100644
--- a/app/graphql/mutations/work_items/delete.rb
+++ b/app/graphql/mutations/work_items/delete.rb
@@ -20,7 +20,7 @@ module Mutations
work_item = authorized_find!(id: id)
result = ::WorkItems::DeleteService.new(
- project: work_item.project,
+ container: work_item.project,
current_user: current_user
).execute(work_item)
diff --git a/app/graphql/mutations/work_items/update.rb b/app/graphql/mutations/work_items/update.rb
index 04c63d8e876..db6af38d82e 100644
--- a/app/graphql/mutations/work_items/update.rb
+++ b/app/graphql/mutations/work_items/update.rb
@@ -22,8 +22,10 @@ module Mutations
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
widget_params = extract_widget_params!(work_item.work_item_type, attributes)
+ interpret_quick_actions!(work_item, current_user, widget_params, attributes)
+
update_result = ::WorkItems::UpdateService.new(
- project: work_item.project,
+ container: work_item.project,
current_user: current_user,
params: attributes,
widget_params: widget_params,
@@ -43,6 +45,37 @@ module Mutations
def find_object(id:)
GitlabSchema.find_by_gid(id)
end
+
+ def interpret_quick_actions!(work_item, current_user, widget_params, attributes = {})
+ return unless work_item.work_item_type.widgets.include?(::WorkItems::Widgets::Description)
+
+ description_param = widget_params[::WorkItems::Widgets::Description.api_symbol]
+ return unless description_param
+
+ original_description = description_param.fetch(:description, work_item.description)
+
+ description, command_params = QuickActions::InterpretService
+ .new(work_item.project, current_user, {})
+ .execute(original_description, work_item)
+
+ description_param[:description] = description if description && description != original_description
+
+ # Widgets have a set of quick action params that they must process.
+ # Map them to widget_params so they can be picked up by widget services.
+ work_item.work_item_type.widgets
+ .filter { |widget| widget.respond_to?(:quick_action_params) }
+ .each do |widget|
+ widget.quick_action_params
+ .filter { |param_name| command_params.key?(param_name) }
+ .each do |param_name|
+ widget_params[widget.api_symbol] ||= {}
+ widget_params[widget.api_symbol][param_name] = command_params.delete(param_name)
+ end
+ end
+
+ # The command_params not processed by widgets (e.g. title) should be placed in 'attributes'.
+ attributes.merge!(command_params || {})
+ end
end
end
end
diff --git a/app/graphql/mutations/work_items/update_task.rb b/app/graphql/mutations/work_items/update_task.rb
index aeb4f1d0f06..8dcc4c325ea 100644
--- a/app/graphql/mutations/work_items/update_task.rb
+++ b/app/graphql/mutations/work_items/update_task.rb
@@ -32,7 +32,7 @@ module Mutations
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
::WorkItems::UpdateService.new(
- project: task.project,
+ container: task.project,
current_user: current_user,
params: task_data_hash.except(:id),
spam_params: spam_params
diff --git a/app/graphql/queries/pipelines/get_pipeline_details.query.graphql b/app/graphql/queries/pipelines/get_pipeline_details.query.graphql
index c8353c738a5..01c312c567f 100644
--- a/app/graphql/queries/pipelines/get_pipeline_details.query.graphql
+++ b/app/graphql/queries/pipelines/get_pipeline_details.query.graphql
@@ -34,6 +34,7 @@ fragment LinkedPipelineData on Pipeline {
__typename
id
name
+ retried
}
project {
__typename
diff --git a/app/graphql/resolvers/board_list_issues_resolver.rb b/app/graphql/resolvers/board_list_issues_resolver.rb
index d70acdf7ca0..03b12dbb12e 100644
--- a/app/graphql/resolvers/board_list_issues_resolver.rb
+++ b/app/graphql/resolvers/board_list_issues_resolver.rb
@@ -13,7 +13,7 @@ module Resolvers
alias_method :list, :object
def resolve(**args)
- filters = item_filters(args[:filters])
+ filters = item_filters(args[:filters], list.board.resource_parent)
mutually_exclusive_milestone_args!(filters)
filter_params = filters.merge(board_id: list.board.id, id: list.id)
diff --git a/app/graphql/resolvers/board_list_resolver.rb b/app/graphql/resolvers/board_list_resolver.rb
index d853846b674..f9d3541cd5f 100644
--- a/app/graphql/resolvers/board_list_resolver.rb
+++ b/app/graphql/resolvers/board_list_resolver.rb
@@ -19,9 +19,8 @@ module Resolvers
description: 'Filters applied when getting issue metadata in the board list.'
def resolve(id: nil, issue_filters: {})
- context.scoped_set!(:issue_filters, item_filters(issue_filters))
-
Gitlab::Graphql::Lazy.with_value(find_list(id: id)) do |list|
+ context.scoped_set!(:issue_filters, item_filters(issue_filters, list&.board&.resource_parent))
list if authorized_resource?(list)
end
end
diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb
index 4dae3b4a9d1..5fc813f4acb 100644
--- a/app/graphql/resolvers/board_lists_resolver.rb
+++ b/app/graphql/resolvers/board_lists_resolver.rb
@@ -22,7 +22,7 @@ module Resolvers
def resolve_with_lookahead(id: nil, issue_filters: {})
lists = board_lists(id)
- context.scoped_set!(:issue_filters, item_filters(issue_filters))
+ context.scoped_set!(:issue_filters, item_filters(issue_filters, board.resource_parent))
List.preload_preferences_for_user(lists, current_user) if load_preferences?
diff --git a/app/graphql/resolvers/ci/runner_jobs_resolver.rb b/app/graphql/resolvers/ci/runner_jobs_resolver.rb
index b818be3f018..467a3525867 100644
--- a/app/graphql/resolvers/ci/runner_jobs_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_jobs_resolver.rb
@@ -30,6 +30,7 @@ module Resolvers
previous_stage_jobs_or_needs: [:needs, :pipeline],
artifacts: [:job_artifacts],
pipeline: [:user],
+ project: [{ project: [:route, { namespace: [:route] }] }],
detailed_status: [
:metadata,
{ pipeline: [:merge_request] },
diff --git a/app/graphql/resolvers/ci/variables_resolver.rb b/app/graphql/resolvers/ci/variables_resolver.rb
new file mode 100644
index 00000000000..71d420120b3
--- /dev/null
+++ b/app/graphql/resolvers/ci/variables_resolver.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class VariablesResolver < BaseResolver
+ type Types::Ci::InstanceVariableType.connection_type, null: true
+
+ argument :sort, ::Types::Ci::VariableSortEnum,
+ required: false,
+ description: 'Sort order of results.'
+
+ def resolve(**args)
+ if parent.is_a?(Group) || parent.is_a?(Project)
+ parent.variables.order_by(args[:sort])
+ elsif current_user&.can_admin_all_resources?
+ ::Ci::InstanceVariable.order_by(args[:sort])
+ end
+ end
+
+ private
+
+ def parent
+ object.respond_to?(:sync) ? object.sync : object
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/board_item_filterable.rb b/app/graphql/resolvers/concerns/board_item_filterable.rb
index 035cdbbd282..dcd2f265573 100644
--- a/app/graphql/resolvers/concerns/board_item_filterable.rb
+++ b/app/graphql/resolvers/concerns/board_item_filterable.rb
@@ -5,7 +5,7 @@ module BoardItemFilterable
private
- def item_filters(args)
+ def item_filters(args, resource_parent)
filters = args.to_h
set_filter_values(filters)
@@ -45,10 +45,6 @@ module BoardItemFilterable
def rewrite_param_name(filters, old_name, new_name)
filters[new_name] = filters.delete(old_name) if filters[old_name].present?
end
-
- def resource_parent
- respond_to?(:board) ? board.resource_parent : list.board.resource_parent
- end
end
::BoardItemFilterable.prepend_mod_with('Resolvers::BoardItemFilterable')
diff --git a/app/graphql/resolvers/concerns/resolves_groups.rb b/app/graphql/resolvers/concerns/resolves_groups.rb
index 1268e74fd58..86dda5cb1cb 100644
--- a/app/graphql/resolvers/concerns/resolves_groups.rb
+++ b/app/graphql/resolvers/concerns/resolves_groups.rb
@@ -5,8 +5,8 @@ module ResolvesGroups
extend ActiveSupport::Concern
include LooksAhead
- def resolve_with_lookahead(**args)
- apply_lookahead(resolve_groups(**args))
+ def resolve_with_lookahead(...)
+ apply_lookahead(resolve_groups(...))
end
private
diff --git a/app/graphql/resolvers/concerns/search_arguments.rb b/app/graphql/resolvers/concerns/search_arguments.rb
index cc1a13fdf29..ac5b7beb5ef 100644
--- a/app/graphql/resolvers/concerns/search_arguments.rb
+++ b/app/graphql/resolvers/concerns/search_arguments.rb
@@ -17,7 +17,6 @@ module SearchArguments
def ready?(**args)
validate_search_in_params!(args)
- validate_anonymous_search_access!(args)
validate_search_rate_limit!(args)
super
@@ -25,14 +24,6 @@ module SearchArguments
private
- def validate_anonymous_search_access!(args)
- return unless args[:search].present?
- return if current_user.present? || Feature.disabled?(:disable_anonymous_search, type: :ops)
-
- raise ::Gitlab::Graphql::Errors::ArgumentError,
- "User must be authenticated to include the `search` argument."
- end
-
def validate_search_in_params!(args)
return unless args[:in].present? && args[:search].blank?
@@ -41,7 +32,7 @@ module SearchArguments
end
def validate_search_rate_limit!(args)
- return if args[:search].blank? || context[:request].nil? || Feature.disabled?(:rate_limit_issuable_searches)
+ return if args[:search].blank? || context[:request].nil?
if current_user.present?
rate_limiter_key = :search_rate_limit
diff --git a/app/graphql/resolvers/data_transfer_resolver.rb b/app/graphql/resolvers/data_transfer_resolver.rb
new file mode 100644
index 00000000000..1a240d2811f
--- /dev/null
+++ b/app/graphql/resolvers/data_transfer_resolver.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class DataTransferResolver < BaseResolver
+ argument :from, Types::DateType,
+ description: 'Retain egress data for 1 year. Current month will increase dynamically as egress occurs.',
+ required: false
+ argument :to, Types::DateType,
+ description: 'End date for the data.',
+ required: false
+
+ type ::Types::DataTransfer::BaseType, null: false
+
+ def self.source
+ raise NotImplementedError
+ end
+
+ def self.project
+ Class.new(self) do
+ type Types::DataTransfer::ProjectDataTransferType, null: false
+
+ def self.source
+ "Project"
+ end
+ end
+ end
+
+ def self.group
+ Class.new(self) do
+ type Types::DataTransfer::GroupDataTransferType, null: false
+
+ def self.source
+ "Group"
+ end
+ end
+ end
+
+ def resolve(**_args)
+ return unless Feature.enabled?(:data_transfer_monitoring)
+
+ start_date = Date.new(2023, 0o1, 0o1)
+ date_for_index = ->(i) { (start_date + i.months).strftime('%Y-%m-%d') }
+
+ nodes = 0.upto(3).map do |i|
+ {
+ date: date_for_index.call(i),
+ repository_egress: 250_000,
+ artifacts_egress: 250_000,
+ packages_egress: 250_000,
+ registry_egress: 250_000
+ }
+ end
+
+ { egress_nodes: nodes }
+ end
+ end
+end
diff --git a/app/graphql/resolvers/group_releases_resolver.rb b/app/graphql/resolvers/group_releases_resolver.rb
new file mode 100644
index 00000000000..115289e1fca
--- /dev/null
+++ b/app/graphql/resolvers/group_releases_resolver.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class GroupReleasesResolver < BaseResolver
+ type Types::ReleaseType.connection_type, null: true
+
+ argument :sort, Types::GroupReleaseSortEnum,
+ required: false, default_value: :released_at_desc,
+ description: 'Sort group releases by given criteria.'
+
+ alias_method :group, :object
+
+ # GroupReleasesFinder only supports sorting by `released_at`
+ SORT_TO_PARAMS_MAP = {
+ released_at_desc: { sort: 'desc' },
+ released_at_asc: { sort: 'asc' }
+ }.freeze
+
+ def resolve(sort:)
+ releases = Releases::GroupReleasesFinder.new(
+ group,
+ current_user,
+ SORT_TO_PARAMS_MAP[sort]
+ ).execute
+ # fix ordering problem with GroupReleasesFinder and keyset pagination
+ # See more on https://gitlab.com/gitlab-org/gitlab/-/issues/378160
+ offset_pagination(releases)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/groups_resolver.rb b/app/graphql/resolvers/groups_resolver.rb
index 6cfdba240f0..902b5279364 100644
--- a/app/graphql/resolvers/groups_resolver.rb
+++ b/app/graphql/resolvers/groups_resolver.rb
@@ -4,31 +4,18 @@ module Resolvers
class GroupsResolver < BaseResolver
include ResolvesGroups
- type Types::GroupType, null: true
-
- argument :include_parent_descendants, GraphQL::Types::Boolean,
- required: false,
- description: 'List of descendant groups of the parent group.',
- default_value: true
-
- argument :owned, GraphQL::Types::Boolean,
- required: false,
- description: 'Limit result to groups owned by authenticated user.'
+ type "Types::GroupConnection", null: true
argument :search, GraphQL::Types::String,
required: false,
description: 'Search query for group name or group full path.'
- alias_method :parent, :object
-
private
# rubocop: disable CodeReuse/ActiveRecord
- def resolve_groups(args)
- return Group.none unless parent.present?
-
+ def resolve_groups(**args)
GroupsFinder
- .new(context[:current_user], args.merge(parent: parent))
+ .new(context[:current_user], args)
.execute
.reorder(name: :asc)
end
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index 24009bf7e18..bbf45efa33e 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -17,6 +17,7 @@ module Resolvers
before_connection_authorization do |nodes, current_user|
projects = nodes.map(&:project)
::Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
+ ::Preloaders::GroupPolicyPreloader.new(projects.filter_map(&:group), current_user).execute
end
def ready?(**args)
@@ -28,8 +29,6 @@ module Resolvers
end
def resolve_with_lookahead(**args)
- return unless Feature.enabled?(:root_level_issues_query)
-
issues = apply_lookahead(
IssuesFinder.new(current_user, prepare_finder_params(args)).execute
)
diff --git a/app/graphql/resolvers/namespace_projects_resolver.rb b/app/graphql/resolvers/namespace_projects_resolver.rb
index c3c61d31e8d..726e78f9971 100644
--- a/app/graphql/resolvers/namespace_projects_resolver.rb
+++ b/app/graphql/resolvers/namespace_projects_resolver.rb
@@ -8,9 +8,9 @@ module Resolvers
description: 'Include also subgroup projects.'
argument :search, GraphQL::Types::String,
- required: false,
- default_value: nil,
- description: 'Search project with most similar names or paths.'
+ required: false,
+ default_value: nil,
+ description: 'Search project with most similar names or paths.'
argument :sort, Types::Projects::NamespaceProjectSortEnum,
required: false,
@@ -22,6 +22,14 @@ module Resolvers
default_value: nil,
description: 'Filter projects by IDs.'
+ argument :with_issues_enabled, GraphQL::Types::Boolean,
+ required: false,
+ description: "Return only projects with issues enabled."
+
+ argument :with_merge_requests_enabled, GraphQL::Types::Boolean,
+ required: false,
+ description: "Return only projects with merge requests enabled."
+
type Types::ProjectType, null: true
def resolve(args)
@@ -54,7 +62,9 @@ module Resolvers
include_subgroups: args.dig(:include_subgroups),
sort: args.dig(:sort),
search: args.dig(:search),
- ids: parse_gids(args.dig(:ids))
+ ids: parse_gids(args.dig(:ids)),
+ with_issues_enabled: args[:with_issues_enabled],
+ with_merge_requests_enabled: args[:with_merge_requests_enabled]
}
end
diff --git a/app/graphql/resolvers/nested_groups_resolver.rb b/app/graphql/resolvers/nested_groups_resolver.rb
new file mode 100644
index 00000000000..a2869b50cbb
--- /dev/null
+++ b/app/graphql/resolvers/nested_groups_resolver.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class NestedGroupsResolver < BaseResolver
+ include ResolvesGroups
+
+ type Types::GroupType, null: true
+
+ argument :include_parent_descendants, GraphQL::Types::Boolean,
+ required: false,
+ description: 'List of descendant groups of the parent group.',
+ default_value: true
+
+ argument :owned, GraphQL::Types::Boolean,
+ required: false,
+ description: 'Limit result to groups owned by authenticated user.'
+
+ argument :search, GraphQL::Types::String,
+ required: false,
+ description: 'Search query for group name or group full path.'
+
+ alias_method :parent, :object
+
+ private
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def resolve_groups(args)
+ return Group.none unless parent.present?
+
+ GroupsFinder
+ .new(context[:current_user], args.merge(parent: parent))
+ .execute
+ .reorder(name: :asc)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/graphql/resolvers/notes/synthetic_note_resolver.rb b/app/graphql/resolvers/notes/synthetic_note_resolver.rb
new file mode 100644
index 00000000000..d4eafcd2c49
--- /dev/null
+++ b/app/graphql/resolvers/notes/synthetic_note_resolver.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Notes
+ class SyntheticNoteResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ authorize :read_note
+
+ type Types::Notes::NoteType, null: true
+
+ argument :sha, GraphQL::Types::String,
+ required: true,
+ description: 'Global ID of the note.'
+
+ argument :noteable_id, ::Types::GlobalIDType[::Noteable],
+ required: true,
+ description: 'Global ID of the resource to search synthetic note on.'
+
+ def resolve(noteable_id:, sha:)
+ noteable = authorized_find!(id: noteable_id)
+
+ synthetic_notes = ResourceEvents::MergeIntoNotesService.new(
+ noteable, current_user, paginated_notes: nil
+ ).execute
+
+ synthetic_notes.find { |note| note.discussion_id == sha }
+ end
+
+ def find_object(id:)
+ GitlabSchema.find_by_gid(id)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/project_pipeline_schedules_resolver.rb b/app/graphql/resolvers/project_pipeline_schedules_resolver.rb
index eb980f72717..32887385d26 100644
--- a/app/graphql/resolvers/project_pipeline_schedules_resolver.rb
+++ b/app/graphql/resolvers/project_pipeline_schedules_resolver.rb
@@ -10,8 +10,13 @@ module Resolvers
required: false,
description: 'Filter pipeline schedules by active status.'
- def resolve(status: nil)
- ::Ci::PipelineSchedulesFinder.new(project).execute(scope: status)
+ argument :ids, [GraphQL::Types::ID],
+ required: false,
+ default_value: nil,
+ description: 'Filter pipeline schedules by IDs.'
+
+ def resolve(status: nil, ids: nil)
+ ::Ci::PipelineSchedulesFinder.new(project).execute(scope: status, ids: ids)
end
end
end
diff --git a/app/graphql/resolvers/projects/services_resolver.rb b/app/graphql/resolvers/projects/services_resolver.rb
index 99de4df945c..3eca0dfd83f 100644
--- a/app/graphql/resolvers/projects/services_resolver.rb
+++ b/app/graphql/resolvers/projects/services_resolver.rb
@@ -2,6 +2,7 @@
module Resolvers
module Projects
+ # TODO: Remove in 17.0, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108418
class ServicesResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource
diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb
index 0bdba53c7af..08981f2c441 100644
--- a/app/graphql/resolvers/projects_resolver.rb
+++ b/app/graphql/resolvers/projects_resolver.rb
@@ -15,14 +15,30 @@ module Resolvers
description: "Sort order of results. Format: `<field_name>_<sort_direction>`, " \
"for example: `id_desc` or `name_asc`"
+ argument :with_issues_enabled, GraphQL::Types::Boolean,
+ required: false,
+ description: "Return only projects with issues enabled."
+
+ argument :with_merge_requests_enabled, GraphQL::Types::Boolean,
+ required: false,
+ description: "Return only projects with merge requests enabled."
+
def resolve(**args)
ProjectsFinder
- .new(current_user: current_user, params: project_finder_params(args), project_ids_relation: parse_gids(args[:ids]))
+ .new(current_user: current_user, params: finder_params(args), project_ids_relation: parse_gids(args[:ids]))
.execute
end
private
+ def finder_params(args)
+ {
+ **project_finder_params(args),
+ with_issues_enabled: args[:with_issues_enabled],
+ with_merge_requests_enabled: args[:with_merge_requests_enabled]
+ }
+ end
+
def parse_gids(gids)
gids&.map { |gid| GitlabSchema.parse_gid(gid, expected_type: ::Project).model_id }
end
diff --git a/app/graphql/resolvers/releases_resolver.rb b/app/graphql/resolvers/releases_resolver.rb
index 358f3c33836..06f4ca2065c 100644
--- a/app/graphql/resolvers/releases_resolver.rb
+++ b/app/graphql/resolvers/releases_resolver.rb
@@ -6,7 +6,7 @@ module Resolvers
argument :sort, Types::ReleaseSortEnum,
required: false, default_value: :released_at_desc,
- description: 'Sort releases by this criteria.'
+ description: 'Sort releases by given criteria.'
alias_method :project, :object
diff --git a/app/graphql/resolvers/saved_reply_resolver.rb b/app/graphql/resolvers/saved_reply_resolver.rb
new file mode 100644
index 00000000000..96bbc139c96
--- /dev/null
+++ b/app/graphql/resolvers/saved_reply_resolver.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class SavedReplyResolver < BaseResolver
+ type Types::SavedReplyType, null: true
+
+ alias_method :target, :object
+
+ argument :id, Types::GlobalIDType[::Users::SavedReply],
+ required: true,
+ description: 'ID of a saved reply.'
+
+ def resolve(id:)
+ return unless Feature.enabled?(:saved_replies, current_user)
+
+ saved_reply = ::Users::SavedReply.find_saved_reply(user_id: current_user.id, id: id.model_id)
+
+ return unless saved_reply
+
+ saved_reply
+ end
+ end
+end
diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb
index 83ed8c37250..0c9aac80274 100644
--- a/app/graphql/resolvers/work_items_resolver.rb
+++ b/app/graphql/resolvers/work_items_resolver.rb
@@ -7,6 +7,10 @@ module Resolvers
type Types::WorkItemType.connection_type, null: true
+ argument :author_username, GraphQL::Types::String,
+ required: false,
+ description: 'Filter work items by author username.',
+ alpha: { milestone: '15.9' }
argument :iid, GraphQL::Types::String,
required: false,
description: 'IID of the issue. For example, "1".'
@@ -39,14 +43,19 @@ module Resolvers
{
work_item_type: :work_item_type,
web_url: { project: { namespace: :route } },
- widgets: :work_item_type
+ widgets: { work_item_type: :enabled_widget_definitions }
}
end
def nested_preloads
{
widgets: widget_preloads,
- user_permissions: { update_work_item: :assignees }
+ user_permissions: { update_work_item: :assignees },
+ project: { jira_import_status: { project: :jira_imports } },
+ author: {
+ location: { author: :user_detail },
+ gitpod_enabled: { author: :user_preference }
+ }
}
end
@@ -55,9 +64,9 @@ module Resolvers
last_edited_by: :last_edited_by,
assignees: :assignees,
parent: :work_item_parent,
- children: { work_item_children_by_created_at: [:author, { project: :project_feature }] },
+ children: { work_item_children_by_relative_position: [:author, { project: :project_feature }] },
labels: :labels,
- milestone: :milestone
+ milestone: { milestone: [:project, :group] }
}
end
diff --git a/app/graphql/subscriptions/notes/base.rb b/app/graphql/subscriptions/notes/base.rb
new file mode 100644
index 00000000000..3653c01e0e2
--- /dev/null
+++ b/app/graphql/subscriptions/notes/base.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Subscriptions
+ module Notes
+ class Base < ::Subscriptions::BaseSubscription
+ include Gitlab::Graphql::Laziness
+
+ argument :noteable_id, ::Types::GlobalIDType[::Noteable],
+ required: false,
+ description: 'ID of the noteable.'
+
+ def subscribe(*args)
+ nil
+ end
+
+ def authorized?(noteable_id:)
+ noteable = force(GitlabSchema.find_by_gid(noteable_id))
+
+ # unsubscribe if user cannot read the noteable anymore for any reason, e.g. issue was set confidential,
+ # in the meantime the read note permissions is checked within its corresponding returned type, i.e. NoteType
+ unauthorized! unless noteable && Ability.allowed?(current_user, :"read_#{noteable.to_ability_name}", noteable)
+
+ true
+ end
+ end
+ end
+end
diff --git a/app/graphql/subscriptions/notes/created.rb b/app/graphql/subscriptions/notes/created.rb
new file mode 100644
index 00000000000..07b7b308163
--- /dev/null
+++ b/app/graphql/subscriptions/notes/created.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Subscriptions
+ module Notes
+ class Created < Base
+ payload_type ::Types::Notes::NoteType
+
+ def update(*args)
+ case object
+ when ResourceEvent
+ object.work_item_synthetic_system_note
+ when Array
+ object.first.work_item_synthetic_system_note(events: object)
+ else
+ object
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/subscriptions/notes/deleted.rb b/app/graphql/subscriptions/notes/deleted.rb
new file mode 100644
index 00000000000..d931ef00d0d
--- /dev/null
+++ b/app/graphql/subscriptions/notes/deleted.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Subscriptions
+ module Notes
+ class Deleted < Base
+ payload_type ::Types::Notes::DeletedNoteType
+
+ DeletedNote = Struct.new(:model_id, :model_name, :discussion_model_id, :last_discussion_note) do
+ def to_global_id
+ ::Gitlab::GlobalId.as_global_id(model_id, model_name: model_name)
+ end
+
+ def discussion_id
+ ::Gitlab::GlobalId.as_global_id(discussion_model_id, model_name: Discussion.name)
+ end
+ end
+
+ def update(*args)
+ DeletedNote.new(object[:id], object[:model_name], object[:discussion_id], object[:last_discussion_note])
+ end
+ end
+ end
+end
diff --git a/app/graphql/subscriptions/notes/updated.rb b/app/graphql/subscriptions/notes/updated.rb
new file mode 100644
index 00000000000..a4748a3361e
--- /dev/null
+++ b/app/graphql/subscriptions/notes/updated.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Subscriptions
+ module Notes
+ class Updated < Base
+ payload_type Types::Notes::NoteType
+ end
+ end
+end
diff --git a/app/graphql/types/achievements/achievement_type.rb b/app/graphql/types/achievements/achievement_type.rb
index e2b9495c83d..67cc9778797 100644
--- a/app/graphql/types/achievements/achievement_type.rb
+++ b/app/graphql/types/achievements/achievement_type.rb
@@ -32,11 +32,6 @@ module Types
null: true,
description: 'Description or notes for the achievement.'
- field :revokeable,
- GraphQL::Types::Boolean,
- null: false,
- description: 'Revokeability of the achievement.'
-
field :created_at,
Types::TimeType,
null: false,
diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb
index 4086015dad6..d2bc1d55408 100644
--- a/app/graphql/types/base_argument.rb
+++ b/app/graphql/types/base_argument.rb
@@ -2,7 +2,7 @@
module Types
class BaseArgument < GraphQL::Schema::Argument
- include GitlabStyleDeprecations
+ include Gitlab::Graphql::Deprecations
attr_reader :doc_reference
diff --git a/app/graphql/types/base_enum.rb b/app/graphql/types/base_enum.rb
index 11877b79e59..45e78b330fb 100644
--- a/app/graphql/types/base_enum.rb
+++ b/app/graphql/types/base_enum.rb
@@ -4,7 +4,7 @@
module Types
class BaseEnum < GraphQL::Schema::Enum
class CustomValue < GraphQL::Schema::EnumValue
- include ::GitlabStyleDeprecations
+ include Gitlab::Graphql::Deprecations
def initialize(name, desc = nil, **kwargs)
init_gitlab_deprecation(kwargs)
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 615c143a0b9..caeb81c95cb 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -2,7 +2,7 @@
module Types
class BaseField < GraphQL::Schema::Field
- include GitlabStyleDeprecations
+ include Gitlab::Graphql::Deprecations
argument_class ::Types::BaseArgument
diff --git a/app/graphql/types/ci/ci_cd_setting_type.rb b/app/graphql/types/ci/ci_cd_setting_type.rb
index 574791b79e6..dd6647b749d 100644
--- a/app/graphql/types/ci/ci_cd_setting_type.rb
+++ b/app/graphql/types/ci/ci_cd_setting_type.rb
@@ -30,6 +30,11 @@ module Types
field :merge_trains_enabled, GraphQL::Types::Boolean, null: true,
description: 'Whether merge trains are enabled.',
method: :merge_trains_enabled?
+ field :opt_in_jwt,
+ GraphQL::Types::Boolean,
+ null: true,
+ description: 'When disabled, the JSON Web Token is always available in all jobs in the pipeline.',
+ method: :opt_in_jwt?
field :project, Types::ProjectType, null: true,
description: 'Project the CI/CD settings belong to.'
end
diff --git a/app/graphql/types/ci/code_quality_report_summary_type.rb b/app/graphql/types/ci/code_quality_report_summary_type.rb
new file mode 100644
index 00000000000..0d560d9e9e8
--- /dev/null
+++ b/app/graphql/types/ci/code_quality_report_summary_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ # This is presented through `PipelineType` that has its own authorization
+ class CodeQualityReportSummaryType < BaseObject
+ graphql_name 'CodeQualityReportSummary'
+ description 'Code Quality report for a pipeline'
+
+ field :count, GraphQL::Types::Int, null: true,
+ description: 'Total number of Code Quality reports.'
+ ::Gitlab::Ci::Reports::CodequalityReports::SEVERITY_PRIORITIES.each_key do |status|
+ field status, GraphQL::Types::Int, null: true,
+ description: "Total number of #{status} status."
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ci/job_token_scope/direction_enum.rb b/app/graphql/types/ci/job_token_scope/direction_enum.rb
new file mode 100644
index 00000000000..f52cf891af8
--- /dev/null
+++ b/app/graphql/types/ci/job_token_scope/direction_enum.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ module JobTokenScope
+ class DirectionEnum < BaseEnum
+ graphql_name 'CiJobTokenScopeDirection'
+ description 'Direction of access.'
+
+ value 'OUTBOUND',
+ value: :outbound,
+ description: 'Job token scope project can access target project in the outbound allowlist.'
+
+ value 'INBOUND',
+ value: :inbound,
+ description: 'Target projects in the inbound allowlist can access the scope project ' \
+ 'through their job tokens.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/ci/job_token_scope_type.rb b/app/graphql/types/ci/job_token_scope_type.rb
index 37c0af944a7..639bbaa22af 100644
--- a/app/graphql/types/ci/job_token_scope_type.rb
+++ b/app/graphql/types/ci/job_token_scope_type.rb
@@ -11,7 +11,23 @@ module Types
Types::ProjectType.connection_type,
null: false,
description: 'Allow list of projects that can be accessed by CI Job tokens created by this project.',
- method: :all_projects
+ method: :outbound_projects,
+ deprecated: {
+ reason: 'The `projects` attribute is being deprecated. Use `outbound_allowlist`',
+ milestone: '15.9'
+ }
+
+ field :outbound_allowlist,
+ Types::ProjectType.connection_type,
+ null: false,
+ description: "Allow list of projects that are accessible using the current project's CI Job tokens.",
+ method: :outbound_projects
+
+ field :inbound_allowlist,
+ Types::ProjectType.connection_type,
+ null: false,
+ description: "Allow list of projects that can access the current project through its CI Job tokens.",
+ method: :inbound_projects
end
end
# rubocop: enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index 4447a10a74e..a97e9cee4b1 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -37,6 +37,8 @@ module Types
# Life-cycle timestamps:
field :created_at, Types::TimeType, null: false,
description: "When the job was created."
+ field :erased_at, Types::TimeType, null: true,
+ description: "When the job was erased."
field :finished_at, Types::TimeType, null: true,
description: 'When a job has finished running.'
field :queued_at, Types::TimeType, null: true,
@@ -97,6 +99,8 @@ module Types
field :web_path, GraphQL::Types::String, null: true,
description: 'Web path of the job.'
+ field :project, Types::ProjectType, null: true, description: 'Project that the job belongs to.'
+
def kind
return ::Ci::Build unless [::Ci::Build, ::Ci::Bridge].include?(object.class)
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index cb561f48b3b..19d261853a7 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -178,6 +178,10 @@ module Types
field :merge_request_event_type, Types::Ci::PipelineMergeRequestEventTypeEnum, null: true,
description: "Event type of the pipeline associated with a merge request."
+ def commit
+ BatchLoader::GraphQL.wrap(object.commit)
+ end
+
def detailed_status
object.detailed_status(current_user)
end
diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb
index 35339624e37..10d18f9ad2a 100644
--- a/app/graphql/types/ci/runner_type.rb
+++ b/app/graphql/types/ci/runner_type.rb
@@ -14,6 +14,9 @@ module Types
JOB_COUNT_LIMIT = 1000
+ # Only allow ephemeral_authentication_token to be visible for a short while
+ RUNNER_EPHEMERAL_TOKEN_AVAILABILITY_TIME = 3.hours
+
alias_method :runner, :object
field :access_level, ::Types::Ci::RunnerAccessLevelEnum, null: false,
@@ -35,6 +38,10 @@ module Types
description: 'Description of the runner.'
field :edit_admin_url, GraphQL::Types::String, null: true,
description: 'Admin form URL of the runner. Only available for administrators.'
+ field :ephemeral_authentication_token, GraphQL::Types::String, null: true,
+ description: 'Ephemeral authentication token used for runner machine registration.',
+ authorize: :read_ephemeral_token,
+ alpha: { milestone: '15.9' }
field :executor_name, GraphQL::Types::String, null: true,
description: 'Executor last advertised by the runner.',
method: :executor_name
@@ -134,6 +141,14 @@ module Types
Gitlab::Routing.url_helpers.edit_admin_runner_url(runner) if can_admin_runners?
end
+ def ephemeral_authentication_token
+ return unless runner.authenticated_user_registration_type?
+ return unless runner.created_at > RUNNER_EPHEMERAL_TOKEN_AVAILABILITY_TIME.ago
+ return if runner.runner_machines.any?
+
+ runner.token
+ end
+
def project_count
BatchLoader::GraphQL.for(runner.id).batch(key: :runner_project_count) do |ids, loader, args|
counts = ::Ci::Runner.project_type
diff --git a/app/graphql/types/ci/runner_upgrade_status_enum.rb b/app/graphql/types/ci/runner_upgrade_status_enum.rb
index 34a931c8f79..668970aaff2 100644
--- a/app/graphql/types/ci/runner_upgrade_status_enum.rb
+++ b/app/graphql/types/ci/runner_upgrade_status_enum.rb
@@ -5,13 +5,13 @@ module Types
class RunnerUpgradeStatusEnum < BaseEnum
graphql_name 'CiRunnerUpgradeStatus'
+ MODEL_STATUS_TO_GRAPHQL_TRANSLATIONS = {
+ invalid_version: :invalid,
+ unavailable: :not_available
+ }.freeze
+
::Ci::RunnerVersion::STATUS_DESCRIPTIONS.each do |status, description|
- status_name_src =
- if status == :invalid_version
- :invalid
- else
- status
- end
+ status_name_src = MODEL_STATUS_TO_GRAPHQL_TRANSLATIONS.fetch(status, status)
value status_name_src.to_s.upcase, description: description, value: status
end
diff --git a/app/graphql/types/ci/variable_sort_enum.rb b/app/graphql/types/ci/variable_sort_enum.rb
new file mode 100644
index 00000000000..3a60899ab5d
--- /dev/null
+++ b/app/graphql/types/ci/variable_sort_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class VariableSortEnum < BaseEnum
+ graphql_name 'CiVariableSort'
+ description 'Values for sorting variables'
+
+ value 'KEY_ASC', 'Sorted by key in ascending order.', value: :key_asc
+ value 'KEY_DESC', 'Sorted by key in descending order.', value: :key_desc
+ end
+ end
+end
diff --git a/app/graphql/types/concerns/gitlab_style_deprecations.rb b/app/graphql/types/concerns/gitlab_style_deprecations.rb
deleted file mode 100644
index 859a27cac4c..00000000000
--- a/app/graphql/types/concerns/gitlab_style_deprecations.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-# Concern for handling GraphQL deprecations.
-# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-schema-items
-module GitlabStyleDeprecations
- extend ActiveSupport::Concern
-
- included do
- attr_accessor :deprecation
- end
-
- def visible?(ctx)
- super && ctx[:remove_deprecated] == true ? deprecation.nil? : true
- end
-
- private
-
- # Set deprecation, mutate the arguments
- def init_gitlab_deprecation(kwargs)
- if kwargs[:deprecation_reason].present?
- raise ArgumentError, 'Use `deprecated` property instead of `deprecation_reason`. ' \
- 'See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-schema-items'
- end
-
- # GitLab allows items to be marked as "alpha", which leverages GraphQL deprecations.
- deprecation_args = kwargs.extract!(:alpha, :deprecated)
-
- self.deprecation = ::Gitlab::Graphql::Deprecation.parse(**deprecation_args)
- return unless deprecation
-
- raise ArgumentError, "Bad deprecation. #{deprecation.errors.full_messages.to_sentence}" unless deprecation.valid?
-
- kwargs[:deprecation_reason] = deprecation.deprecation_reason
- kwargs[:description] = deprecation.edit_description(kwargs[:description])
- end
-end
diff --git a/app/graphql/types/data_transfer/base_type.rb b/app/graphql/types/data_transfer/base_type.rb
new file mode 100644
index 00000000000..e077612bfd5
--- /dev/null
+++ b/app/graphql/types/data_transfer/base_type.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ module DataTransfer
+ class BaseType < BaseObject
+ authorize
+
+ field :egress_nodes, type: Types::DataTransfer::EgressNodeType.connection_type,
+ description: 'Data nodes.',
+ null: true # disallow null once data_transfer_monitoring feature flag is rolled-out!
+ end
+ end
+end
diff --git a/app/graphql/types/data_transfer/egress_node_type.rb b/app/graphql/types/data_transfer/egress_node_type.rb
new file mode 100644
index 00000000000..a050540999f
--- /dev/null
+++ b/app/graphql/types/data_transfer/egress_node_type.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Types
+ module DataTransfer
+ class EgressNodeType < BaseObject
+ authorize
+
+ field :date, GraphQL::Types::String,
+ description: 'First day of the node range. There is one node per month.',
+ null: false
+
+ field :total_egress, GraphQL::Types::BigInt,
+ description: 'Total egress for that project in that period of time.',
+ null: false
+
+ field :repository_egress, GraphQL::Types::BigInt,
+ description: 'Repository egress for that project in that period of time.',
+ null: false
+
+ field :artifacts_egress, GraphQL::Types::BigInt,
+ description: 'Artifacts egress for that project in that period of time.',
+ null: false
+
+ field :packages_egress, GraphQL::Types::BigInt,
+ description: 'Packages egress for that project in that period of time.',
+ null: false
+
+ field :registry_egress, GraphQL::Types::BigInt,
+ description: 'Registery egress for that project in that period of time.',
+ null: false
+
+ def total_egress
+ object.values.select { |x| x.is_a?(Integer) }.sum
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/data_transfer/group_data_transfer_type.rb b/app/graphql/types/data_transfer/group_data_transfer_type.rb
new file mode 100644
index 00000000000..a9a353e10e8
--- /dev/null
+++ b/app/graphql/types/data_transfer/group_data_transfer_type.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Types
+ module DataTransfer
+ class GroupDataTransferType < BaseType
+ graphql_name 'GroupDataTransfer'
+ authorize
+ end
+ end
+end
diff --git a/app/graphql/types/data_transfer/project_data_transfer_type.rb b/app/graphql/types/data_transfer/project_data_transfer_type.rb
new file mode 100644
index 00000000000..f385aa20a7e
--- /dev/null
+++ b/app/graphql/types/data_transfer/project_data_transfer_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module DataTransfer
+ class ProjectDataTransferType < BaseType
+ graphql_name 'ProjectDataTransfer'
+ authorize
+
+ field :total_egress, GraphQL::Types::BigInt,
+ description: 'Total egress for that project in that period of time.',
+ null: true # disallow null once data_transfer_monitoring feature flag is rolled-out!
+
+ def total_egress(**_)
+ return unless Feature.enabled?(:data_transfer_monitoring)
+
+ 40_000_000
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/deployment_type.rb b/app/graphql/types/deployment_type.rb
index 1c23fd44ea1..6d895cc81cf 100644
--- a/app/graphql/types/deployment_type.rb
+++ b/app/graphql/types/deployment_type.rb
@@ -65,9 +65,9 @@ module Types
field :tags,
[Types::DeploymentTagType],
description: 'Git tags that contain this deployment. ' \
- 'This field can only be resolved for one deployment in any single request.',
+ 'This field can only be resolved for two deployments in any single request.',
calls_gitaly: true do
- extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 2
end
end
end
diff --git a/app/graphql/types/group_release_sort_enum.rb b/app/graphql/types/group_release_sort_enum.rb
new file mode 100644
index 00000000000..7420e7a31ad
--- /dev/null
+++ b/app/graphql/types/group_release_sort_enum.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ # Not inheriting from Types::SortEnum since we only want
+ # to implement a subset of the sort values it defines.
+ class GroupReleaseSortEnum < BaseEnum
+ graphql_name 'GroupReleaseSort'
+ description 'Values for sorting releases belonging to a group'
+
+ # Borrowed from Types::ReleaseSortEnum and Types::SortEnum
+ # These values/descriptions should stay in-sync as much as possible.
+ value 'RELEASED_AT_DESC', 'Released at by descending order.', value: :released_at_desc
+ value 'RELEASED_AT_ASC', 'Released at by ascending order.', value: :released_at_asc
+ end
+end
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index 4e5ddbac8a2..3543ac29c17 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -193,14 +193,14 @@ module Types
null: true,
description: 'List of descendant groups of this group.',
complexity: 5,
- resolver: Resolvers::GroupsResolver
+ resolver: Resolvers::NestedGroupsResolver
field :ci_variables,
Types::Ci::GroupVariableType.connection_type,
null: true,
description: "List of the group's CI/CD variables.",
authorize: :admin_group,
- method: :variables
+ resolver: Resolvers::Ci::VariablesResolver
field :runners, Types::Ci::RunnerType.connection_type,
null: true,
@@ -233,6 +233,17 @@ module Types
resolver: Resolvers::WorkItems::TypesResolver,
description: 'Work item types available to the group.'
+ field :releases,
+ Types::ReleaseType.connection_type,
+ null: true,
+ description: 'Releases belonging to projects in the group.',
+ resolver: Resolvers::GroupReleasesResolver
+
+ field :data_transfer, Types::DataTransfer::GroupDataTransferType,
+ null: true,
+ resolver: Resolvers::DataTransferResolver.group,
+ description: 'Data transfer data point for a specific period. This is mocked data under a development feature flag.'
+
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
LabelsFinder
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index 4948063610a..1e5833a5cf0 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -54,19 +54,24 @@ module Types
description: 'Indicates the issue is confidential.'
field :discussion_locked, GraphQL::Types::Boolean, null: false,
description: 'Indicates discussion is locked on the issue.'
- field :due_date, Types::TimeType, null: true,
- description: 'Due date of the issue.'
- field :hidden, GraphQL::Types::Boolean, null: true, resolver_method: :hidden?,
- description: 'Indicates the issue is hidden because the author has been banned. ' \
- 'Will always return `null` if `ban_user_feature_flag` feature flag is disabled.'
-
field :downvotes, GraphQL::Types::Int,
null: false,
description: 'Number of downvotes the issue has received.',
resolver: Resolvers::DownVotesCountResolver
+ field :due_date, Types::TimeType, null: true,
+ description: 'Due date of the issue.'
+ field :hidden, GraphQL::Types::Boolean, null: true,
+ description: 'Indicates the issue is hidden because the author has been banned.', method: :hidden?
field :merge_requests_count, GraphQL::Types::Int, null: false,
description: 'Number of merge requests that close the issue on merge.',
resolver: Resolvers::MergeRequestsCountResolver
+
+ field :related_merge_requests, Types::MergeRequestType.connection_type,
+ null: true,
+ description: 'Merge requests related to the issue. This field can only be resolved for one issue in any single request.' do
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ end
+
field :relative_position, GraphQL::Types::Int, null: true,
description: 'Relative position of the issue (used for positioning in epic tree and issue boards).'
field :upvotes, GraphQL::Types::Int,
@@ -182,6 +187,17 @@ module Types
Gitlab::Graphql::Loaders::BatchModelLoader.new(Issue, object.duplicated_to_id).find
end
+ def related_merge_requests
+ # rubocop: disable CodeReuse/ActiveRecord
+ MergeRequest.where(
+ id: ::Issues::ReferencedMergeRequestsService.new(container: object.project, current_user: current_user)
+ .execute(object)
+ .first
+ .map(&:id)
+ )
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+
def discussion_locked
!!object.discussion_locked
end
@@ -190,10 +206,6 @@ module Types
object.creatable_note_email_address(context[:current_user])
end
- def hidden?
- object.hidden? if Feature.enabled?(:ban_user_feature_flag)
- end
-
def escalation_status
object.supports_escalation? ? object.escalation_status&.status_name : nil
end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index abf7b3ad530..3c288c1d496 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -213,9 +213,9 @@ module Types
field :security_auto_fix, GraphQL::Types::Boolean, null: true,
description: 'Indicates if the merge request is created by @GitLab-Security-Bot.'
field :squash, GraphQL::Types::Boolean, null: false,
- description: 'Indicates if squash on merge is enabled.'
+ description: 'Indicates if the merge request is set to be squashed when merged. [Project settings](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html#configure-squash-options-for-a-project) may override this value. Use `squash_on_merge` instead to take project squash options into account.'
field :squash_on_merge, GraphQL::Types::Boolean, null: false, method: :squash_on_merge?,
- description: 'Indicates if squash on merge is enabled.'
+ description: 'Indicates if the merge request will be squashed when merged.'
field :timelogs, Types::TimelogType.connection_type, null: false,
description: 'Timelogs on the merge request.'
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 5a92ba754aa..e48e9deae96 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -66,6 +66,7 @@ module Types
mount_mutation Mutations::Issues::Move
mount_mutation Mutations::Issues::LinkAlerts
mount_mutation Mutations::Issues::UnlinkAlert
+ mount_mutation Mutations::Issues::BulkUpdate, alpha: { milestone: '15.9' }
mount_mutation Mutations::Labels::Create
mount_mutation Mutations::Members::Groups::BulkUpdate
mount_mutation Mutations::MergeRequests::Accept
@@ -123,6 +124,7 @@ module Types
mount_mutation Mutations::Ci::PipelineSchedule::TakeOwnership
mount_mutation Mutations::Ci::PipelineSchedule::Play
mount_mutation Mutations::Ci::PipelineSchedule::Create
+ mount_mutation Mutations::Ci::PipelineSchedule::Update
mount_mutation Mutations::Ci::CiCdSettingsUpdate, deprecated: {
reason: :renamed,
replacement: 'ProjectCiCdSettingsUpdate',
diff --git a/app/graphql/types/notes/deleted_note_type.rb b/app/graphql/types/notes/deleted_note_type.rb
new file mode 100644
index 00000000000..f799fc01f6e
--- /dev/null
+++ b/app/graphql/types/notes/deleted_note_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ module Notes
+ # rubocop: disable Graphql/AuthorizeTypes
+ class DeletedNoteType < BaseObject
+ graphql_name 'DeletedNote'
+
+ field :id, ::Types::GlobalIDType[::Note],
+ null: false,
+ description: 'ID of the deleted note.'
+
+ field :discussion_id, ::Types::GlobalIDType[::Discussion],
+ null: true,
+ description: 'ID of the discussion for the deleted note.'
+
+ field :last_discussion_note, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Whether deleted note is the last note in the discussion.'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/packages/package_details_type.rb b/app/graphql/types/packages/package_details_type.rb
index 6c0d955ed77..f63b41b3c92 100644
--- a/app/graphql/types/packages/package_details_type.rb
+++ b/app/graphql/types/packages/package_details_type.rb
@@ -28,6 +28,8 @@ module Types
field :last_downloaded_at, Types::TimeType, null: true, description: 'Last time that a file of this package was downloaded.'
+ field :public_package, GraphQL::Types::Boolean, null: true, description: 'Indicates if there is public access to the package.'
+
def versions
object.versions
end
@@ -63,6 +65,10 @@ module Types
def pypi_url
pypi_registry_url(object.project.id)
end
+
+ def public_package
+ object.project.public? || object.project.project_feature.package_registry_access_level == ProjectFeature::PUBLIC
+ end
end
end
end
diff --git a/app/graphql/types/permission_types/base_permission_type.rb b/app/graphql/types/permission_types/base_permission_type.rb
index 0192af25d0f..d45c61f489b 100644
--- a/app/graphql/types/permission_types/base_permission_type.rb
+++ b/app/graphql/types/permission_types/base_permission_type.rb
@@ -28,7 +28,7 @@ module Types
end
def self.define_field_resolver_method(ability)
- unless self.respond_to?(ability)
+ unless respond_to?(ability)
define_method ability.to_sym do |*args|
Ability.allowed?(context[:current_user], ability, object, args.to_h)
end
diff --git a/app/graphql/types/permission_types/merge_request.rb b/app/graphql/types/permission_types/merge_request.rb
index 73a2f820f79..88d8c38361a 100644
--- a/app/graphql/types/permission_types/merge_request.rb
+++ b/app/graphql/types/permission_types/merge_request.rb
@@ -21,10 +21,15 @@ module Types
end
permission_field :can_merge, calls_gitaly: true
+ permission_field :can_approve
def can_merge
object.can_be_merged_by?(context[:current_user])
end
+
+ def can_approve
+ object.eligible_for_approval_by?(context[:current_user])
+ end
end
end
end
diff --git a/app/graphql/types/permission_types/work_item.rb b/app/graphql/types/permission_types/work_item.rb
index bae1dae4834..f35f42001e0 100644
--- a/app/graphql/types/permission_types/work_item.rb
+++ b/app/graphql/types/permission_types/work_item.rb
@@ -6,7 +6,7 @@ module Types
graphql_name 'WorkItemPermissions'
description 'Check permissions for the current user on a work item'
- abilities :read_work_item, :update_work_item, :delete_work_item
+ abilities :read_work_item, :update_work_item, :delete_work_item, :admin_work_item
end
end
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index fe13ee7ef3c..c105ab9814c 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -345,7 +345,7 @@ module Types
null: true,
description: "List of the project's CI/CD variables.",
authorize: :admin_build,
- method: :variables
+ resolver: Resolvers::Ci::VariablesResolver
field :ci_cd_settings, Types::Ci::CiCdSettingType,
null: true,
@@ -393,6 +393,10 @@ module Types
field :services, Types::Projects::ServiceType.connection_type,
null: true,
+ deprecated: {
+ reason: 'This will be renamed to `Project.integrations`',
+ milestone: '15.9'
+ },
description: 'Project services.',
resolver: Resolvers::Projects::ServicesResolver
@@ -562,6 +566,21 @@ module Types
resolver: ::Resolvers::Ci::ProjectRunnersResolver,
description: "Find runners visible to the current user."
+ field :data_transfer, Types::DataTransfer::ProjectDataTransferType,
+ null: true, # disallow null once data_transfer_monitoring feature flag is rolled-out!
+ resolver: Resolvers::DataTransferResolver.project,
+ description: 'Data transfer data point for a specific period. This is mocked data under a development feature flag.'
+
+ field :visible_forks, Types::ProjectType.connection_type,
+ null: true,
+ alpha: { milestone: '15.10' },
+ description: "Visible forks of the project." do
+ argument :minimum_access_level,
+ type: ::Types::AccessLevelEnum,
+ required: false,
+ description: 'Minimum access level.'
+ end
+
def timelog_categories
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
end
@@ -601,7 +620,11 @@ module Types
end
def open_issues_count
- object.open_issues_count if object.feature_available?(:issues, context[:current_user])
+ BatchLoader::GraphQL.wrap(object.open_issues_count) if object.feature_available?(:issues, context[:current_user])
+ end
+
+ def forks_count
+ BatchLoader::GraphQL.wrap(object.forks_count)
end
def statistics
@@ -612,6 +635,8 @@ module Types
project.container_repositories.size
end
+ # Even if the parameter name is `sha`, it is actually a ref name. We always send `ref` to the endpoint.
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/389065
def ci_config_variables(sha:)
result = ::Ci::ListConfigVariablesService.new(object, context[:current_user]).execute(sha)
@@ -630,6 +655,11 @@ module Types
def sast_ci_configuration
return unless Ability.allowed?(current_user, :read_code, object)
+ if project.repository.empty?
+ raise Gitlab::Graphql::Errors::MutationError,
+ _(format('You must %s before using Security features.', add_file_docs_link.html_safe)).html_safe
+ end
+
::Security::CiConfiguration::SastParserService.new(object).configuration
end
@@ -643,11 +673,28 @@ module Types
::Projects::RepositoryLanguagesService.new(project, current_user).execute
end
+ def visible_forks(minimum_access_level: nil)
+ if minimum_access_level.nil?
+ object.forks.public_or_visible_to_user(current_user)
+ else
+ object.forks.visible_to_user_and_access_level(current_user, minimum_access_level)
+ end
+ end
+
private
def project
@project ||= object.respond_to?(:sync) ? object.sync : object
end
+
+ def add_file_docs_link
+ ActionController::Base.helpers.link_to _('add at least one file to the repository'),
+ Rails.application.routes.url_helpers.help_page_url(
+ 'user/project/repository/index.md',
+ anchor: 'add-files-to-a-repository'),
+ target: '_blank',
+ rel: 'noopener noreferrer'
+ end
end
end
diff --git a/app/graphql/types/projects/namespace_project_sort_enum.rb b/app/graphql/types/projects/namespace_project_sort_enum.rb
index bd7058196dd..7c7b54226d3 100644
--- a/app/graphql/types/projects/namespace_project_sort_enum.rb
+++ b/app/graphql/types/projects/namespace_project_sort_enum.rb
@@ -8,6 +8,7 @@ module Types
value 'SIMILARITY', 'Most similar to the search query.', value: :similarity
value 'STORAGE', 'Sort by storage size.', value: :storage
+ value 'ACTIVITY_DESC', 'Sort by latest activity, in descending order.', value: :latest_activity_desc
end
end
end
diff --git a/app/graphql/types/projects/service_type.rb b/app/graphql/types/projects/service_type.rb
index 1416d93d3b4..ec58e3254ae 100644
--- a/app/graphql/types/projects/service_type.rb
+++ b/app/graphql/types/projects/service_type.rb
@@ -2,6 +2,7 @@
module Types
module Projects
+ # TODO: Remove in 17.0, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108418
module ServiceType
include Types::BaseInterface
graphql_name 'Service'
diff --git a/app/graphql/types/projects/service_type_enum.rb b/app/graphql/types/projects/service_type_enum.rb
index d0cecbfea49..fd88fa957e7 100644
--- a/app/graphql/types/projects/service_type_enum.rb
+++ b/app/graphql/types/projects/service_type_enum.rb
@@ -2,6 +2,7 @@
module Types
module Projects
+ # TODO: Remove in 17.0, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108418
class ServiceTypeEnum < BaseEnum
graphql_name 'ServiceType'
diff --git a/app/graphql/types/projects/services/base_service_type.rb b/app/graphql/types/projects/services/base_service_type.rb
index 5341ae2a864..9a48aafa5a8 100644
--- a/app/graphql/types/projects/services/base_service_type.rb
+++ b/app/graphql/types/projects/services/base_service_type.rb
@@ -3,6 +3,7 @@
module Types
module Projects
module Services
+ # TODO: Remove in 17.0, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108418
class BaseServiceType < BaseObject
graphql_name 'BaseService'
diff --git a/app/graphql/types/projects/services/jira_project_type.rb b/app/graphql/types/projects/services/jira_project_type.rb
index 1c5b97802e3..eb721d02b36 100644
--- a/app/graphql/types/projects/services/jira_project_type.rb
+++ b/app/graphql/types/projects/services/jira_project_type.rb
@@ -4,6 +4,7 @@ module Types
module Projects
module Services
# rubocop:disable Graphql/AuthorizeTypes
+ # TODO: Remove in 17.0, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108418
class JiraProjectType < BaseObject
graphql_name 'JiraProject'
diff --git a/app/graphql/types/projects/services/jira_service_type.rb b/app/graphql/types/projects/services/jira_service_type.rb
index 425a283c674..ac274d7f890 100644
--- a/app/graphql/types/projects/services/jira_service_type.rb
+++ b/app/graphql/types/projects/services/jira_service_type.rb
@@ -3,6 +3,7 @@
module Types
module Projects
module Services
+ # TODO: Remove in 17.0, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108418
class JiraServiceType < BaseObject
graphql_name 'JiraService'
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 990ba1fb7fc..fb906759ba4 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -17,7 +17,8 @@ module Types
field :ci_variables,
Types::Ci::InstanceVariableType.connection_type,
null: true,
- description: "List of the instance's CI/CD variables."
+ description: "List of the instance's CI/CD variables.",
+ resolver: Resolvers::Ci::VariablesResolver
field :container_repository, Types::ContainerRepositoryDetailsType,
null: true,
description: 'Find a container repository.' do
@@ -40,6 +41,10 @@ module Types
null: true,
resolver: Resolvers::GroupResolver,
description: "Find a group."
+ field :groups, Types::GroupType.connection_type,
+ null: true,
+ resolver: Resolvers::GroupsResolver,
+ description: "Find groups."
field :issue, Types::IssueType,
null: true,
description: 'Find an issue.' do
@@ -50,8 +55,7 @@ module Types
alpha: { milestone: '15.6' },
resolver: Resolvers::IssuesResolver,
description: 'Find issues visible to the current user.' \
- ' At least one filter must be provided.' \
- ' Returns `null` if the `root_level_issues_query` feature flag is disabled.'
+ ' At least one filter must be provided.'
field :jobs,
::Types::Ci::JobType.connection_type,
null: true,
@@ -76,6 +80,15 @@ module Types
null: true,
resolver: Resolvers::NamespaceResolver,
description: "Find a namespace."
+ field :note,
+ ::Types::Notes::NoteType,
+ null: true,
+ description: 'Find a note.',
+ alpha: { milestone: '15.9' } do
+ argument :id, ::Types::GlobalIDType[::Note],
+ required: true,
+ description: 'Global ID of the note.'
+ end
field :package,
description: 'Find a package. This field can only be resolved for one query in any single request. Returns `null` if a package has no `default` status.',
resolver: Resolvers::PackageDetailsResolver
@@ -95,8 +108,10 @@ module Types
resolver: Resolvers::Ci::RunnerResolver,
extras: [:lookahead],
description: "Find a runner."
- field :runner_platforms, resolver: Resolvers::Ci::RunnerPlatformsResolver
- field :runner_setup, resolver: Resolvers::Ci::RunnerSetupResolver
+ field :runner_platforms, resolver: Resolvers::Ci::RunnerPlatformsResolver,
+ deprecated: { reason: 'No longer used, use gitlab-runner documentation to learn about supported platforms', milestone: '15.9' }
+ field :runner_setup, resolver: Resolvers::Ci::RunnerSetupResolver,
+ deprecated: { reason: 'No longer used, use gitlab-runner documentation to learn about runner registration commands', milestone: '15.9' }
field :runners, Types::Ci::RunnerType.connection_type,
null: true,
resolver: Resolvers::Ci::RunnersResolver,
@@ -106,6 +121,12 @@ module Types
null: true,
resolver: Resolvers::SnippetsResolver,
description: 'Find Snippets visible to the current user.'
+ field :synthetic_note,
+ Types::Notes::NoteType,
+ null: true,
+ description: 'Find a synthetic note',
+ resolver: ::Resolvers::Notes::SyntheticNoteResolver,
+ alpha: { milestone: '15.9' }
field :timelogs, Types::TimelogType.connection_type,
null: true,
description: 'Find timelogs visible to the current user.',
@@ -145,6 +166,10 @@ module Types
GitlabSchema.find_by_gid(id)
end
+ def note(id:)
+ GitlabSchema.find_by_gid(id)
+ end
+
def merge_request(id:)
GitlabSchema.find_by_gid(id)
end
@@ -166,12 +191,6 @@ module Types
application_settings
end
- def ci_variables
- return unless current_user&.can_admin_all_resources?
-
- ::Ci::InstanceVariable.all
- end
-
def application_settings
Gitlab::CurrentSettings.current_application_settings
end
diff --git a/app/graphql/types/release_asset_link_type.rb b/app/graphql/types/release_asset_link_type.rb
index e171c683e7d..b2bc52c7745 100644
--- a/app/graphql/types/release_asset_link_type.rb
+++ b/app/graphql/types/release_asset_link_type.rb
@@ -9,8 +9,12 @@ module Types
present_using Releases::LinkPresenter
- field :external, GraphQL::Types::Boolean, null: true, method: :external?,
- description: 'Indicates the link points to an external resource.'
+ field :external, GraphQL::Types::Boolean,
+ null: true,
+ method: :external?,
+ description: 'Indicates the link points to an external resource.',
+ deprecated: { reason: 'No longer used', milestone: '15.9' }
+
field :id, GraphQL::Types::ID, null: false,
description: 'ID of the link.'
field :link_type,
diff --git a/app/graphql/types/saved_reply_type.rb b/app/graphql/types/saved_reply_type.rb
index 329f431b10e..8c9f3d19810 100644
--- a/app/graphql/types/saved_reply_type.rb
+++ b/app/graphql/types/saved_reply_type.rb
@@ -4,6 +4,8 @@ module Types
class SavedReplyType < BaseObject
graphql_name 'SavedReply'
+ connection_type_class(Types::CountableConnectionType)
+
authorize :read_saved_replies
field :id, Types::GlobalIDType[::Users::SavedReply],
diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb
index f7f26ba4c5a..33fc0cbe20e 100644
--- a/app/graphql/types/subscription_type.rb
+++ b/app/graphql/types/subscription_type.rb
@@ -4,41 +4,60 @@ module Types
class SubscriptionType < ::Types::BaseObject
graphql_name 'Subscription'
- field :issuable_assignees_updated, subscription: Subscriptions::IssuableUpdated, null: true,
- description: 'Triggered when the assignees of an issuable are updated.'
+ field :issuable_assignees_updated,
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the assignees of an issuable are updated.'
- field :issue_crm_contacts_updated, subscription: Subscriptions::IssuableUpdated, null: true,
- description: 'Triggered when the crm contacts of an issuable are updated.'
+ field :issue_crm_contacts_updated,
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the crm contacts of an issuable are updated.'
- field :issuable_title_updated, subscription: Subscriptions::IssuableUpdated, null: true,
- description: 'Triggered when the title of an issuable is updated.'
+ field :issuable_title_updated,
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the title of an issuable is updated.'
- field :issuable_description_updated, subscription: Subscriptions::IssuableUpdated, null: true,
- description: 'Triggered when the description of an issuable is updated.'
+ field :issuable_description_updated,
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the description of an issuable is updated.'
- field :issuable_labels_updated, subscription: Subscriptions::IssuableUpdated, null: true,
- description: 'Triggered when the labels of an issuable are updated.'
+ field :issuable_labels_updated,
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the labels of an issuable are updated.'
- field :issuable_dates_updated, subscription: Subscriptions::IssuableUpdated, null: true,
- description: 'Triggered when the due date or start date of an issuable is updated.'
+ field :issuable_dates_updated,
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the due date or start date of an issuable is updated.'
- field :issuable_milestone_updated, subscription: Subscriptions::IssuableUpdated, null: true,
- description: 'Triggered when the milestone of an issuable is updated.'
+ field :issuable_milestone_updated,
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the milestone of an issuable is updated.'
+
+ field :work_item_note_created,
+ subscription: ::Subscriptions::Notes::Created, null: true,
+ description: 'Triggered when a note is created.',
+ alpha: { milestone: '15.9' }
+
+ field :work_item_note_deleted,
+ subscription: ::Subscriptions::Notes::Deleted, null: true,
+ description: 'Triggered when a note is deleted.',
+ alpha: { milestone: '15.9' }
+
+ field :work_item_note_updated,
+ subscription: ::Subscriptions::Notes::Updated, null: true,
+ description: 'Triggered when a note is updated.',
+ alpha: { milestone: '15.9' }
field :merge_request_reviewers_updated,
- subscription: Subscriptions::IssuableUpdated,
- null: true,
- description: 'Triggered when the reviewers of a merge request are updated.'
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the reviewers of a merge request are updated.'
field :merge_request_merge_status_updated,
- subscription: Subscriptions::IssuableUpdated,
- null: true,
- description: 'Triggered when the merge status of a merge request is updated.'
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when the merge status of a merge request is updated.'
field :merge_request_approval_state_updated,
- subscription: Subscriptions::IssuableUpdated,
- null: true,
- description: 'Triggered when approval state of a merge request is updated.'
+ subscription: Subscriptions::IssuableUpdated, null: true,
+ description: 'Triggered when approval state of a merge request is updated.'
end
end
diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb
index a5bed3b9e19..9115b5a4760 100644
--- a/app/graphql/types/user_interface.rb
+++ b/app/graphql/types/user_interface.rb
@@ -137,6 +137,11 @@ module Types
description: 'Saved replies authored by the user. ' \
'Will not return saved replies if `saved_replies` feature flag is disabled.'
+ field :saved_reply,
+ resolver: Resolvers::SavedReplyResolver,
+ description: 'Saved reply authored by the user. ' \
+ 'Will not return saved reply if `saved_replies` feature flag is disabled.'
+
field :gitpod_enabled, GraphQL::Types::Boolean, null: true,
description: 'Whether Gitpod is enabled at the user level.'
diff --git a/app/graphql/types/work_item_id_type.rb b/app/graphql/types/work_item_id_type.rb
index bb01f865414..777edfad529 100644
--- a/app/graphql/types/work_item_id_type.rb
+++ b/app/graphql/types/work_item_id_type.rb
@@ -37,7 +37,11 @@ module Types
def suitable?(gid)
return false if gid&.model_name&.safe_constantize.blank?
+ # Using === operation doesn't work for model classes.
+ # See https://github.com/rails/rails/blob/v6.1.6.1/activerecord/lib/active_record/core.rb#L452
+ # rubocop:disable Performance/RedundantEqualityComparisonBlock
[::WorkItem, ::Issue].any? { |model_class| gid.model_class == model_class }
+ # rubocop:enable Performance/RedundantEqualityComparisonBlock
end
private
diff --git a/app/graphql/types/work_item_type.rb b/app/graphql/types/work_item_type.rb
index 6a1a4f158be..b46362f66b8 100644
--- a/app/graphql/types/work_item_type.rb
+++ b/app/graphql/types/work_item_type.rb
@@ -8,6 +8,9 @@ module Types
authorize :read_work_item
+ field :author, Types::UserType, null: true,
+ description: 'User that created the work item.',
+ alpha: { milestone: '15.9' }
field :closed_at, Types::TimeType, null: true,
description: 'Timestamp of when the work item was closed.'
field :confidential, GraphQL::Types::Boolean, null: false,
diff --git a/app/graphql/types/work_items/widget_type_enum.rb b/app/graphql/types/work_items/widget_type_enum.rb
index 4e5933bff86..2ad951d421b 100644
--- a/app/graphql/types/work_items/widget_type_enum.rb
+++ b/app/graphql/types/work_items/widget_type_enum.rb
@@ -6,8 +6,8 @@ module Types
graphql_name 'WorkItemWidgetType'
description 'Type of a work item widget'
- ::WorkItems::Type.available_widgets.each do |widget|
- value widget.type.to_s.upcase, value: widget.type, description: "#{widget.type.to_s.titleize} widget."
+ ::WorkItems::WidgetDefinition.widget_classes.each do |cls|
+ value cls.type.to_s.upcase, value: cls.type, description: "#{cls.type.to_s.titleize} widget."
end
end
end