diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-20 18:40:28 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-20 18:40:28 +0300 |
commit | b595cb0c1dec83de5bdee18284abe86614bed33b (patch) | |
tree | 8c3d4540f193c5ff98019352f554e921b3a41a72 /app/graphql | |
parent | 2f9104a328fc8a4bddeaa4627b595166d24671d0 (diff) |
Add latest changes from gitlab-org/gitlab@15-2-stable-eev15.2.0-rc42
Diffstat (limited to 'app/graphql')
40 files changed, 466 insertions, 109 deletions
diff --git a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb index 6a91a097a17..cbe1cfb4099 100644 --- a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb +++ b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb @@ -15,6 +15,15 @@ module Mutations argument :title, GraphQL::Types::String, required: false, description: copy_field_description(Types::WorkItemType, :title) + argument :description_widget, ::Types::WorkItems::Widgets::DescriptionInputType, + required: false, + description: 'Input for description widget.' + argument :weight_widget, ::Types::WorkItems::Widgets::WeightInputType, + required: false, + description: 'Input for weight widget.' + argument :hierarchy_widget, ::Types::WorkItems::Widgets::HierarchyUpdateInputType, + required: false, + description: 'Input for hierarchy widget.' end end end diff --git a/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb b/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb new file mode 100644 index 00000000000..445b2eb6441 --- /dev/null +++ b/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Mutations + module WorkItems + module Widgetable + extend ActiveSupport::Concern + + 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_params = attributes.extract!(*widget_keys) + + not_supported_keys = widget_params.keys - work_item_type.widgets.map(&:api_symbol) + if not_supported_keys.present? + raise Gitlab::Graphql::Errors::ArgumentError, + "Following widget keys are not supported by #{work_item_type.name} type: #{not_supported_keys}" + end + + # Cannot use prepare to use `.to_h` on each input due to + # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87472#note_945199865 + widget_params.transform_values { |values| values.to_h } + end + end + end +end diff --git a/app/graphql/mutations/notes/create/diff_note.rb b/app/graphql/mutations/notes/create/diff_note.rb index 019e7cb8623..7b8c06fd104 100644 --- a/app/graphql/mutations/notes/create/diff_note.rb +++ b/app/graphql/mutations/notes/create/diff_note.rb @@ -32,7 +32,8 @@ module Mutations def create_note_params(noteable, args) super(noteable, args).merge({ type: 'DiffNote', - position: position(noteable, args) + position: position(noteable, args), + merge_request_diff_head_sha: args[:position][:head_sha] }) end diff --git a/app/graphql/mutations/pages/base.rb b/app/graphql/mutations/pages/base.rb new file mode 100644 index 00000000000..5eb8ecdf0ba --- /dev/null +++ b/app/graphql/mutations/pages/base.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mutations + module Pages + class Base < BaseMutation + include FindsProject + + argument :project_path, GraphQL::Types::ID, + required: true, + description: 'Full path of the project.' + end + end +end diff --git a/app/graphql/mutations/pages/mark_onboarding_complete.rb b/app/graphql/mutations/pages/mark_onboarding_complete.rb new file mode 100644 index 00000000000..2f5ce5db54a --- /dev/null +++ b/app/graphql/mutations/pages/mark_onboarding_complete.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Mutations + module Pages + class MarkOnboardingComplete < Base + graphql_name 'PagesMarkOnboardingComplete' + + field :onboarding_complete, + Boolean, + null: false, + description: "Indicates the new onboarding_complete state of the project's Pages metadata." + + authorize :admin_project + + def resolve(project_path:) + project = authorized_find!(project_path) + + project.mark_pages_onboarding_complete + + { + onboarding_complete: project.pages_metadatum.onboarding_complete, + errors: errors_on_object(project) + } + end + end + end +end diff --git a/app/graphql/mutations/snippets/create.rb b/app/graphql/mutations/snippets/create.rb index 2921a77b86d..96ac3f8a113 100644 --- a/app/graphql/mutations/snippets/create.rb +++ b/app/graphql/mutations/snippets/create.rb @@ -54,7 +54,7 @@ module Mutations # Only when the user is not an api user and the operation was successful if !api_user? && service_response.success? - ::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user) + ::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user, project: project) end snippet = service_response.payload[:snippet] diff --git a/app/graphql/mutations/snippets/update.rb b/app/graphql/mutations/snippets/update.rb index 2a2941c5328..39843a3714a 100644 --- a/app/graphql/mutations/snippets/update.rb +++ b/app/graphql/mutations/snippets/update.rb @@ -43,7 +43,7 @@ module Mutations # Only when the user is not an api user and the operation was successful if !api_user? && service_response.success? - ::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user) + ::Gitlab::UsageDataCounters::EditorUniqueCounter.track_snippet_editor_edit_action(author: current_user, project: snippet.project) end snippet = service_response.payload[:snippet] diff --git a/app/graphql/mutations/user_callouts/create.rb b/app/graphql/mutations/user_callouts/create.rb index 1be99ea0ecd..7f372053e84 100644 --- a/app/graphql/mutations/user_callouts/create.rb +++ b/app/graphql/mutations/user_callouts/create.rb @@ -15,7 +15,7 @@ module Mutations description: 'User callout dismissed.' def resolve(feature_name:) - callout = Users::DismissCalloutService.new( + callout = ::Users::DismissCalloutService.new( container: nil, current_user: current_user, params: { feature_name: feature_name } ).execute errors = errors_on_object(callout) diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb index 2ae26ed0e1a..350153eaf19 100644 --- a/app/graphql/mutations/work_items/create.rb +++ b/app/graphql/mutations/work_items/create.rb @@ -7,6 +7,7 @@ module Mutations include Mutations::SpamProtection include FindsProject + include Mutations::WorkItems::Widgetable description "Creates a work item. Available only when feature flag `work_items` is enabled." @@ -15,6 +16,9 @@ module Mutations argument :description, GraphQL::Types::String, required: false, description: copy_field_description(Types::WorkItemType, :description) + argument :hierarchy_widget, ::Types::WorkItems::Widgets::HierarchyCreateInputType, + required: false, + description: 'Input for hierarchy widget.' argument :project_path, GraphQL::Types::ID, required: true, description: 'Full path of the project the work item is associated with.' @@ -36,10 +40,18 @@ module Mutations return { errors: ['`work_items` feature flag disabled for this project'] } end + spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) params = global_id_compatibility_params(attributes).merge(author_id: current_user.id) + type = ::WorkItems::Type.find(attributes[:work_item_type_id]) + widget_params = extract_widget_params!(type, params) - spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) - create_result = ::WorkItems::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute + create_result = ::WorkItems::CreateService.new( + project: project, + current_user: current_user, + params: params, + spam_params: spam_params, + widget_params: widget_params + ).execute check_spam_action_response!(create_result[:work_item]) if create_result[:work_item] diff --git a/app/graphql/mutations/work_items/update.rb b/app/graphql/mutations/work_items/update.rb index c495da00f41..5d8c574877a 100644 --- a/app/graphql/mutations/work_items/update.rb +++ b/app/graphql/mutations/work_items/update.rb @@ -9,6 +9,7 @@ module Mutations include Mutations::SpamProtection include Mutations::WorkItems::UpdateArguments + include Mutations::WorkItems::Widgetable authorize :update_work_item @@ -24,19 +25,21 @@ module Mutations end spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) + widget_params = extract_widget_params!(work_item.work_item_type, attributes) - ::WorkItems::UpdateService.new( + update_result = ::WorkItems::UpdateService.new( project: work_item.project, current_user: current_user, params: attributes, + widget_params: widget_params, spam_params: spam_params ).execute(work_item) check_spam_action_response!(work_item) { - work_item: work_item.valid? ? work_item : nil, - errors: errors_on_object(work_item) + work_item: (update_result[:work_item] if update_result[:status] == :success), + errors: Array.wrap(update_result[:message]) } end diff --git a/app/graphql/mutations/work_items/update_widgets.rb b/app/graphql/mutations/work_items/update_widgets.rb index d19da0abaac..7037b7e5a2a 100644 --- a/app/graphql/mutations/work_items/update_widgets.rb +++ b/app/graphql/mutations/work_items/update_widgets.rb @@ -2,6 +2,7 @@ module Mutations module WorkItems + # TODO: Deprecate in favor of using WorkItemUpdate. See https://gitlab.com/gitlab-org/gitlab/-/issues/366300 class UpdateWidgets < BaseMutation graphql_name 'WorkItemUpdateWidgets' description "Updates the attributes of a work item's widgets by global ID." \ diff --git a/app/graphql/queries/container_registry/get_container_repositories.query.graphql b/app/graphql/queries/container_registry/get_container_repositories.query.graphql index 264878ccaa2..5f995eb958b 100644 --- a/app/graphql/queries/container_registry/get_container_repositories.query.graphql +++ b/app/graphql/queries/container_registry/get_container_repositories.query.graphql @@ -32,6 +32,10 @@ query getProjectContainerRepositories( createdAt expirationPolicyStartedAt expirationPolicyCleanupStatus + project { + id + path + } __typename } pageInfo { @@ -67,6 +71,10 @@ query getProjectContainerRepositories( createdAt expirationPolicyStartedAt expirationPolicyCleanupStatus + project { + id + path + } __typename } pageInfo { diff --git a/app/graphql/queries/repository/path_last_commit.query.graphql b/app/graphql/queries/repository/path_last_commit.query.graphql index bcb07ae3182..914be3a72c1 100644 --- a/app/graphql/queries/repository/path_last_commit.query.graphql +++ b/app/graphql/queries/repository/path_last_commit.query.graphql @@ -4,43 +4,46 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) { id repository { __typename - tree(path: $path, ref: $ref) { + paginatedTree(path: $path, ref: $ref) { __typename - lastCommit { + nodes { __typename - id - sha - title - titleHtml - descriptionHtml - message - webPath - authoredDate - authorName - authorGravatar - author { + lastCommit { __typename id - name - avatarUrl + sha + title + titleHtml + descriptionHtml + message webPath - } - signatureHtml - pipelines(ref: $ref, first: 1) { - __typename - edges { + authoredDate + authorName + authorGravatar + author { + __typename + id + name + avatarUrl + webPath + } + signatureHtml + pipelines(ref: $ref, first: 1) { __typename - node { + edges { __typename - id - detailedStatus { + node { __typename id - detailsPath - icon - tooltip - text - group + detailedStatus { + __typename + id + detailsPath + icon + tooltip + text + group + } } } } diff --git a/app/graphql/resolvers/ci/jobs_resolver.rb b/app/graphql/resolvers/ci/jobs_resolver.rb index df138a15538..91f29948ad0 100644 --- a/app/graphql/resolvers/ci/jobs_resolver.rb +++ b/app/graphql/resolvers/ci/jobs_resolver.rb @@ -15,9 +15,15 @@ module Resolvers required: false, description: 'Filter jobs by status.' - def resolve(statuses: nil, security_report_types: []) + argument :retried, ::GraphQL::Types::Boolean, + required: false, + description: 'Filter jobs by retry-status.' + + def resolve(statuses: nil, security_report_types: [], retried: nil) jobs = init_collection(security_report_types) jobs = jobs.with_status(statuses) if statuses.present? + jobs = jobs.retried if retried + jobs = jobs.latest if retried == false jobs end diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb index e221dfea4d0..64738608b60 100644 --- a/app/graphql/resolvers/ci/runners_resolver.rb +++ b/app/graphql/resolvers/ci/runners_resolver.rb @@ -36,6 +36,10 @@ module Resolvers required: false, description: 'Sort order of results.' + argument :upgrade_status, ::Types::Ci::RunnerUpgradeStatusTypeEnum, + required: false, + description: 'Filter by upgrade status.' + def resolve_with_lookahead(**args) apply_lookahead( ::Ci::RunnersFinder @@ -54,6 +58,7 @@ module Resolvers status_status: params[:status]&.to_s, type_type: params[:type], tag_name: params[:tag_list], + upgrade_status: params[:upgrade_status], search: params[:search], sort: params[:sort]&.to_s, preload: { diff --git a/app/graphql/resolvers/ci/test_suite_resolver.rb b/app/graphql/resolvers/ci/test_suite_resolver.rb index 5d61d9e986b..f758e217b47 100644 --- a/app/graphql/resolvers/ci/test_suite_resolver.rb +++ b/app/graphql/resolvers/ci/test_suite_resolver.rb @@ -28,7 +28,7 @@ module Resolvers def load_test_suite_data(builds) suite = builds.sum do |build| - build.collect_test_reports!(Gitlab::Ci::Reports::TestReports.new) + build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) end Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, pipeline.project).load! diff --git a/app/graphql/resolvers/clusters/agents_resolver.rb b/app/graphql/resolvers/clusters/agents_resolver.rb index 28618bef807..0b9eb361dbd 100644 --- a/app/graphql/resolvers/clusters/agents_resolver.rb +++ b/app/graphql/resolvers/clusters/agents_resolver.rb @@ -15,12 +15,10 @@ module Resolvers description: 'Name of the cluster agent.' end - alias_method :project, :object - def resolve_with_lookahead(**args) apply_lookahead( ::Clusters::AgentsFinder - .new(project, current_user, params: args) + .new(object, current_user, params: args) .execute ) end @@ -36,3 +34,5 @@ module Resolvers end end end + +Resolvers::Clusters::AgentsResolver.prepend_mod_with('Resolvers::Clusters::AgentsResolver') diff --git a/app/graphql/resolvers/todo_resolver.rb b/app/graphql/resolvers/todo_resolver.rb index f0be1b6e9a5..0653cd27b4d 100644 --- a/app/graphql/resolvers/todo_resolver.rb +++ b/app/graphql/resolvers/todo_resolver.rb @@ -2,68 +2,16 @@ module Resolvers class TodoResolver < BaseResolver - type Types::TodoType.connection_type, null: true + description 'Retrieve a single to-do item' - alias_method :target, :object + type Types::TodoType, null: true - argument :action, [Types::TodoActionEnum], - required: false, - description: 'Action to be filtered.' + argument :id, Types::GlobalIDType[Todo], + required: true, + description: 'ID of the to-do item.' - argument :author_id, [GraphQL::Types::ID], - required: false, - description: 'ID of an author.' - - argument :project_id, [GraphQL::Types::ID], - required: false, - description: 'ID of a project.' - - argument :group_id, [GraphQL::Types::ID], - required: false, - description: 'ID of a group.' - - argument :state, [Types::TodoStateEnum], - required: false, - description: 'State of the todo.' - - argument :type, [Types::TodoTargetEnum], - required: false, - description: 'Type of the todo.' - - before_connection_authorization do |nodes, current_user| - Preloaders::UserMaxAccessLevelInProjectsPreloader.new( - nodes.map(&:project).compact, - current_user - ).execute - end - - def resolve(**args) - return Todo.none unless current_user.present? && target.present? - return Todo.none if target.is_a?(User) && target != current_user - - TodosFinder.new(current_user, todo_finder_params(args)).execute.with_entity_associations - end - - private - - def todo_finder_params(args) - { - state: args[:state], - type: args[:type], - group_id: args[:group_id], - author_id: args[:author_id], - action_id: args[:action], - project_id: args[:project_id] - }.merge(target_params) - end - - def target_params - return {} unless TodosFinder::TODO_TYPES.include?(target.class.name) - - { - type: target.class.name, - target_id: target.id - } + def resolve(id:) + GitlabSchema.find_by_gid(id) end end end diff --git a/app/graphql/resolvers/todos_resolver.rb b/app/graphql/resolvers/todos_resolver.rb new file mode 100644 index 00000000000..3e8dddb4859 --- /dev/null +++ b/app/graphql/resolvers/todos_resolver.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Resolvers + class TodosResolver < BaseResolver + type Types::TodoType.connection_type, null: true + + alias_method :target, :object + + argument :action, [Types::TodoActionEnum], + required: false, + description: 'Action to be filtered.' + + argument :author_id, [GraphQL::Types::ID], + required: false, + description: 'ID of an author.' + + argument :project_id, [GraphQL::Types::ID], + required: false, + description: 'ID of a project.' + + argument :group_id, [GraphQL::Types::ID], + required: false, + description: 'ID of a group.' + + argument :state, [Types::TodoStateEnum], + required: false, + description: 'State of the todo.' + + argument :type, [Types::TodoTargetEnum], + required: false, + description: 'Type of the todo.' + + before_connection_authorization do |nodes, current_user| + Preloaders::UserMaxAccessLevelInProjectsPreloader.new( + nodes.map(&:project).compact, + current_user + ).execute + end + + def resolve(**args) + return Todo.none unless current_user.present? && target.present? + return Todo.none if target.is_a?(User) && target != current_user + + TodosFinder.new(current_user, todo_finder_params(args)).execute.with_entity_associations + end + + private + + def todo_finder_params(args) + { + state: args[:state], + type: args[:type], + group_id: args[:group_id], + author_id: args[:author_id], + action_id: args[:action], + project_id: args[:project_id] + }.merge(target_params) + end + + def target_params + return {} unless TodosFinder::TODO_TYPES.include?(target.class.name) + + { + type: target.class.name, + target_id: target.id + } + end + end +end diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb index 43b7bbb419f..a0d19229d3d 100644 --- a/app/graphql/types/alert_management/alert_type.rb +++ b/app/graphql/types/alert_management/alert_type.rb @@ -116,7 +116,7 @@ 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::TodoResolver + field :todos, 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/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index b20a671179b..42b55f47f92 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -70,6 +70,8 @@ module Types description: 'Downstream pipeline for a bridge.' field :manual_job, GraphQL::Types::Boolean, null: true, description: 'Whether the job has a manual action.' + field :manual_variables, VariableType.connection_type, null: true, + description: 'Variables added to a manual job when the job is triggered.' 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, @@ -78,6 +80,8 @@ module Types description: 'Ref name of the job.' field :ref_path, GraphQL::Types::String, null: true, description: 'Path to the ref.' + field :retried, GraphQL::Types::Boolean, null: true, + description: 'Indicates that the job has been retried.' field :retryable, GraphQL::Types::Boolean, null: false, method: :retryable?, description: 'Indicates the job can be retried.' field :scheduling_type, GraphQL::Types::String, null: true, @@ -188,6 +192,14 @@ module Types def triggered object.try(:trigger_request) end + + def manual_variables + if object.manual? && object.respond_to?(:job_variables) + object.job_variables + else + [] + end + end end end end diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb index 949e216a982..ac5ffd39407 100644 --- a/app/graphql/types/ci/runner_type.rb +++ b/app/graphql/types/ci/runner_type.rb @@ -159,12 +159,19 @@ module Types owner_ids = runner_owner_ids_by_runner_id.values.flatten.uniq owners = assoc_type.where(id: owner_ids).index_by(&:id) + # Preload projects namespaces to avoid N+1 queries when checking the `read_project` policy for each + preload_projects_namespaces(owners.values) if assoc_type == Project + 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 + + def preload_projects_namespaces(_projects) + # overridden in EE + end end end end diff --git a/app/graphql/types/ci/runner_upgrade_status_type_enum.rb b/app/graphql/types/ci/runner_upgrade_status_type_enum.rb index 02feafe3df9..8e32eee5e6e 100644 --- a/app/graphql/types/ci/runner_upgrade_status_type_enum.rb +++ b/app/graphql/types/ci/runner_upgrade_status_type_enum.rb @@ -5,10 +5,15 @@ module Types class RunnerUpgradeStatusTypeEnum < BaseEnum graphql_name 'CiRunnerUpgradeStatusType' - value 'UNKNOWN', description: 'Upgrade status is unknown.', value: :unknown + ::Ci::RunnerVersion::STATUS_DESCRIPTIONS.each do |status, description| + status_name_src = + if status == :invalid_version + :invalid + else + status + end - Gitlab::Ci::RunnerUpgradeCheck::STATUSES.each do |status, description| - value status.to_s.upcase, description: description, value: status + value status_name_src.to_s.upcase, description: description, value: status end end end diff --git a/app/graphql/types/ci/variable_type.rb b/app/graphql/types/ci/variable_type.rb new file mode 100644 index 00000000000..63f89b6d207 --- /dev/null +++ b/app/graphql/types/ci/variable_type.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Types + module Ci + # rubocop: disable Graphql/AuthorizeTypes + class VariableType < BaseObject + graphql_name 'CiVariable' + + field :id, GraphQL::Types::ID, null: false, + description: 'ID of the variable.' + + field :key, GraphQL::Types::String, null: true, + description: 'Name of the variable.' + + field :value, GraphQL::Types::String, null: true, + description: 'Value of the variable.' + + field :variable_type, ::Types::Ci::VariableTypeEnum, null: true, + description: 'Type of the variable.' + + field :protected, GraphQL::Types::Boolean, null: true, + description: 'Indicates whether the variable is protected.' + + field :masked, GraphQL::Types::Boolean, null: true, + description: 'Indicates whether the variable is masked.' + + field :raw, GraphQL::Types::Boolean, null: true, + description: 'Indicates whether the variable is raw.' + + field :environment_scope, GraphQL::Types::String, null: true, + description: 'Scope defining the environments in which the variable can be used.' + + def environment_scope + if object.respond_to?(:environment_scope) + object.environment_scope + end + end + end + end +end diff --git a/app/graphql/types/ci/variable_type_enum.rb b/app/graphql/types/ci/variable_type_enum.rb new file mode 100644 index 00000000000..44430754a2e --- /dev/null +++ b/app/graphql/types/ci/variable_type_enum.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Types + module Ci + class VariableTypeEnum < BaseEnum + graphql_name 'CiVariableType' + + ::Ci::Variable.variable_types.keys.each do |variable_type| + value variable_type.upcase, value: variable_type, description: "#{variable_type.humanize} type." + end + end + end +end diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index 49971d52a30..52e9f808066 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -194,6 +194,13 @@ module Types complexity: 5, resolver: Resolvers::GroupsResolver + field :ci_variables, + Types::Ci::VariableType.connection_type, + null: true, + description: "List of the group's CI/CD variables.", + authorize: :admin_group, + method: :variables + field :runners, Types::Ci::RunnerType.connection_type, null: true, resolver: Resolvers::Ci::GroupRunnersResolver, diff --git a/app/graphql/types/issue_type_enum.rb b/app/graphql/types/issue_type_enum.rb index b18c8b90e96..bc21b802179 100644 --- a/app/graphql/types/issue_type_enum.rb +++ b/app/graphql/types/issue_type_enum.rb @@ -8,5 +8,9 @@ module Types ::WorkItems::Type.allowed_types_for_issues.each do |issue_type| value issue_type.upcase, value: issue_type, description: "#{issue_type.titleize} issue type" end + + value 'TASK', value: 'task', + description: 'Task issue type. Available only when feature flag `work_items` is enabled.', + deprecated: { milestone: '15.2', reason: :alpha } end end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 8642957af02..46ab3f3f432 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -148,6 +148,7 @@ module Types mount_mutation Mutations::WorkItems::UpdateTask, deprecated: { milestone: '15.1', reason: :alpha } mount_mutation Mutations::SavedReplies::Create mount_mutation Mutations::SavedReplies::Update + mount_mutation Mutations::Pages::MarkOnboardingComplete mount_mutation Mutations::SavedReplies::Destroy end end diff --git a/app/graphql/types/permission_types/group_enum.rb b/app/graphql/types/permission_types/group_enum.rb index cc4f5e9f1f0..8b0fee8898c 100644 --- a/app/graphql/types/permission_types/group_enum.rb +++ b/app/graphql/types/permission_types/group_enum.rb @@ -7,6 +7,8 @@ module Types description 'User permission on groups' value 'CREATE_PROJECTS', value: :create_projects, description: 'Groups where the user can create projects.' + value 'TRANSFER_PROJECTS', value: :transfer_projects, + description: 'Groups where the user can transfer projects to.' end end end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 603d5ead540..7e3800c6a13 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -70,10 +70,10 @@ module Types description: 'Indicates if shared runners are enabled for the project.' field :service_desk_enabled, GraphQL::Types::Boolean, null: true, - description: 'Indicates if the project has service desk enabled.' + description: 'Indicates if the project has Service Desk enabled.' field :service_desk_address, GraphQL::Types::String, null: true, - description: 'E-mail address of the service desk.' + description: 'E-mail address of the Service Desk.' field :avatar_url, GraphQL::Types::String, null: true, calls_gitaly: true, description: 'URL to avatar image file of the project.' @@ -220,6 +220,13 @@ module Types description: 'Build pipeline counts of the project.', resolver: Resolvers::Ci::ProjectPipelineCountsResolver + field :ci_variables, + Types::Ci::VariableType.connection_type, + null: true, + description: "List of the project's CI/CD variables.", + authorize: :admin_build, + method: :variables + field :ci_cd_settings, Types::Ci::CiCdSettingType, null: true, diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 46d121f6552..9207a867639 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -123,6 +123,11 @@ module Types resolver: Resolvers::Ci::RunnersResolver, description: "Find runners visible to the current user." + field :ci_variables, + Types::Ci::VariableType.connection_type, + null: true, + description: "List of the instance's CI/CD variables." + field :ci_config, resolver: Resolvers::Ci::ConfigResolver, complexity: 126 # AUTHENTICATED_MAX_COMPLEXITY / 2 + 1 field :timelogs, Types::TimelogType.connection_type, @@ -136,6 +141,10 @@ module Types null: true, resolver: Resolvers::BoardListResolver + field :todo, + null: true, + resolver: Resolvers::TodoResolver + field :topics, Types::Projects::TopicType.connection_type, null: true, resolver: Resolvers::TopicsResolver, @@ -174,6 +183,12 @@ 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_type.rb b/app/graphql/types/release_type.rb index 43dc0c4ce85..d906c577aa5 100644 --- a/app/graphql/types/release_type.rb +++ b/app/graphql/types/release_type.rb @@ -40,6 +40,8 @@ module Types authorize: :download_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/user_interface.rb b/app/graphql/types/user_interface.rb index 1c8a1352c72..edbc8aee9c5 100644 --- a/app/graphql/types/user_interface.rb +++ b/app/graphql/types/user_interface.rb @@ -88,7 +88,7 @@ module Types null: true, description: 'Personal namespace of the user.' - field :todos, resolver: Resolvers::TodoResolver, description: 'To-do items of the user.' + field :todos, resolver: Resolvers::TodosResolver, description: 'To-do items of the user.' # Merge request field: MRs can be authored, assigned, or assigned-for-review: field :authored_merge_requests, diff --git a/app/graphql/types/work_item_id_type.rb b/app/graphql/types/work_item_id_type.rb index ddcf3416014..bb01f865414 100644 --- a/app/graphql/types/work_item_id_type.rb +++ b/app/graphql/types/work_item_id_type.rb @@ -27,6 +27,7 @@ module Types def coerce_input(string, ctx) gid = super + return if gid.nil? # Always return a WorkItemID even if an Issue Global ID is provided as input return work_item_gid(gid) if suitable?(gid) diff --git a/app/graphql/types/work_items/widget_interface.rb b/app/graphql/types/work_items/widget_interface.rb index f3cf1d74829..1b752393296 100644 --- a/app/graphql/types/work_items/widget_interface.rb +++ b/app/graphql/types/work_items/widget_interface.rb @@ -16,13 +16,19 @@ module Types ::Types::WorkItems::Widgets::DescriptionType when ::WorkItems::Widgets::Hierarchy ::Types::WorkItems::Widgets::HierarchyType + when ::WorkItems::Widgets::Assignees + ::Types::WorkItems::Widgets::AssigneesType + when ::WorkItems::Widgets::Weight + ::Types::WorkItems::Widgets::WeightType else raise "Unknown GraphQL type for widget #{object}" end end orphan_types ::Types::WorkItems::Widgets::DescriptionType, - ::Types::WorkItems::Widgets::HierarchyType + ::Types::WorkItems::Widgets::HierarchyType, + ::Types::WorkItems::Widgets::AssigneesType, + ::Types::WorkItems::Widgets::WeightType end end end diff --git a/app/graphql/types/work_items/widgets/assignees_type.rb b/app/graphql/types/work_items/widgets/assignees_type.rb new file mode 100644 index 00000000000..08ee06fdfa0 --- /dev/null +++ b/app/graphql/types/work_items/widgets/assignees_type.rb @@ -0,0 +1,31 @@ +# 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 AssigneesType < BaseObject + graphql_name 'WorkItemWidgetAssignees' + description 'Represents an assignees widget' + + implements Types::WorkItems::WidgetInterface + + field :assignees, Types::UserType.connection_type, null: true, + description: 'Assignees of the work item.' + + field :allows_multiple_assignees, GraphQL::Types::Boolean, null: true, method: :allows_multiple_assignees?, + description: 'Indicates whether multiple assignees are allowed.' + + field :can_invite_members, GraphQL::Types::Boolean, null: false, resolver_method: :can_invite_members?, + description: 'Indicates whether the current user can invite members to the work item\'s project.' + + def can_invite_members? + Ability.allowed?(current_user, :admin_project_member, object.work_item.project) + end + end + # rubocop:enable Graphql/AuthorizeTypes + end + end +end diff --git a/app/graphql/types/work_items/widgets/hierarchy_create_input_type.rb b/app/graphql/types/work_items/widgets/hierarchy_create_input_type.rb new file mode 100644 index 00000000000..cee6d69cd0c --- /dev/null +++ b/app/graphql/types/work_items/widgets/hierarchy_create_input_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Types + module WorkItems + module Widgets + class HierarchyCreateInputType < BaseInputObject + graphql_name 'WorkItemWidgetHierarchyCreateInput' + + argument :parent_id, ::Types::GlobalIDType[::WorkItem], + required: false, + loads: ::Types::WorkItemType, + description: 'Global ID of the parent work item.' + end + end + end +end diff --git a/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb b/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb new file mode 100644 index 00000000000..e1a9ebb76e9 --- /dev/null +++ b/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + module WorkItems + module Widgets + class HierarchyUpdateInputType < BaseInputObject + graphql_name 'WorkItemWidgetHierarchyUpdateInput' + + argument :parent_id, ::Types::GlobalIDType[::WorkItem], + required: false, + loads: ::Types::WorkItemType, + description: 'Global ID of the parent work item. Use `null` to remove the association.' + + argument :children_ids, [::Types::GlobalIDType[::WorkItem]], + required: false, + description: 'Global IDs of children work items.', + loads: ::Types::WorkItemType, + as: :children + end + end + end +end diff --git a/app/graphql/types/work_items/widgets/weight_input_type.rb b/app/graphql/types/work_items/widgets/weight_input_type.rb new file mode 100644 index 00000000000..a01c63222a5 --- /dev/null +++ b/app/graphql/types/work_items/widgets/weight_input_type.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module WorkItems + module Widgets + class WeightInputType < BaseInputObject + graphql_name 'WorkItemWidgetWeightInput' + + argument :weight, GraphQL::Types::Int, + required: true, + description: 'Weight of the work item.' + end + end + end +end diff --git a/app/graphql/types/work_items/widgets/weight_type.rb b/app/graphql/types/work_items/widgets/weight_type.rb new file mode 100644 index 00000000000..c8eaf560268 --- /dev/null +++ b/app/graphql/types/work_items/widgets/weight_type.rb @@ -0,0 +1,21 @@ +# 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 WeightType < BaseObject + graphql_name 'WorkItemWidgetWeight' + description 'Represents a weight widget' + + implements Types::WorkItems::WidgetInterface + + field :weight, GraphQL::Types::Int, null: true, + description: 'Weight of the work item.' + end + # rubocop:enable Graphql/AuthorizeTypes + end + end +end |