diff options
Diffstat (limited to 'app/graphql/types')
46 files changed, 506 insertions, 114 deletions
diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb index a0d19229d3d..a13453f9194 100644 --- a/app/graphql/types/alert_management/alert_type.rb +++ b/app/graphql/types/alert_management/alert_type.rb @@ -13,6 +13,11 @@ module Types authorize :read_alert_management_alert + field :id, + GraphQL::Types::ID, + null: false, + description: 'ID of the alert.' + field :iid, GraphQL::Types::ID, null: false, @@ -116,7 +121,10 @@ module Types null: true, description: 'Runbook for the alert as defined in alert details.' - field :todos, description: 'To-do items of the current user for the alert.', resolver: Resolvers::TodosResolver + field :todos, + Types::TodoType.connection_type, + description: 'To-do items of the current user for the alert.', + resolver: Resolvers::TodosResolver field :details_url, GraphQL::Types::String, diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb index 36ba3399754..615c143a0b9 100644 --- a/app/graphql/types/base_field.rb +++ b/app/graphql/types/base_field.rb @@ -135,15 +135,16 @@ module Types :resolver_complexity, args, child_complexity: child_complexity ).to_i complexity += 1 if calls_gitaly? - complexity += complexity * connection_complexity_multiplier(ctx, args) + ext_conn = resolver&.try(:calculate_ext_conn_complexity) + complexity += complexity * connection_complexity_multiplier(ctx, args, calculate_ext_conn_complexity: ext_conn) complexity.to_i end end - def connection_complexity_multiplier(ctx, args) + def connection_complexity_multiplier(ctx, args, calculate_ext_conn_complexity:) # Resolvers may add extra complexity depending on number of items being loaded. - return 0 unless connection? + return 0 if !connection? && !calculate_ext_conn_complexity page_size = max_page_size || ctx.schema.default_max_page_size limit_value = [args[:first], args[:last], page_size].compact.min diff --git a/app/graphql/types/ci/config_variable_type.rb b/app/graphql/types/ci/config_variable_type.rb index 5b5890fd5a5..020af5b2444 100644 --- a/app/graphql/types/ci/config_variable_type.rb +++ b/app/graphql/types/ci/config_variable_type.rb @@ -19,6 +19,7 @@ module Types description: 'Value of the variable.' field :value_options, [GraphQL::Types::String], + hash_key: :options, null: true, description: 'Value options for the variable.' end diff --git a/app/graphql/types/ci/freeze_period_status_enum.rb b/app/graphql/types/ci/freeze_period_status_enum.rb new file mode 100644 index 00000000000..aebd0f537e9 --- /dev/null +++ b/app/graphql/types/ci/freeze_period_status_enum.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Types + module Ci + class FreezePeriodStatusEnum < BaseEnum + graphql_name 'CiFreezePeriodStatus' + description 'Deploy freeze period status' + + value 'ACTIVE', value: :active, description: 'Freeze period is active.' + value 'INACTIVE', value: :inactive, description: 'Freeze period is inactive.' + end + end +end diff --git a/app/graphql/types/ci/freeze_period_type.rb b/app/graphql/types/ci/freeze_period_type.rb new file mode 100644 index 00000000000..6a3f2ed8fa4 --- /dev/null +++ b/app/graphql/types/ci/freeze_period_type.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Types + module Ci + class FreezePeriodType < BaseObject + graphql_name 'CiFreezePeriod' + description 'Represents a deployment freeze window of a project' + + authorize :read_freeze_period + + present_using ::Ci::FreezePeriodPresenter + + field :status, Types::Ci::FreezePeriodStatusEnum, + description: 'Freeze period status.', + null: false + + field :start_cron, GraphQL::Types::String, + description: 'Start of the freeze period in cron format.', + null: false, + method: :freeze_start + + field :end_cron, GraphQL::Types::String, + description: 'End of the freeze period in cron format.', + null: false, + method: :freeze_end + + field :cron_timezone, GraphQL::Types::String, + description: 'Time zone for the cron fields, defaults to UTC if not provided.', + null: true + + field :start_time, Types::TimeType, + description: 'Timestamp (UTC) of when the current/next active period starts.', + null: true + + field :end_time, Types::TimeType, + description: 'Timestamp (UTC) of when the current/next active period ends.', + null: true, + method: :time_end_from_now + end + end +end diff --git a/app/graphql/types/ci/pipeline_schedule_type.rb b/app/graphql/types/ci/pipeline_schedule_type.rb index 04f9fc78a92..904fa3f1c72 100644 --- a/app/graphql/types/ci/pipeline_schedule_type.rb +++ b/app/graphql/types/ci/pipeline_schedule_type.rb @@ -5,6 +5,8 @@ module Types class PipelineScheduleType < BaseObject graphql_name 'PipelineSchedule' + description 'Represents a pipeline schedule' + connection_type_class(Types::CountableConnectionType) expose_permissions Types::PermissionTypes::Ci::PipelineSchedules @@ -17,7 +19,9 @@ module Types field :owner, ::Types::UserType, null: false, description: 'Owner of the pipeline schedule.' - field :active, GraphQL::Types::Boolean, null: false, description: 'Indicates if a pipeline schedule is active.' + field :active, GraphQL::Types::Boolean, null: false, description: 'Indicates if the pipeline schedule is active.' + + field :project, ::Types::ProjectType, null: true, description: 'Project of the pipeline schedule.' field :next_run_at, Types::TimeType, null: false, description: 'Time when the next pipeline will run.' @@ -26,20 +30,50 @@ module Types field :last_pipeline, PipelineType, null: true, description: 'Last pipeline object.' field :ref_for_display, GraphQL::Types::String, - null: true, description: 'Git ref for the pipeline schedule.', method: :ref_for_display - - field :ref_path, GraphQL::Types::String, null: true, description: 'Path to the ref that triggered the pipeline.' + null: true, description: 'Git ref for the pipeline schedule.' field :for_tag, GraphQL::Types::Boolean, null: false, description: 'Indicates if a pipelines schedule belongs to a tag.', method: :for_tag? - field :cron, GraphQL::Types::String, null: false, description: 'Cron notation for the schedule.' + field :edit_path, GraphQL::Types::String, + null: true, + description: 'Edit path of the pipeline schedule.', + authorize: :update_pipeline_schedule + + field :variables, + Types::Ci::PipelineScheduleVariableType.connection_type, + null: true, + description: 'Pipeline schedule variables.', + authorize: :read_pipeline_schedule_variables + + field :ref, GraphQL::Types::String, + null: true, description: 'Ref of the pipeline schedule.', method: :ref_for_display + + field :ref_path, GraphQL::Types::String, + null: true, + description: 'Path to the ref that triggered the pipeline.' - field :cron_timezone, GraphQL::Types::String, null: false, description: 'Timezone for the pipeline schedule.' + field :cron, GraphQL::Types::String, + null: false, + description: 'Cron notation for the schedule.' + + field :cron_timezone, GraphQL::Types::String, + null: false, + description: 'Timezone for the pipeline schedule.' + + field :created_at, Types::TimeType, + null: false, description: 'Timestamp of when the pipeline schedule was created.' + + field :updated_at, Types::TimeType, + null: false, description: 'Timestamp of when the pipeline schedule was last updated.' def ref_path ::Gitlab::Routing.url_helpers.project_commits_path(object.project, object.ref_for_display) end + + def edit_path + ::Gitlab::Routing.url_helpers.edit_project_pipeline_schedule_path(object.project, object) + end end end end diff --git a/app/graphql/types/ci/pipeline_schedule_variable_type.rb b/app/graphql/types/ci/pipeline_schedule_variable_type.rb new file mode 100644 index 00000000000..1cb407bc2e4 --- /dev/null +++ b/app/graphql/types/ci/pipeline_schedule_variable_type.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Types + module Ci + class PipelineScheduleVariableType < BaseObject + graphql_name 'PipelineScheduleVariable' + + authorize :read_pipeline_schedule_variables + + implements(VariableInterface) + end + end +end diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb index 4a523f2edd9..cb561f48b3b 100644 --- a/app/graphql/types/ci/pipeline_type.rb +++ b/app/graphql/types/ci/pipeline_type.rb @@ -78,7 +78,7 @@ module Types resolver: Resolvers::Ci::PipelineStagesResolver field :user, - type: Types::UserType, + type: 'Types::UserType', null: true, description: 'Pipeline user.' diff --git a/app/graphql/types/ci/runner_job_execution_status_enum.rb b/app/graphql/types/ci/runner_job_execution_status_enum.rb new file mode 100644 index 00000000000..686ea085199 --- /dev/null +++ b/app/graphql/types/ci/runner_job_execution_status_enum.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Types + module Ci + class RunnerJobExecutionStatusEnum < BaseEnum + graphql_name 'CiRunnerJobExecutionStatus' + + value 'IDLE', + description: "Runner is idle.", + value: :idle, + deprecated: { milestone: '15.7', reason: :alpha } + + value 'RUNNING', + description: 'Runner is executing jobs.', + value: :running, + deprecated: { milestone: '15.7', reason: :alpha } + end + end +end diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb index a9c76974850..5d34906f7b8 100644 --- a/app/graphql/types/ci/runner_type.rb +++ b/app/graphql/types/ci/runner_type.rb @@ -23,6 +23,9 @@ module Types deprecated: { reason: 'Use paused', milestone: '14.8' } field :admin_url, GraphQL::Types::String, null: true, description: 'Admin URL of the runner. Only available for administrators.' + field :architecture_name, GraphQL::Types::String, null: true, + description: 'Architecture provided by the the runner.', + method: :architecture field :contacted_at, Types::TimeType, null: true, description: 'Timestamp of last contact from this runner.', method: :contacted_at @@ -35,32 +38,39 @@ module Types field :executor_name, GraphQL::Types::String, null: true, description: 'Executor last advertised by the runner.', method: :executor_name - field :platform_name, GraphQL::Types::String, null: true, - description: 'Platform provided by the runner.', - method: :platform - field :architecture_name, GraphQL::Types::String, null: true, - description: 'Architecture provided by the the runner.', - method: :architecture - field :maintenance_note, GraphQL::Types::String, null: true, - description: 'Runner\'s maintenance notes.' - field :groups, ::Types::GroupType.connection_type, null: true, - description: 'Groups the runner is associated with. For group runners only.' + field :groups, 'Types::GroupConnection', + null: true, + resolver: ::Resolvers::Ci::RunnerGroupsResolver, + description: 'Groups the runner is associated with. For group runners only.' field :id, ::Types::GlobalIDType[::Ci::Runner], null: false, description: 'ID of the runner.' field :ip_address, GraphQL::Types::String, null: true, description: 'IP address of the runner.' field :job_count, GraphQL::Types::Int, null: true, description: "Number of jobs processed by the runner (limited to #{JOB_COUNT_LIMIT}, plus one to indicate that more items exist)." + field :job_execution_status, + Types::Ci::RunnerJobExecutionStatusEnum, + null: true, + description: 'Job execution status of the runner.', + deprecated: { milestone: '15.7', reason: :alpha } field :jobs, ::Types::Ci::JobType.connection_type, null: true, description: 'Jobs assigned to the runner. This field can only be resolved for one runner in any single request.', authorize: :read_builds, resolver: ::Resolvers::Ci::RunnerJobsResolver field :locked, GraphQL::Types::Boolean, null: true, description: 'Indicates the runner is locked.' + field :maintenance_note, GraphQL::Types::String, null: true, + description: 'Runner\'s maintenance notes.' field :maximum_timeout, GraphQL::Types::Int, null: true, description: 'Maximum timeout (in seconds) for jobs processed by the runner.' + field :owner_project, ::Types::ProjectType, null: true, + description: 'Project that owns the runner. For project runners only.', + resolver: ::Resolvers::Ci::RunnerOwnerProjectResolver field :paused, GraphQL::Types::Boolean, null: false, description: 'Indicates the runner is paused and not available to run jobs.' + field :platform_name, GraphQL::Types::String, null: true, + description: 'Platform provided by the runner.', + method: :platform field :project_count, GraphQL::Types::Int, null: true, description: 'Number of projects that the runner is associated with.' field :projects, @@ -88,9 +98,6 @@ module Types method: :token_expires_at field :version, GraphQL::Types::String, null: true, description: 'Version of the runner.' - field :owner_project, ::Types::ProjectType, null: true, - description: 'Project that owns the runner. For project runners only.', - resolver: ::Resolvers::Ci::RunnerOwnerProjectResolver markdown_field :maintenance_note_html, null: true @@ -99,8 +106,25 @@ module Types end def job_count - # We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT - runner.builds.limit(JOB_COUNT_LIMIT + 1).count + BatchLoader::GraphQL.for(runner.id).batch(key: :job_count) do |runner_ids, loader, _args| + # rubocop: disable CodeReuse/ActiveRecord + # We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT + builds_tbl = ::Ci::Build.arel_table + runners_tbl = ::Ci::Runner.arel_table + lateral_query = ::Ci::Build.select(1) + .where(builds_tbl['runner_id'].eq(runners_tbl['id'])) + .limit(JOB_COUNT_LIMIT + 1) + counts = ::Ci::Runner.joins("JOIN LATERAL (#{lateral_query.to_sql}) builds_with_limit ON true") + .id_in(runner_ids) + .select(:id, Arel.star.count.as('count')) + .group(:id) + .index_by(&:id) + # rubocop: enable CodeReuse/ActiveRecord + + runner_ids.each do |runner_id| + loader.call(runner_id, counts[runner_id]&.count || 0) + end + end end def admin_url @@ -111,14 +135,13 @@ module Types Gitlab::Routing.url_helpers.edit_admin_runner_url(runner) if can_admin_runners? end - # rubocop: disable CodeReuse/ActiveRecord def project_count BatchLoader::GraphQL.for(runner.id).batch(key: :runner_project_count) do |ids, loader, args| counts = ::Ci::Runner.project_type .select(:id, 'COUNT(ci_runner_projects.id) as count') .left_outer_joins(:runner_projects) - .where(id: ids) - .group(:id) + .id_in(ids) + .group(:id) # rubocop: disable CodeReuse/ActiveRecord .index_by(&:id) ids.each do |id| @@ -126,12 +149,15 @@ module Types end end end - # rubocop: enable CodeReuse/ActiveRecord - def groups - return unless runner.group_type? + def job_execution_status + BatchLoader::GraphQL.for(runner.id).batch(key: :running_builds_exist) do |runner_ids, loader| + statuses = ::Ci::Runner.id_in(runner_ids).with_running_builds.index_by(&:id) - batched_owners(::Ci::RunnerNamespace, Group, :runner_groups, :namespace_id) + runner_ids.each do |runner_id| + loader.call(runner_id, statuses[runner_id] ? :running : :idle) + end + end end private @@ -139,29 +165,6 @@ module Types def can_admin_runners? context[:current_user]&.can_admin_all_resources? end - - # rubocop: disable CodeReuse/ActiveRecord - def batched_owners(runner_assoc_type, assoc_type, key, column_name) - BatchLoader::GraphQL.for(runner.id).batch(key: key) do |runner_ids, loader| - plucked_runner_and_owner_ids = runner_assoc_type - .select(:runner_id, column_name) - .where(runner_id: runner_ids) - .pluck(:runner_id, column_name) - # In plucked_runner_and_owner_ids, first() represents the runner ID, and second() the owner ID, - # so let's group the owner IDs by runner ID - runner_owner_ids_by_runner_id = plucked_runner_and_owner_ids - .group_by(&:first) - .transform_values { |runner_and_owner_id| runner_and_owner_id.map(&:second) } - - owner_ids = runner_owner_ids_by_runner_id.values.flatten.uniq - owners = assoc_type.where(id: owner_ids).index_by(&:id) - - runner_ids.each do |runner_id| - loader.call(runner_id, runner_owner_ids_by_runner_id[runner_id]&.map { |owner_id| owners[owner_id] } || []) - end - end - end - # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/app/graphql/types/commit_signature_interface.rb b/app/graphql/types/commit_signature_interface.rb index 6b0c16e538a..0449a0634ef 100644 --- a/app/graphql/types/commit_signature_interface.rb +++ b/app/graphql/types/commit_signature_interface.rb @@ -21,7 +21,8 @@ module Types description: 'Project of the associated commit.' orphan_types Types::CommitSignatures::GpgSignatureType, - Types::CommitSignatures::X509SignatureType + Types::CommitSignatures::X509SignatureType, + Types::CommitSignatures::SshSignatureType def self.resolve_type(object, context) case object @@ -29,6 +30,8 @@ module Types Types::CommitSignatures::GpgSignatureType when ::CommitSignatures::X509CommitSignature Types::CommitSignatures::X509SignatureType + when ::CommitSignatures::SshSignature + Types::CommitSignatures::SshSignatureType else raise 'Unsupported commit signature type' end diff --git a/app/graphql/types/commit_signatures/gpg_signature_type.rb b/app/graphql/types/commit_signatures/gpg_signature_type.rb index 2a845fff3e2..3baf2d9d21d 100644 --- a/app/graphql/types/commit_signatures/gpg_signature_type.rb +++ b/app/graphql/types/commit_signatures/gpg_signature_type.rb @@ -11,6 +11,7 @@ module Types authorize :download_code field :user, Types::UserType, null: true, + method: :signed_by_user, description: 'User associated with the key.' field :gpg_key_user_name, GraphQL::Types::String, diff --git a/app/graphql/types/commit_signatures/ssh_signature_type.rb b/app/graphql/types/commit_signatures/ssh_signature_type.rb new file mode 100644 index 00000000000..92eb4f7949a --- /dev/null +++ b/app/graphql/types/commit_signatures/ssh_signature_type.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Types + module CommitSignatures + class SshSignatureType < Types::BaseObject + graphql_name 'SshSignature' + description 'SSH signature for a signed commit' + + implements Types::CommitSignatureInterface + + authorize :download_code + + field :user, Types::UserType, null: true, + method: :signed_by_user, + calls_gitaly: true, + description: 'User associated with the key.' + + field :key, Types::KeyType, + null: true, + description: 'SSH key used for the signature.' + end + end +end diff --git a/app/graphql/types/commit_signatures/x509_signature_type.rb b/app/graphql/types/commit_signatures/x509_signature_type.rb index 9ac96dbc015..2d58c3d5b5d 100644 --- a/app/graphql/types/commit_signatures/x509_signature_type.rb +++ b/app/graphql/types/commit_signatures/x509_signature_type.rb @@ -11,6 +11,7 @@ module Types authorize :download_code field :user, Types::UserType, null: true, + method: :signed_by_user, calls_gitaly: true, description: 'User associated with the key.' diff --git a/app/graphql/types/container_repository_type.rb b/app/graphql/types/container_repository_type.rb index cb818ac5e92..dfa599e798c 100644 --- a/app/graphql/types/container_repository_type.rb +++ b/app/graphql/types/container_repository_type.rb @@ -13,6 +13,7 @@ module Types field :expiration_policy_cleanup_status, Types::ContainerRepositoryCleanupStatusEnum, null: true, description: 'Tags cleanup status for the container repository.' field :expiration_policy_started_at, Types::TimeType, null: true, description: 'Timestamp when the cleanup done by the expiration policy was started on the container repository.' field :id, GraphQL::Types::ID, null: false, description: 'ID of the container repository.' + field :last_cleanup_deleted_tags_count, GraphQL::Types::Int, null: true, description: 'Number of deleted tags from the last cleanup.' field :location, GraphQL::Types::String, null: false, description: 'URL of the container repository.' field :migration_state, GraphQL::Types::String, null: false, description: 'Migration state of the container repository.' field :name, GraphQL::Types::String, null: false, description: 'Name of the container repository.' @@ -21,7 +22,6 @@ module Types field :status, Types::ContainerRepositoryStatusEnum, null: true, description: 'Status of the container repository.' field :tags_count, GraphQL::Types::Int, null: false, description: 'Number of tags associated with this image.' field :updated_at, Types::TimeType, null: false, description: 'Timestamp when the container repository was updated.' - field :last_cleanup_deleted_tags_count, GraphQL::Types::Int, null: true, description: 'Number of deleted tags from the last cleanup.' def can_delete Ability.allowed?(current_user, :update_container_image, object) diff --git a/app/graphql/types/dependency_proxy/manifest_type.rb b/app/graphql/types/dependency_proxy/manifest_type.rb index f7e751e30d3..53b7610e490 100644 --- a/app/graphql/types/dependency_proxy/manifest_type.rb +++ b/app/graphql/types/dependency_proxy/manifest_type.rb @@ -14,11 +14,11 @@ module Types field :id, ::Types::GlobalIDType[::DependencyProxy::Manifest], null: false, description: 'ID of the manifest.' field :image_name, GraphQL::Types::String, null: false, description: 'Name of the image.' field :size, GraphQL::Types::String, null: false, description: 'Size of the manifest file.' - field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.' field :status, Types::DependencyProxy::ManifestTypeEnum, null: false, description: "Status of the manifest (#{::DependencyProxy::Manifest.statuses.keys.join(', ')})" + field :updated_at, Types::TimeType, null: false, description: 'Date of most recent update.' def image_name object.file_name.chomp(File.extname(object.file_name)) diff --git a/app/graphql/types/deployment_details_type.rb b/app/graphql/types/deployment_details_type.rb deleted file mode 100644 index bbb5cc8e3f1..00000000000 --- a/app/graphql/types/deployment_details_type.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module Types - class DeploymentDetailsType < DeploymentType - graphql_name 'DeploymentDetails' - description 'The details of the deployment' - authorize :read_deployment - present_using ::Deployments::DeploymentPresenter - - field :tags, - [Types::DeploymentTagType], - description: 'Git tags that contain this deployment.', - calls_gitaly: true - end -end - -Types::DeploymentDetailsType.prepend_mod_with('Types::DeploymentDetailsType') diff --git a/app/graphql/types/deployment_type.rb b/app/graphql/types/deployment_type.rb index 59b59dc4e1d..1c23fd44ea1 100644 --- a/app/graphql/types/deployment_type.rb +++ b/app/graphql/types/deployment_type.rb @@ -1,12 +1,6 @@ # frozen_string_literal: true module Types - # If you're considering to add a new field in DeploymentType, please follow this guideline: - # - If the field is preloadable in batch, define it in DeploymentType. - # In this case, you should extend DeploymentsResolver logic to preload the field. Also, add a new test that - # fetching the specific field for multiple deployments doesn't cause N+1 query problem. - # - If the field is NOT preloadable in batch, define it in DeploymentDetailsType. - # This type can be only fetched for a single deployment, so you don't need to take care of the preloading. class DeploymentType < BaseObject graphql_name 'Deployment' description 'The deployment of an environment' @@ -15,6 +9,8 @@ module Types authorize :read_deployment + expose_permissions Types::PermissionTypes::Deployment + field :id, GraphQL::Types::ID, description: 'Global ID of the deployment.' @@ -65,5 +61,15 @@ module Types Types::UserType, description: 'User who executed the deployment.', method: :deployed_by + + field :tags, + [Types::DeploymentTagType], + description: 'Git tags that contain this deployment. ' \ + 'This field can only be resolved for one deployment in any single request.', + calls_gitaly: true do + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + end end end + +Types::DeploymentType.prepend_mod_with('Types::DeploymentType') diff --git a/app/graphql/types/environment_type.rb b/app/graphql/types/environment_type.rb index dd2286d333d..5f58fc38540 100644 --- a/app/graphql/types/environment_type.rb +++ b/app/graphql/types/environment_type.rb @@ -9,6 +9,12 @@ module Types authorize :read_environment + expose_permissions Types::PermissionTypes::Environment, + description: 'Permissions for the current user on the resource. '\ + 'This field can only be resolved for one environment in any single request.' do + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + end + field :name, GraphQL::Types::String, null: false, description: 'Human-readable name of the environment.' @@ -67,6 +73,11 @@ module Types description: 'Last deployment of the environment.', resolver: Resolvers::Environments::LastDeploymentResolver + field :deploy_freezes, + [Types::Ci::FreezePeriodType], + null: true, + description: 'Deployment freeze periods of the environment.' + def tier object.tier.to_sym end diff --git a/app/graphql/types/global_id_type.rb b/app/graphql/types/global_id_type.rb index a71c2fb0e6c..7ebd98ff2e7 100644 --- a/app/graphql/types/global_id_type.rb +++ b/app/graphql/types/global_id_type.rb @@ -49,9 +49,7 @@ module Types An example `#{graphql_name}` is: `"#{::Gitlab::GlobalId.build(model_name: model_name, id: 1)}"`. #{ if deprecation = Gitlab::GlobalId::Deprecations.deprecation_by(model_name) - 'The older format `"' + - ::Gitlab::GlobalId.build(model_name: deprecation.old_name, id: 1).to_s + - '"` was deprecated in ' + deprecation.milestone + '.' + "The older format `\"#{::Gitlab::GlobalId.build(model_name: deprecation.old_name, id: 1)}\"` was deprecated in #{deprecation.milestone}." end} MD diff --git a/app/graphql/types/group_connection.rb b/app/graphql/types/group_connection.rb new file mode 100644 index 00000000000..e4332e24302 --- /dev/null +++ b/app/graphql/types/group_connection.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# Normally this wouldn't be needed and we could use +# +# type Types::GroupType.connection_type, null: true +# +# in a resolver. However we can end up with cyclic definitions. +# Running the spec locally can result in errors like +# +# NameError: uninitialized constant Types::GroupType +# +# or other errors. To fix this, we created this file and use +# +# type "Types::GroupConnection", null: true +# +# which gives a delayed resolution, and the proper connection type. +# +# See gitlab/app/graphql/types/ci/runner_type.rb +# Reference: https://github.com/rmosolgo/graphql-ruby/issues/3974#issuecomment-1084444214 +# and https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#testing-tips-and-tricks +# +Types::GroupConnection = Types::GroupType.connection_type diff --git a/app/graphql/types/issue_type_enum.rb b/app/graphql/types/issue_type_enum.rb index 78cd27f60c3..d7f587ff03d 100644 --- a/app/graphql/types/issue_type_enum.rb +++ b/app/graphql/types/issue_type_enum.rb @@ -16,5 +16,9 @@ module Types value 'OBJECTIVE', value: 'objective', description: 'Objective issue type. Available only when feature flag `okrs_mvc` is enabled.', alpha: { milestone: '15.6' } + + value 'KEY_RESULT', value: 'key_result', + description: 'Key Result issue type. Available only when feature flag `okrs_mvc` is enabled.', + alpha: { milestone: '15.7' } end end diff --git a/app/graphql/types/key_type.rb b/app/graphql/types/key_type.rb new file mode 100644 index 00000000000..30699793045 --- /dev/null +++ b/app/graphql/types/key_type.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Types + class KeyType < Types::BaseObject # rubocop:disable Graphql/AuthorizeTypes + graphql_name 'Key' + description 'Represents an SSH key.' + + field :created_at, Types::TimeType, null: false, + description: 'Timestamp of when the key was created.' + field :expires_at, Types::TimeType, null: false, + description: "Timestamp of when the key expires. It's null if it never expires." + field :id, GraphQL::Types::ID, null: false, description: 'ID of the key.' + field :key, GraphQL::Types::String, null: false, method: :publishable_key, + description: 'Public key of the key pair.' + field :title, GraphQL::Types::String, null: false, description: 'Title of the key.' + end +end diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index 49bf7aa638c..abf7b3ad530 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -200,10 +200,10 @@ module Types description: 'Array of available auto merge strategies.' field :commits, Types::CommitType.connection_type, null: true, calls_gitaly: true, description: 'Merge request commits.' - field :committers, Types::UserType.connection_type, null: true, complexity: 5, - calls_gitaly: true, description: 'Users who have added commits to the merge request.' field :commits_without_merge_commits, Types::CommitType.connection_type, null: true, calls_gitaly: true, description: 'Merge request commits excluding merge commits.' + field :committers, Types::UserType.connection_type, null: true, complexity: 5, + calls_gitaly: true, description: 'Users who have added commits to the merge request.' field :has_ci, GraphQL::Types::Boolean, null: false, method: :has_ci?, description: 'Indicates if the merge request has CI.' field :merge_user, Types::UserType, null: true, diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 1cbb2ede544..b342e57804b 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -63,6 +63,8 @@ module Types mount_mutation Mutations::Issues::SetEscalationStatus mount_mutation Mutations::Issues::Update mount_mutation Mutations::Issues::Move + mount_mutation Mutations::Issues::LinkAlerts + mount_mutation Mutations::Issues::UnlinkAlert mount_mutation Mutations::Labels::Create mount_mutation Mutations::MergeRequests::Accept mount_mutation Mutations::MergeRequests::Create @@ -117,6 +119,8 @@ module Types mount_mutation Mutations::Ci::Pipeline::Retry mount_mutation Mutations::Ci::PipelineSchedule::Delete mount_mutation Mutations::Ci::PipelineSchedule::TakeOwnership + mount_mutation Mutations::Ci::PipelineSchedule::Play + mount_mutation Mutations::Ci::PipelineSchedule::Create mount_mutation Mutations::Ci::CiCdSettingsUpdate, deprecated: { reason: :renamed, replacement: 'ProjectCiCdSettingsUpdate', diff --git a/app/graphql/types/nested_environment_type.rb b/app/graphql/types/nested_environment_type.rb new file mode 100644 index 00000000000..b835af2bf45 --- /dev/null +++ b/app/graphql/types/nested_environment_type.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + class NestedEnvironmentType < BaseObject + graphql_name 'NestedEnvironment' + description 'Describes where code is deployed for a project organized by folder.' + + field :name, GraphQL::Types::String, + null: false, description: 'Human-readable name of the environment.' + + field :size, GraphQL::Types::Int, + null: false, description: 'Number of environments nested in the folder.' + + field :environment, + Types::EnvironmentType, + null: true, description: 'Latest environment in the folder.' + + def environment + BatchLoader::GraphQL.for(object.last_id).batch do |environment_ids, loader| + Environment.id_in(environment_ids).each do |environment| + loader.call(environment.id, environment) + end + end + end + end + # rubocop: enable Graphql/AuthorizeTypes +end diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb index eef5ce40bde..05629ea9223 100644 --- a/app/graphql/types/notes/note_type.rb +++ b/app/graphql/types/notes/note_type.rb @@ -77,6 +77,14 @@ module Types def author Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find end + + # We now support also SyntheticNote notes as a NoteType, but SyntheticNote does not have a real note ID, + # as SyntheticNote is generated dynamically from a ResourceEvent instance. + def id + return super unless object.is_a?(SyntheticNote) + + ::Gitlab::GlobalId.build(object, model_name: object.class.to_s, id: object.discussion_id) + end end end end diff --git a/app/graphql/types/packages/package_links_type.rb b/app/graphql/types/packages/package_links_type.rb index f16937530b9..eb29fb655bd 100644 --- a/app/graphql/types/packages/package_links_type.rb +++ b/app/graphql/types/packages/package_links_type.rb @@ -12,6 +12,8 @@ module Types field :web_path, GraphQL::Types::String, null: true, description: 'Path to the package details page.' def web_path + return unless object.default? + package_path(object) 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 07e6e7a55d6..0192af25d0f 100644 --- a/app/graphql/types/permission_types/base_permission_type.rb +++ b/app/graphql/types/permission_types/base_permission_type.rb @@ -11,20 +11,20 @@ module Types abilities.each { |ability| ability_field(ability) } end - def self.ability_field(ability, **kword_args) + def self.ability_field(ability, **kword_args, &block) define_field_resolver_method(ability) unless resolving_keywords?(kword_args) - permission_field(ability, **kword_args) + permission_field(ability, **kword_args, &block) end - def self.permission_field(name, **kword_args) + def self.permission_field(name, **kword_args, &block) kword_args = kword_args.reverse_merge( name: name, type: GraphQL::Types::Boolean, description: "Indicates the user can perform `#{name}` on this resource", null: false) - field(**kword_args) # rubocop:disable Graphql/Descriptions + field(**kword_args, &block) # rubocop:disable Graphql/Descriptions end def self.define_field_resolver_method(ability) diff --git a/app/graphql/types/permission_types/deployment.rb b/app/graphql/types/permission_types/deployment.rb new file mode 100644 index 00000000000..fce376552b1 --- /dev/null +++ b/app/graphql/types/permission_types/deployment.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Types + module PermissionTypes + class Deployment < BasePermissionType + graphql_name 'DeploymentPermissions' + + abilities :destroy_deployment + ability_field :update_deployment, calls_gitaly: true + end + end +end + +Types::PermissionTypes::Deployment.prepend_mod_with('Types::PermissionTypes::Deployment') diff --git a/app/graphql/types/permission_types/environment.rb b/app/graphql/types/permission_types/environment.rb new file mode 100644 index 00000000000..59c9fce64e5 --- /dev/null +++ b/app/graphql/types/permission_types/environment.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + module PermissionTypes + class Environment < BasePermissionType + graphql_name 'EnvironmentPermissions' + + abilities :update_environment, :destroy_environment, :stop_environment + end + end +end diff --git a/app/graphql/types/permission_types/project.rb b/app/graphql/types/permission_types/project.rb index f6a5563d367..c833b512222 100644 --- a/app/graphql/types/permission_types/project.rb +++ b/app/graphql/types/permission_types/project.rb @@ -17,7 +17,8 @@ module Types :admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content, :admin_operations, - :read_merge_request, :read_design, :create_design, :destroy_design + :read_merge_request, :read_design, :create_design, :destroy_design, + :read_environment permission_field :create_snippet diff --git a/app/graphql/types/project_statistics_type.rb b/app/graphql/types/project_statistics_type.rb index c43baf1280b..a1d721856a9 100644 --- a/app/graphql/types/project_statistics_type.rb +++ b/app/graphql/types/project_statistics_type.rb @@ -11,6 +11,10 @@ module Types field :build_artifacts_size, GraphQL::Types::Float, null: false, description: 'Build artifacts size of the project in bytes.' + field :container_registry_size, + GraphQL::Types::Float, + null: true, + description: 'Container Registry size of the project in bytes.' field :lfs_objects_size, GraphQL::Types::Float, null: false, @@ -29,9 +33,5 @@ module Types description: 'Uploads size of the project in bytes.' field :wiki_size, GraphQL::Types::Float, null: true, description: 'Wiki size of the project in bytes.' - field :container_registry_size, - GraphQL::Types::Float, - null: true, - description: 'Container Registry size of the project in bytes.' end end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 771dad00fb3..fe13ee7ef3c 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -258,8 +258,11 @@ module Types field :environments, Types::EnvironmentType.connection_type, null: true, - description: 'Environments of the project.', - resolver: Resolvers::EnvironmentsResolver + description: 'Environments of the project. ' \ + 'This field can only be resolved for one project in any single request.', + resolver: Resolvers::EnvironmentsResolver do + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + end field :environment, Types::EnvironmentType, @@ -267,8 +270,18 @@ module Types description: 'A single environment of the project.', resolver: Resolvers::EnvironmentsResolver.single + field :nested_environments, + Types::NestedEnvironmentType.connection_type, + null: true, + calls_gitaly: true, + description: 'Environments for this project with nested folders, ' \ + 'can only be resolved for one project in any single request', + resolver: Resolvers::Environments::NestedEnvironmentsResolver do + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + end + field :deployment, - Types::DeploymentDetailsType, + Types::DeploymentType, null: true, description: 'Details of the deployment of the project.', resolver: Resolvers::DeploymentResolver.single @@ -526,6 +539,13 @@ module Types resolver: Resolvers::Projects::ForkTargetsResolver, description: 'Namespaces in which the current user can fork the project into.' + field :fork_details, Types::Projects::ForkDetailsType, + calls_gitaly: true, + alpha: { milestone: '15.7' }, + authorize: :read_code, + resolver: Resolvers::Projects::ForkDetailsResolver, + description: 'Details of the fork project compared to its upstream project.' + field :branch_rules, Types::Projects::BranchRuleType.connection_type, null: true, @@ -537,6 +557,11 @@ module Types description: "Programming languages used in the project.", calls_gitaly: true + field :runners, Types::Ci::RunnerType.connection_type, + null: true, + resolver: ::Resolvers::Ci::ProjectRunnersResolver, + description: "Find runners visible to the current user." + def timelog_categories object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories) end diff --git a/app/graphql/types/projects/fork_details_type.rb b/app/graphql/types/projects/fork_details_type.rb new file mode 100644 index 00000000000..88c17d89620 --- /dev/null +++ b/app/graphql/types/projects/fork_details_type.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Types + module Projects + # rubocop: disable Graphql/AuthorizeTypes + class ForkDetailsType < BaseObject + graphql_name 'ForkDetails' + description 'Details of the fork project compared to its upstream project.' + + field :ahead, GraphQL::Types::Int, + null: true, + description: 'Number of commits ahead of upstream.' + + field :behind, GraphQL::Types::Int, + null: true, + description: 'Number of commits behind upstream.' + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 21cb3f9e06c..7263f792bae 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -67,7 +67,7 @@ module Types end field :package, - description: 'Find a package. This field can only be resolved for one query in any single request.', + 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 field :user, Types::UserType, diff --git a/app/graphql/types/release_type.rb b/app/graphql/types/release_type.rb index a20e53ad1bd..8516256b433 100644 --- a/app/graphql/types/release_type.rb +++ b/app/graphql/types/release_type.rb @@ -13,9 +13,6 @@ module Types present_using ReleasePresenter - field :id, ::Types::GlobalIDType[Release], - null: false, - description: 'Global ID of the release.' field :assets, Types::ReleaseAssetsType, null: true, method: :itself, description: 'Assets of the release.' field :created_at, Types::TimeType, null: true, @@ -26,6 +23,11 @@ module Types description: 'Description (also known as "release notes") of the release.' field :evidences, Types::EvidenceType.connection_type, null: true, description: 'Evidence for the release.' + field :historical_release, GraphQL::Types::Boolean, null: true, method: :historical_release?, + description: 'Indicates the release is an historical release.' + field :id, ::Types::GlobalIDType[Release], + null: false, + description: 'Global ID of the release.' field :links, Types::ReleaseLinksType, null: true, method: :itself, description: 'Links of the release.' field :milestones, Types::MilestoneType.connection_type, null: true, @@ -42,8 +44,6 @@ module Types authorize: :read_code field :upcoming_release, GraphQL::Types::Boolean, null: true, method: :upcoming_release?, description: 'Indicates the release is an upcoming release.' - field :historical_release, GraphQL::Types::Boolean, null: true, method: :historical_release?, - description: 'Indicates the release is an historical release.' field :author, Types::UserType, null: true, description: 'User that created the release.' diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb index b1b712aab38..64aaf3e73a0 100644 --- a/app/graphql/types/root_storage_statistics_type.rb +++ b/app/graphql/types/root_storage_statistics_type.rb @@ -7,6 +7,7 @@ module Types authorize :read_statistics field :build_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI artifacts size in bytes.' + field :container_registry_size, GraphQL::Types::Float, null: false, description: 'Container Registry size in bytes.' field :dependency_proxy_size, GraphQL::Types::Float, null: false, description: 'Dependency Proxy sizes in bytes.' field :lfs_objects_size, GraphQL::Types::Float, null: false, description: 'LFS objects size in bytes.' field :packages_size, GraphQL::Types::Float, null: false, description: 'Packages size in bytes.' @@ -16,6 +17,5 @@ module Types field :storage_size, GraphQL::Types::Float, null: false, description: 'Total storage in bytes.' field :uploads_size, GraphQL::Types::Float, null: false, description: 'Uploads size in bytes.' field :wiki_size, GraphQL::Types::Float, null: false, description: 'Wiki size in bytes.' - field :container_registry_size, GraphQL::Types::Float, null: false, description: 'Container Registry size in bytes.' end end diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb index 9d5edec82b2..f7f26ba4c5a 100644 --- a/app/graphql/types/subscription_type.rb +++ b/app/graphql/types/subscription_type.rb @@ -34,6 +34,11 @@ module Types 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.' end end diff --git a/app/graphql/types/todo_action_enum.rb b/app/graphql/types/todo_action_enum.rb index ef43b6eb464..33e1c4e98a4 100644 --- a/app/graphql/types/todo_action_enum.rb +++ b/app/graphql/types/todo_action_enum.rb @@ -5,11 +5,12 @@ module Types value 'assigned', value: 1, description: 'User was assigned.' value 'mentioned', value: 2, description: 'User was mentioned.' value 'build_failed', value: 3, description: 'Build triggered by the user failed.' - value 'marked', value: 4, description: 'User added a TODO.' + value 'marked', value: 4, description: 'User added a to-do item.' value 'approval_required', value: 5, description: 'User was set as an approver.' value 'unmergeable', value: 6, description: 'Merge request authored by the user could not be merged.' value 'directly_addressed', value: 7, description: 'User was directly addressed.' value 'merge_train_removed', value: 8, description: 'Merge request authored by the user was removed from the merge train.' value 'review_requested', value: 9, description: 'Review was requested from the user.' + value 'member_access_requested', value: 10, description: 'Group access requested from the user.' end end diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb index 0de6b1d6f8a..6e5ce35033b 100644 --- a/app/graphql/types/todo_type.rb +++ b/app/graphql/types/todo_type.rb @@ -15,13 +15,11 @@ module Types field :project, Types::ProjectType, description: 'Project this to-do item is associated with.', - null: true, - authorize: :read_project + null: true field :group, 'Types::GroupType', description: 'Group this to-do item is associated with.', - null: true, - authorize: :read_group + null: true field :author, Types::UserType, description: 'Author of this to-do item.', diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb index f49b3eee4f5..51046d09f90 100644 --- a/app/graphql/types/user_interface.rb +++ b/app/graphql/types/user_interface.rb @@ -88,7 +88,10 @@ module Types null: true, description: 'Personal namespace of the user.' - field :todos, resolver: Resolvers::TodosResolver, description: 'To-do items of the user.' + field :todos, + Types::TodoType.connection_type, + description: 'To-do items of the user.', + resolver: Resolvers::TodosResolver # Merge request field: MRs can be authored, assigned, or assigned-for-review: field :authored_merge_requests, diff --git a/app/graphql/types/work_items/notes_filter_type_enum.rb b/app/graphql/types/work_items/notes_filter_type_enum.rb new file mode 100644 index 00000000000..93fb4689f0b --- /dev/null +++ b/app/graphql/types/work_items/notes_filter_type_enum.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Types + module WorkItems + class NotesFilterTypeEnum < BaseEnum + graphql_name 'NotesFilterType' + description 'Work item notes collection type.' + + ::UserPreference::NOTES_FILTERS.each_pair do |key, value| + value key.upcase, + value: value, + description: UserPreference.notes_filters.invert[::UserPreference::NOTES_FILTERS[key]] + end + + def self.default_value + ::UserPreference::NOTES_FILTERS[:all_notes] + end + end + end +end diff --git a/app/graphql/types/work_items/widget_interface.rb b/app/graphql/types/work_items/widget_interface.rb index b85d0a23535..672a78f12e1 100644 --- a/app/graphql/types/work_items/widget_interface.rb +++ b/app/graphql/types/work_items/widget_interface.rb @@ -17,7 +17,8 @@ module Types ::Types::WorkItems::Widgets::LabelsType, ::Types::WorkItems::Widgets::AssigneesType, ::Types::WorkItems::Widgets::StartAndDueDateType, - ::Types::WorkItems::Widgets::MilestoneType + ::Types::WorkItems::Widgets::MilestoneType, + ::Types::WorkItems::Widgets::NotesType ].freeze def self.ce_orphan_types @@ -41,6 +42,8 @@ module Types ::Types::WorkItems::Widgets::StartAndDueDateType when ::WorkItems::Widgets::Milestone ::Types::WorkItems::Widgets::MilestoneType + when ::WorkItems::Widgets::Notes + ::Types::WorkItems::Widgets::NotesType else raise "Unknown GraphQL type for widget #{object}" end diff --git a/app/graphql/types/work_items/widgets/hierarchy_type.rb b/app/graphql/types/work_items/widgets/hierarchy_type.rb index 0ccd8af7dc8..4ec8ec84779 100644 --- a/app/graphql/types/work_items/widgets/hierarchy_type.rb +++ b/app/graphql/types/work_items/widgets/hierarchy_type.rb @@ -20,8 +20,29 @@ module Types null: true, complexity: 5, description: 'Child work items.' + field :has_children, GraphQL::Types::Boolean, + null: false, description: 'Indicates if the work item has children.' + + # rubocop: disable CodeReuse/ActiveRecord + def has_children? + BatchLoader::GraphQL.for(object.work_item.id).batch(default_value: false) do |ids, loader| + links_for_parents = ::WorkItems::ParentLink.for_parents(ids) + .select(:work_item_parent_id) + .group(:work_item_parent_id) + .reorder(nil) + + links_for_parents.each { |link| loader.call(link.work_item_parent_id, true) } + end + end + # rubocop: enable CodeReuse/ActiveRecord + + alias_method :has_children, :has_children? + def children - object.children.inc_relations_for_permission_check + relation = object.children + relation = relation.inc_relations_for_permission_check unless object.children.loaded? + + relation end end # rubocop:enable Graphql/AuthorizeTypes diff --git a/app/graphql/types/work_items/widgets/notes_type.rb b/app/graphql/types/work_items/widgets/notes_type.rb new file mode 100644 index 00000000000..7da2777beee --- /dev/null +++ b/app/graphql/types/work_items/widgets/notes_type.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Types + module WorkItems + module Widgets + # Disabling widget level authorization as it might be too granular + # and we already authorize the parent work item + # rubocop:disable Graphql/AuthorizeTypes + class NotesType < BaseObject + graphql_name 'WorkItemWidgetNotes' + description 'Represents a notes widget' + + implements Types::WorkItems::WidgetInterface + + # This field loads user comments, system notes and resource events as a discussion for an work item, + # raising the complexity considerably. In order to discourage fetching this field as part of fetching + # a list of issues we raise the complexity + field :discussions, Types::Notes::DiscussionType.connection_type, + null: true, + description: "Notes on this work item.", + resolver: Resolvers::WorkItems::WorkItemDiscussionsResolver + end + # rubocop:enable Graphql/AuthorizeTypes + end + end +end |