diff options
Diffstat (limited to 'app/graphql')
41 files changed, 265 insertions, 202 deletions
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb index eed7959a2f1..0c7195c5be3 100644 --- a/app/graphql/gitlab_schema.rb +++ b/app/graphql/gitlab_schema.rb @@ -15,9 +15,6 @@ class GitlabSchema < GraphQL::Schema use Gitlab::Graphql::Tracers::MetricsTracer use Gitlab::Graphql::Tracers::LoggerTracer - # TODO: Old tracer which will be removed eventually - # See https://gitlab.com/gitlab-org/gitlab/-/issues/345396 - use Gitlab::Graphql::GenericTracing use Gitlab::Graphql::Tracers::TimerTracer use Gitlab::Graphql::Subscriptions::ActionCableWithLoadBalancing diff --git a/app/graphql/mutations/alert_management/prometheus_integration/create.rb b/app/graphql/mutations/alert_management/prometheus_integration/create.rb index 9c3aefce033..b06a4f58df5 100644 --- a/app/graphql/mutations/alert_management/prometheus_integration/create.rb +++ b/app/graphql/mutations/alert_management/prometheus_integration/create.rb @@ -17,7 +17,7 @@ module Mutations description: 'Whether the integration is receiving alerts.' argument :api_url, GraphQL::Types::String, - required: true, + required: false, description: 'Endpoint at which Prometheus can be queried.' 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 6071d6750c2..0358bb11c58 100644 --- a/app/graphql/mutations/ci/job_token_scope/add_project.rb +++ b/app/graphql/mutations/ci/job_token_scope/add_project.rb @@ -35,14 +35,13 @@ module Mutations def resolve(project_path:, target_project_path:, direction: nil) project = authorized_find!(project_path) target_project = Project.find_by_full_path(target_project_path) - frozen_outbound = project.frozen_outbound_job_token_scopes? - if direction == :outbound && frozen_outbound + if direction == :outbound raise Gitlab::Graphql::Errors::ArgumentError, 'direction: OUTBOUND scope entries can only be removed. ' \ 'Only INBOUND scope can be expanded.' end - direction ||= frozen_outbound ? :inbound : :outbound + direction ||= :inbound result = ::Ci::JobTokenScope::AddProjectService .new(project, current_user) diff --git a/app/graphql/mutations/ci/pipeline_schedule/create.rb b/app/graphql/mutations/ci/pipeline_schedule/create.rb index 65b355cd80f..71a366ed342 100644 --- a/app/graphql/mutations/ci/pipeline_schedule/create.rb +++ b/app/graphql/mutations/ci/pipeline_schedule/create.rb @@ -51,14 +51,28 @@ module Mutations params = pipeline_schedule_attrs.merge(variables_attributes: variables.map(&:to_h)) - schedule = ::Ci::CreatePipelineScheduleService - .new(project, current_user, params) - .execute - - unless schedule.persisted? - return { - pipeline_schedule: nil, errors: schedule.errors.full_messages - } + if ::Feature.enabled?(:ci_refactoring_pipeline_schedule_create_service, project) + response = ::Ci::PipelineSchedules::CreateService + .new(project, current_user, params) + .execute + + schedule = response.payload + + unless response.success? + return { + pipeline_schedule: nil, errors: response.errors + } + end + else + schedule = ::Ci::CreatePipelineScheduleService + .new(project, current_user, params) + .execute + + unless schedule.persisted? + return { + pipeline_schedule: nil, errors: schedule.errors.full_messages + } + end end { diff --git a/app/graphql/mutations/ci/pipeline_schedule/update.rb b/app/graphql/mutations/ci/pipeline_schedule/update.rb index a0b5e793ecb..aff0a5494e7 100644 --- a/app/graphql/mutations/ci/pipeline_schedule/update.rb +++ b/app/graphql/mutations/ci/pipeline_schedule/update.rb @@ -43,7 +43,7 @@ module Mutations def resolve(id:, variables: [], **pipeline_schedule_attrs) schedule = authorized_find!(id: id) - params = pipeline_schedule_attrs.merge(variables_attributes: variables.map(&:to_h)) + params = pipeline_schedule_attrs.merge(variables_attributes: variable_attributes_for(variables)) service_response = ::Ci::PipelineSchedules::UpdateService .new(schedule, current_user, params) @@ -54,6 +54,18 @@ module Mutations errors: service_response.errors } end + + private + + def variable_attributes_for(variables) + variables.map do |variable| + variable.to_h.tap do |hash| + hash[:id] = GlobalID::Locator.locate(hash[:id]).id if hash[:id] + + hash[:_destroy] = hash.delete(:destroy) + end + end + end end end end diff --git a/app/graphql/mutations/ci/pipeline_schedule/variable_input_type.rb b/app/graphql/mutations/ci/pipeline_schedule/variable_input_type.rb index 54a6ad92448..eb6a78eb67a 100644 --- a/app/graphql/mutations/ci/pipeline_schedule/variable_input_type.rb +++ b/app/graphql/mutations/ci/pipeline_schedule/variable_input_type.rb @@ -8,11 +8,18 @@ module Mutations description 'Attributes for the pipeline schedule variable.' + PipelineScheduleVariableID = ::Types::GlobalIDType[::Ci::PipelineScheduleVariable] + + argument :id, PipelineScheduleVariableID, required: false, description: 'ID of the variable to mutate.' + argument :key, GraphQL::Types::String, required: true, description: 'Name of the variable.' argument :value, GraphQL::Types::String, required: true, description: 'Value of the variable.' argument :variable_type, Types::Ci::VariableTypeEnum, required: true, description: 'Type of the variable.' + + argument :destroy, GraphQL::Types::Boolean, required: false, + description: 'Boolean option to destroy the variable.' 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 d4e55fd1792..082c345adf6 100644 --- a/app/graphql/mutations/ci/project_ci_cd_settings_update.rb +++ b/app/graphql/mutations/ci/project_ci_cd_settings_update.rb @@ -39,7 +39,7 @@ module Mutations def resolve(full_path:, **args) project = authorized_find!(full_path) - if args[:job_token_scope_enabled] && project.frozen_outbound_job_token_scopes? + if args[:job_token_scope_enabled] raise Gitlab::Graphql::Errors::ArgumentError, 'job_token_scope_enabled can only be set to false' end diff --git a/app/graphql/mutations/ci/runner/create.rb b/app/graphql/mutations/ci/runner/create.rb index 7eca6c27d10..4d4134781a5 100644 --- a/app/graphql/mutations/ci/runner/create.rb +++ b/app/graphql/mutations/ci/runner/create.rb @@ -37,8 +37,6 @@ module Mutations parse_gid(**args) - check_feature_flag(**args) - super end @@ -79,28 +77,6 @@ module Mutations GitlabSchema.parse_gid(args[:project_id], expected_type: ::Project) end end - - def check_feature_flag(**args) - case args[:runner_type] - when 'instance_type' - if Feature.disabled?(:create_runner_workflow_for_admin, current_user) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, - '`create_runner_workflow_for_admin` feature flag is disabled.' - end - when 'group_type' - namespace = find_object(**args).sync - if Feature.disabled?(:create_runner_workflow_for_namespace, namespace) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, - '`create_runner_workflow_for_namespace` feature flag is disabled.' - end - when 'project_type' - project = find_object(**args).sync - if project && Feature.disabled?(:create_runner_workflow_for_namespace, project.namespace) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, - '`create_runner_workflow_for_namespace` feature flag is disabled.' - end - end - end end end end diff --git a/app/graphql/mutations/environments/create.rb b/app/graphql/mutations/environments/create.rb index 271585eb06c..f18ce0eba97 100644 --- a/app/graphql/mutations/environments/create.rb +++ b/app/graphql/mutations/environments/create.rb @@ -35,6 +35,11 @@ module Mutations required: false, description: 'Cluster agent of the environment.' + argument :kubernetes_namespace, + GraphQL::Types::String, + required: false, + description: 'Kubernetes namespace of the environment.' + field :environment, Types::EnvironmentType, null: true, diff --git a/app/graphql/mutations/environments/update.rb b/app/graphql/mutations/environments/update.rb index 431a7add00e..07ab22685cc 100644 --- a/app/graphql/mutations/environments/update.rb +++ b/app/graphql/mutations/environments/update.rb @@ -28,6 +28,11 @@ module Mutations required: false, description: 'Cluster agent of the environment.' + argument :kubernetes_namespace, + GraphQL::Types::String, + required: false, + description: 'Kubernetes namespace of the environment.' + field :environment, Types::EnvironmentType, null: true, diff --git a/app/graphql/resolvers/alert_management/http_integrations_resolver.rb b/app/graphql/resolvers/alert_management/http_integrations_resolver.rb index 225e20bab83..ac04e0967e6 100644 --- a/app/graphql/resolvers/alert_management/http_integrations_resolver.rb +++ b/app/graphql/resolvers/alert_management/http_integrations_resolver.rb @@ -35,7 +35,7 @@ module Resolvers end def http_integrations - ::AlertManagement::HttpIntegrationsFinder.new(project, {}).execute + ::AlertManagement::HttpIntegrationsFinder.new(project, { type_identifier: :http }).execute end end end diff --git a/app/graphql/resolvers/alert_management/integrations_resolver.rb b/app/graphql/resolvers/alert_management/integrations_resolver.rb index a97650e95d9..9b20d3367f1 100644 --- a/app/graphql/resolvers/alert_management/integrations_resolver.rb +++ b/app/graphql/resolvers/alert_management/integrations_resolver.rb @@ -40,7 +40,7 @@ module Resolvers def http_integrations return [] unless http_integrations_allowed? - ::AlertManagement::HttpIntegrationsFinder.new(project, {}).execute + ::AlertManagement::HttpIntegrationsFinder.new(project, { type_identifier: :http }).execute end def prometheus_integrations_allowed? diff --git a/app/graphql/resolvers/ci/inherited_variables_resolver.rb b/app/graphql/resolvers/ci/inherited_variables_resolver.rb index 01f966942a4..4e83265e247 100644 --- a/app/graphql/resolvers/ci/inherited_variables_resolver.rb +++ b/app/graphql/resolvers/ci/inherited_variables_resolver.rb @@ -5,8 +5,12 @@ module Resolvers class InheritedVariablesResolver < BaseResolver type Types::Ci::ProjectVariableType.connection_type, null: true - def resolve - object.group&.self_and_ancestors&.flat_map(&:variables) || [] + argument :sort, Types::Ci::GroupVariablesSortEnum, + required: false, default_value: :created_desc, + description: 'Sort variables by the criteria.' + + def resolve(sort:) + ::Ci::GroupVariablesFinder.new(object, sort).execute end end end diff --git a/app/graphql/resolvers/ci/runner_job_count_resolver.rb b/app/graphql/resolvers/ci/runner_job_count_resolver.rb new file mode 100644 index 00000000000..a43d3f3a100 --- /dev/null +++ b/app/graphql/resolvers/ci/runner_job_count_resolver.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + class RunnerJobCountResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type GraphQL::Types::Int, null: true + + authorize :read_runner + authorizes_object! + + argument :statuses, [::Types::Ci::JobStatusEnum], + required: false, + description: 'Filter jobs by status.', + alpha: { milestone: '16.2' } + + alias_method :runner, :object + + def resolve(statuses: nil) + BatchLoader::GraphQL.for(runner.id).batch(key: [:job_count, statuses]) do |runner_ids, loader, _args| + counts_by_runner = calculate_job_count_per_runner(runner_ids, statuses) + + runner_ids.each do |runner_id| + loader.call(runner_id, counts_by_runner[runner_id]&.count || 0) + end + end + end + + private + + def calculate_job_count_per_runner(runner_ids, statuses) + # rubocop: disable CodeReuse/ActiveRecord + 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'])) + lateral_query = lateral_query.where(status: statuses) if statuses + # We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT + lateral_query = lateral_query.limit(::Types::Ci::RunnerType::JOB_COUNT_LIMIT + 1) + ::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 + end + end + end +end diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb index 735e38c1a5c..632655d3681 100644 --- a/app/graphql/resolvers/ci/runners_resolver.rb +++ b/app/graphql/resolvers/ci/runners_resolver.rb @@ -4,6 +4,7 @@ module Resolvers module Ci class RunnersResolver < BaseResolver include LooksAhead + include Gitlab::Graphql::Authorize::AuthorizeResource type Types::Ci::RunnerType.connection_type, null: true @@ -105,3 +106,5 @@ module Resolvers end end end + +Resolvers::Ci::RunnersResolver.prepend_mod diff --git a/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb b/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb index 2ea7a02bf15..d9bcf39b818 100644 --- a/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb +++ b/app/graphql/resolvers/concerns/issues/look_ahead_preloads.rb @@ -20,17 +20,15 @@ module Issues end def preloads - preload_hash = { + { alert_management_alert: [:alert_management_alert], assignees: [:assignees], participants: Issue.participant_includes, timelogs: [:timelogs], customer_relations_contacts: { customer_relations_contacts: [:group] }, - escalation_status: [:incident_management_issuable_escalation_status] + escalation_status: [:incident_management_issuable_escalation_status], + type: :work_item_type } - preload_hash[:type] = :work_item_type if Feature.enabled?(:issue_type_uses_work_item_types_table) - - preload_hash end end end diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb index b9326015ac0..c0a068097a7 100644 --- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb +++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb @@ -11,6 +11,11 @@ module ResolvesMergeRequests end def resolve_with_lookahead(**args) + if args[:group_id] + args[:group_id] = ::GitlabSchema.parse_gid(args[:group_id], expected_type: ::Group).model_id + args[:include_subgroups] = true + end + mr_finder = MergeRequestsFinder.new(current_user, args.compact) finder = Gitlab::Graphql::Loaders::IssuableLoader.new(mr_parent, mr_finder) diff --git a/app/graphql/resolvers/issues/base_resolver.rb b/app/graphql/resolvers/issues/base_resolver.rb index fefd17d5e20..495b72231fc 100644 --- a/app/graphql/resolvers/issues/base_resolver.rb +++ b/app/graphql/resolvers/issues/base_resolver.rb @@ -16,6 +16,9 @@ module Resolvers argument :assignee_usernames, [GraphQL::Types::String], required: false, description: 'Usernames of users assigned to the issue.' + argument :assignee_wildcard_id, ::Types::AssigneeWildcardIdEnum, + required: false, + description: 'Filter by assignee wildcard. Incompatible with assigneeUsername and assigneeUsernames.' argument :author_username, GraphQL::Types::String, required: false, description: 'Username of the author of the issue.' @@ -148,6 +151,7 @@ module Resolvers rewrite_param_name(args, :assignee_usernames, :assignee_username) rewrite_param_name(args[:or], :assignee_usernames, :assignee_username) rewrite_param_name(args[:not], :assignee_usernames, :assignee_username) + rewrite_param_name(args, :assignee_wildcard_id, :assignee_id) end def rewrite_param_name(params, old_name, new_name) @@ -163,7 +167,7 @@ module Resolvers end def mutually_exclusive_assignee_username_args - [:assignee_usernames, :assignee_username] + [:assignee_usernames, :assignee_username, :assignee_wildcard_id] end def params_not_mutually_exclusive(args, mutually_exclusive_args) @@ -171,7 +175,7 @@ module Resolvers arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(', ') raise ::Gitlab::Graphql::Errors::ArgumentError, - "only one of [#{arg_str}] arguments is allowed at the same time." + "only one of [#{arg_str}] arguments is allowed at the same time." end end # rubocop:enable Graphql/ResolverType diff --git a/app/graphql/resolvers/metrics/dashboard_resolver.rb b/app/graphql/resolvers/metrics/dashboard_resolver.rb deleted file mode 100644 index 5abad0de539..00000000000 --- a/app/graphql/resolvers/metrics/dashboard_resolver.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - module Metrics - class DashboardResolver < Resolvers::BaseResolver - type Types::Metrics::DashboardType, null: true - calls_gitaly! - - argument :path, GraphQL::Types::String, - required: true, - description: <<~MD - Path to a file which defines a metrics dashboard eg: `"config/prometheus/common_metrics.yml"`. - MD - - alias_method :environment, :object - - def resolve(path:) - return if Feature.enabled?(:remove_monitor_metrics) - return unless environment - - ::PerformanceMonitoring::PrometheusDashboard.find_for(path: path, **service_params) - end - - private - - def service_params - { - project: environment.project, - user: current_user, - options: { environment: environment } - } - end - end - end -end diff --git a/app/graphql/resolvers/user_merge_requests_resolver_base.rb b/app/graphql/resolvers/user_merge_requests_resolver_base.rb index b2d85307c49..72dbc0a93e9 100644 --- a/app/graphql/resolvers/user_merge_requests_resolver_base.rb +++ b/app/graphql/resolvers/user_merge_requests_resolver_base.rb @@ -4,6 +4,14 @@ module Resolvers class UserMergeRequestsResolverBase < MergeRequestsResolver include ResolvesProject + argument :group_id, + type: ::Types::GlobalIDType[::Group], + required: false, + description: <<~DESC + The global ID of the group the authored merge requests should be in. + Merge requests in subgroups are included. + DESC + argument :project_path, type: GraphQL::Types::String, required: false, diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb index 36dd930c3d9..c17406b3e56 100644 --- a/app/graphql/types/alert_management/alert_type.rb +++ b/app/graphql/types/alert_management/alert_type.rb @@ -111,13 +111,6 @@ module Types null: true, description: 'Assignees of the alert.' - field :metrics_dashboard_url, - GraphQL::Types::String, - null: true, - description: 'URL for metrics embed for the alert.', - deprecated: { reason: 'Returns no data. Underlying feature was removed in 16.0', - milestone: '16.0' } - field :runbook, GraphQL::Types::String, null: true, @@ -143,12 +136,6 @@ module Types method: :details_url, null: false, description: 'URL of the alert.' - - def metrics_dashboard_url - return if Feature.enabled?(:remove_monitor_metrics) - - object.metrics_dashboard_url - end end end end diff --git a/app/graphql/types/assignee_wildcard_id_enum.rb b/app/graphql/types/assignee_wildcard_id_enum.rb new file mode 100644 index 00000000000..09afab7de37 --- /dev/null +++ b/app/graphql/types/assignee_wildcard_id_enum.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + class AssigneeWildcardIdEnum < BaseEnum + graphql_name 'AssigneeWildcardId' + description 'Assignee ID wildcard values' + + value 'NONE', 'No assignee is assigned.' + value 'ANY', 'An assignee is assigned.' + end +end diff --git a/app/graphql/types/boards/assignee_wildcard_id_enum.rb b/app/graphql/types/boards/assignee_wildcard_id_enum.rb deleted file mode 100644 index ba9058a78d9..00000000000 --- a/app/graphql/types/boards/assignee_wildcard_id_enum.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module Types - module Boards - class AssigneeWildcardIdEnum < BaseEnum - graphql_name 'AssigneeWildcardId' - description 'Assignee ID wildcard values' - - value 'NONE', 'No assignee is assigned.' - value 'ANY', 'An assignee is assigned.' - end - end -end diff --git a/app/graphql/types/boards/board_issue_input_type.rb b/app/graphql/types/boards/board_issue_input_type.rb index 897e3d05948..ea7c207cda2 100644 --- a/app/graphql/types/boards/board_issue_input_type.rb +++ b/app/graphql/types/boards/board_issue_input_type.rb @@ -17,9 +17,9 @@ module Types required: false, description: 'Search query for issue title or description.' - argument :assignee_wildcard_id, ::Types::Boards::AssigneeWildcardIdEnum, + argument :assignee_wildcard_id, ::Types::AssigneeWildcardIdEnum, required: false, - description: 'Filter by assignee wildcard. Incompatible with assigneeUsername.' + description: 'Filter by assignee wildcard. Incompatible with assigneeUsername and assigneeUsernames.' argument :confidential, GraphQL::Types::Boolean, required: false, diff --git a/app/graphql/types/ci/config/include_type.rb b/app/graphql/types/ci/config/include_type.rb index 71eb8f755ab..b5816453a51 100644 --- a/app/graphql/types/ci/config/include_type.rb +++ b/app/graphql/types/ci/config/include_type.rb @@ -15,22 +15,22 @@ module Types field :location, GraphQL::Types::String, null: true, - description: 'File location. It can be masked if it contains masked variables, e.g., ' \ - '".gitlab/ci/build-images.gitlab-ci.yml".' + description: 'File location. It can be masked if it contains masked variables. For example, ' \ + '`".gitlab/ci/build-images.gitlab-ci.yml"`.' field :blob, GraphQL::Types::String, null: true, - description: 'File blob location. It can be masked if it contains masked variables, e.g., ' \ - '"https://gitlab.com/gitlab-org/gitlab/-/blob/e52d6d0246d7375291850e61f0abc101fbda9dc2' \ - '/.gitlab/ci/build-images.gitlab-ci.yml".' + description: 'File blob location. It can be masked if it contains masked variables. For example, ' \ + '`"https://gitlab.com/gitlab-org/gitlab/-/blob/e52d6d0246d7375291850e61f0abc101fbda9dc2' \ + '/.gitlab/ci/build-images.gitlab-ci.yml"`.' field :raw, GraphQL::Types::String, null: true, - description: 'File raw location. It can be masked if it contains masked variables, e.g., ' \ - '"https://gitlab.com/gitlab-org/gitlab/-/raw/e52d6d0246d7375291850e61f0abc101fbda9dc2' \ - '/.gitlab/ci/build-images.gitlab-ci.yml".' + description: 'File raw location. It can be masked if it contains masked variables. For example, ' \ + '`"https://gitlab.com/gitlab-org/gitlab/-/raw/e52d6d0246d7375291850e61f0abc101fbda9dc2' \ + '/.gitlab/ci/build-images.gitlab-ci.yml"`.' field :extra, # rubocop:disable Graphql/JSONType GraphQL::Types::JSON, diff --git a/app/graphql/types/ci/group_variable_type.rb b/app/graphql/types/ci/group_variable_type.rb index f9ed54f0d10..7e2afba0d53 100644 --- a/app/graphql/types/ci/group_variable_type.rb +++ b/app/graphql/types/ci/group_variable_type.rb @@ -21,6 +21,10 @@ module Types field :protected, GraphQL::Types::Boolean, null: true, description: 'Indicates whether the variable is protected.' + + field :description, GraphQL::Types::String, + null: true, + description: 'Description of the variable.' end end end diff --git a/app/graphql/types/ci/group_variables_sort_enum.rb b/app/graphql/types/ci/group_variables_sort_enum.rb new file mode 100644 index 00000000000..5cf9fd4039b --- /dev/null +++ b/app/graphql/types/ci/group_variables_sort_enum.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Types + module Ci + # Not inheriting from Types::SortEnum since we only want + # to implement a subset of the sort values it defines. + class GroupVariablesSortEnum < BaseEnum + graphql_name 'CiGroupVariablesSort' + description 'Values for sorting inherited variables' + + # Borrowed from Types::SortEnum + # These values/descriptions should stay in-sync as much as possible. + value 'CREATED_DESC', 'Created at descending order.', value: :created_desc + value 'CREATED_ASC', 'Created at ascending order.', value: :created_asc + + value 'KEY_DESC', 'Key by descending order.', value: :key_desc + value 'KEY_ASC', 'Key by ascending order.', value: :key_asc + end + end +end diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index a779ceb2e2a..02b10f3e4bd 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -87,8 +87,10 @@ module Types description: 'Play path of the job.' field :playable, GraphQL::Types::Boolean, null: false, method: :playable?, description: 'Indicates the job can be played.' - field :previous_stage_jobs_or_needs, Types::Ci::JobNeedUnion.connection_type, null: true, - description: 'Jobs that must complete before the job runs. Returns `BuildNeed`, which is the needed jobs if the job uses the `needs` keyword, or the previous stage jobs otherwise.' + field :previous_stage_jobs_or_needs, Types::Ci::JobNeedUnion.connection_type, + null: true, + description: 'Jobs that must complete before the job runs. Returns `BuildNeed`, ' \ + 'which is the needed jobs if the job uses the `needs` keyword, or the previous stage jobs otherwise.' field :ref_name, GraphQL::Types::String, null: true, description: 'Ref name of the job.' field :ref_path, GraphQL::Types::String, null: true, @@ -179,7 +181,9 @@ module Types stages = pipeline.stages.by_position(positions) stages.each do |stage| - loader.call([pipeline, stage.position], stage.latest_statuses) + # Without `.to_a`, the memoization will only preserve the activerecord relation object. And when there is + # a call, the SQL query will be executed again. + loader.call([pipeline, stage.position], stage.latest_statuses.to_a) end end end diff --git a/app/graphql/types/ci/project_variable_type.rb b/app/graphql/types/ci/project_variable_type.rb index 2a5375045e5..a9679000511 100644 --- a/app/graphql/types/ci/project_variable_type.rb +++ b/app/graphql/types/ci/project_variable_type.rb @@ -21,6 +21,10 @@ module Types field :masked, GraphQL::Types::Boolean, null: true, description: 'Indicates whether the variable is masked.' + + field :description, GraphQL::Types::String, + null: true, + description: 'Description of the variable.' end end end diff --git a/app/graphql/types/ci/runner_sort_enum.rb b/app/graphql/types/ci/runner_sort_enum.rb index 8f2a13bd699..4195eb043ed 100644 --- a/app/graphql/types/ci/runner_sort_enum.rb +++ b/app/graphql/types/ci/runner_sort_enum.rb @@ -15,3 +15,5 @@ module Types end end end + +Types::Ci::RunnerSortEnum.prepend_mod diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb index 8e509cc8493..2baf64ca663 100644 --- a/app/graphql/types/ci/runner_type.rb +++ b/app/graphql/types/ci/runner_type.rb @@ -24,8 +24,9 @@ module Types 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 + deprecated: { reason: "Use field in `manager` object instead", milestone: '16.2' }, + 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 @@ -46,17 +47,20 @@ module Types description: 'URL of the registration page of the runner manager. Only available for the creator of the runner for a limited time during registration.', alpha: { milestone: '15.11' } field :executor_name, GraphQL::Types::String, null: true, - description: 'Executor last advertised by the runner.', - method: :executor_name + deprecated: { reason: "Use field in `manager` object instead", milestone: '16.2' }, + description: 'Executor last advertised by the runner.', + method: :executor_name field :groups, 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.' + deprecated: { reason: "Use field in `manager` object instead", milestone: '16.2' }, + 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)." + description: "Number of jobs processed by the runner (limited to #{JOB_COUNT_LIMIT}, plus one to indicate that more items exist).", + resolver: ::Resolvers::Ci::RunnerJobCountResolver field :job_execution_status, Types::Ci::RunnerJobExecutionStatusEnum, null: true, @@ -82,8 +86,9 @@ module Types 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 + deprecated: { reason: "Use field in `manager` object instead", milestone: '16.2' }, + 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, @@ -94,7 +99,8 @@ module Types field :register_admin_url, GraphQL::Types::String, null: true, description: 'URL of the temporary registration page of the runner. Only available before the runner is registered. Only available for administrators.' field :revision, GraphQL::Types::String, null: true, - description: 'Revision of the runner.' + deprecated: { reason: "Use field in `manager` object instead", milestone: '16.2' }, + description: 'Revision of the runner.' field :run_untagged, GraphQL::Types::Boolean, null: false, description: 'Indicates the runner is able to run untagged jobs.' field :runner_type, ::Types::Ci::RunnerTypeEnum, null: false, @@ -112,7 +118,8 @@ module Types description: 'Runner token expiration time.', method: :token_expires_at field :version, GraphQL::Types::String, null: true, - description: 'Version of the runner.' + deprecated: { reason: "Use field in `manager` object instead", milestone: '16.2' }, + description: 'Version of the runner.' markdown_field :maintenance_note_html, null: true @@ -120,28 +127,6 @@ module Types ::MarkupHelper.markdown(object.maintenance_note, context.to_h.dup) end - def job_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 Gitlab::Routing.url_helpers.admin_runner_url(runner) if can_admin_runners? end diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb index c0f3d1db57b..a9d8075329d 100644 --- a/app/graphql/types/ci/stage_type.rb +++ b/app/graphql/types/ci/stage_type.rb @@ -33,7 +33,7 @@ module Types by_pipeline = keys.group_by(&:pipeline) include_needs = keys.any? do |k| k.requires?(%i[nodes jobs nodes needs]) || - k.requires?(%i[nodes jobs nodes previousStageJobsAndNeeds]) + k.requires?(%i[nodes jobs nodes previousStageJobsOrNeeds]) end by_pipeline.each do |pl, key_group| diff --git a/app/graphql/types/current_user_todos.rb b/app/graphql/types/current_user_todos.rb index 4c4cb516979..d5441ea1d15 100644 --- a/app/graphql/types/current_user_todos.rb +++ b/app/graphql/types/current_user_todos.rb @@ -17,7 +17,8 @@ module Types def current_user_todos(state: nil) state ||= %i[done pending] # TodosFinder treats a `nil` state param as `pending` - key = [state, unpresented.class.name] + target_type_name = unpresented.try(:todoable_target_type_name) || unpresented.class.name + key = [state, target_type_name] BatchLoader::GraphQL.for(unpresented).batch(default_value: [], key: key) do |targets, loader, args| state, klass_name = args[:key] diff --git a/app/graphql/types/environment_type.rb b/app/graphql/types/environment_type.rb index 936ad52200c..aee09e5a143 100644 --- a/app/graphql/types/environment_type.rb +++ b/app/graphql/types/environment_type.rb @@ -33,6 +33,9 @@ module Types field :external_url, GraphQL::Types::String, null: true, description: 'External URL of the environment.' + field :kubernetes_namespace, GraphQL::Types::String, null: true, + description: 'Kubernetes namespace of the environment.' + field :created_at, Types::TimeType, description: 'When the environment was created.' @@ -51,11 +54,6 @@ module Types field :environment_type, GraphQL::Types::String, description: 'Folder name of the environment.' - field :metrics_dashboard, Types::Metrics::DashboardType, null: true, - description: 'Metrics dashboard schema for the environment.', - resolver: Resolvers::Metrics::DashboardResolver, - deprecated: { reason: 'Returns no data. Underlying feature was removed in 16.0', milestone: '16.0' } - field :latest_opened_most_severe_alert, Types::AlertManagement::AlertType, null: true, diff --git a/app/graphql/types/ide_type.rb b/app/graphql/types/ide_type.rb new file mode 100644 index 00000000000..34447577f23 --- /dev/null +++ b/app/graphql/types/ide_type.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Types + class IdeType < BaseObject + graphql_name 'Ide' + description 'IDE settings and feature flags.' + + authorize :read_user + + field :code_suggestions_enabled, GraphQL::Types::Boolean, null: false, + description: 'Indicates whether AI assisted code suggestions are enabled.' + + def code_suggestions_enabled + object.can?(:access_code_suggestions) + end + end +end diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index f32dfc0dbcf..99c719f1402 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -35,7 +35,7 @@ module Types field :iid, GraphQL::Types::String, null: false, description: 'Internal ID of the merge request.' field :merge_when_pipeline_succeeds, GraphQL::Types::Boolean, null: true, - description: 'Indicates if the merge has been set to be merged when its pipeline succeeds (MWPS).' + description: 'Indicates if the merge has been set to auto-merge.' field :merged_at, Types::TimeType, null: true, complexity: 5, description: 'Timestamp of when the merge request was merged, null if not merged.' field :project, Types::ProjectType, null: false, @@ -207,7 +207,7 @@ module Types 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, - description: 'User who merged this merge request or set it to merge when pipeline succeeds.' + description: 'User who merged this merge request or set it to auto-merge.' field :mergeable, GraphQL::Types::Boolean, null: false, method: :mergeable?, calls_gitaly: true, description: 'Indicates if the merge request is mergeable.' field :security_auto_fix, GraphQL::Types::Boolean, null: true, diff --git a/app/graphql/types/metrics/dashboard_type.rb b/app/graphql/types/metrics/dashboard_type.rb deleted file mode 100644 index 5570b904d79..00000000000 --- a/app/graphql/types/metrics/dashboard_type.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module Types - module Metrics - # rubocop: disable Graphql/AuthorizeTypes - # Authorization is performed at environment level - class DashboardType < ::Types::BaseObject - graphql_name 'MetricsDashboard' - - field :path, GraphQL::Types::String, null: true, - description: 'Path to a file with the dashboard definition.' - - field :schema_validation_warnings, - [GraphQL::Types::String], - null: true, - description: 'Dashboard schema validation warnings.' - - field :annotations, - Types::Metrics::Dashboards::AnnotationType.connection_type, - null: true, - description: 'Annotations added to the dashboard.', - resolver: Resolvers::Metrics::Dashboards::AnnotationResolver - - # In order to maintain backward compatibility we need to return NULL when there are no warnings - # and dashboard validation returns an empty array when there are no issues. - def schema_validation_warnings - warnings = object.schema_validation_warnings - warnings unless warnings.empty? - end - end - # rubocop: enable Graphql/AuthorizeTypes - end -end diff --git a/app/graphql/types/project_statistics_type.rb b/app/graphql/types/project_statistics_type.rb index a1d721856a9..ef4edcddbe9 100644 --- a/app/graphql/types/project_statistics_type.rb +++ b/app/graphql/types/project_statistics_type.rb @@ -35,3 +35,5 @@ module Types description: 'Wiki size of the project in bytes.' end end + +Types::ProjectStatisticsType.prepend_mod_with('Types::ProjectStatisticsType') diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index f8a516501c3..992663b4d98 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -727,6 +727,8 @@ module Types if minimum_access_level.nil? object.forks.public_or_visible_to_user(current_user) else + return [] if current_user.nil? + object.forks.visible_to_user_and_access_level(current_user, minimum_access_level) end end diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb index 67ee0589882..dbed51ac71a 100644 --- a/app/graphql/types/root_storage_statistics_type.rb +++ b/app/graphql/types/root_storage_statistics_type.rb @@ -8,11 +8,19 @@ module Types 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 :container_registry_size_is_estimated, GraphQL::Types::Boolean, method: :registry_size_estimated, null: false, + description: 'Indicates whether the deduplicated Container Registry size for ' \ + 'the namespace is an estimated value or not.' 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.' - field :pipeline_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI pipeline artifacts size in bytes.' - field :registry_size_estimated, GraphQL::Types::Boolean, null: false, description: 'Indicates whether the deduplicated Container Registry size for the namespace is an estimated value or not.' + field :pipeline_artifacts_size, GraphQL::Types::Float, null: false, + description: 'CI pipeline artifacts size in bytes.' + field :registry_size_estimated, GraphQL::Types::Boolean, + null: false, + deprecated: { reason: 'Use `container_registry_size_is_estimated`', milestone: '16.2' }, + description: 'Indicates whether the deduplicated Container Registry size for ' \ + 'the namespace is an estimated value or not.' field :repository_size, GraphQL::Types::Float, null: false, description: 'Git repository size in bytes.' field :snippets_size, GraphQL::Types::Float, null: false, description: 'Snippets size in bytes.' field :storage_size, GraphQL::Types::Float, null: false, description: 'Total storage in bytes.' @@ -20,3 +28,5 @@ module Types field :wiki_size, GraphQL::Types::Float, null: false, description: 'Wiki size in bytes.' end end + +Types::RootStorageStatisticsType.prepend_mod_with('Types::RootStorageStatisticsType') diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb index 5357f2f8e66..9e5f6810aca 100644 --- a/app/graphql/types/user_interface.rb +++ b/app/graphql/types/user_interface.rb @@ -197,6 +197,17 @@ module Types null: true, description: 'Timestamp of when the user was created.' + field :pronouns, + type: ::GraphQL::Types::String, + null: true, + description: 'Pronouns of the user.' + + field :ide, + type: Types::IdeType, + null: true, + description: 'IDE settings.', + method: :itself + definition_methods do def resolve_type(object, context) # in the absence of other information, we cannot tell - just default to |