Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/graphql/types')
-rw-r--r--app/graphql/types/base_field.rb29
-rw-r--r--app/graphql/types/ci/detailed_status_type.rb2
-rw-r--r--app/graphql/types/ci/job_type.rb2
-rw-r--r--app/graphql/types/ci/pipeline_merge_request_event_type_enum.rb19
-rw-r--r--app/graphql/types/ci/pipeline_type.rb3
-rw-r--r--app/graphql/types/ci/runner_type.rb29
-rw-r--r--app/graphql/types/ci/runner_web_url_edge.rb11
-rw-r--r--app/graphql/types/ci/status_action_type.rb3
-rw-r--r--app/graphql/types/concerns/find_closest.rb15
-rw-r--r--app/graphql/types/customer_relations/contact_state_enum.rb17
-rw-r--r--app/graphql/types/customer_relations/organization_state_enum.rb17
-rw-r--r--app/graphql/types/global_id_type.rb3
-rw-r--r--app/graphql/types/group_member_type.rb2
-rw-r--r--app/graphql/types/group_type.rb6
-rw-r--r--app/graphql/types/issue_sort_enum.rb6
-rw-r--r--app/graphql/types/issue_type.rb7
-rw-r--r--app/graphql/types/limited_countable_connection_type.rb26
-rw-r--r--app/graphql/types/merge_requests/interacts_with_merge_request.rb10
-rw-r--r--app/graphql/types/milestone_type.rb4
-rw-r--r--app/graphql/types/mutation_type.rb14
-rw-r--r--app/graphql/types/packages/cleanup/keep_duplicated_package_files_enum.rb25
-rw-r--r--app/graphql/types/packages/cleanup/policy_type.rb23
-rw-r--r--app/graphql/types/permission_types/base_permission_type.rb14
-rw-r--r--app/graphql/types/project_type.rb15
-rw-r--r--app/graphql/types/query_complexity_type.rb4
-rw-r--r--app/graphql/types/query_type.rb10
-rw-r--r--app/graphql/types/release_asset_link_type.rb9
-rw-r--r--app/graphql/types/release_type.rb3
-rw-r--r--app/graphql/types/terraform/state_type.rb4
-rw-r--r--app/graphql/types/time_type.rb3
-rw-r--r--app/graphql/types/todo_type.rb4
-rw-r--r--app/graphql/types/work_item_sort_enum.rb11
-rw-r--r--app/graphql/types/work_item_type.rb2
-rw-r--r--app/graphql/types/work_items/updated_task_input_type.rb11
-rw-r--r--app/graphql/types/work_items/widget_interface.rb28
-rw-r--r--app/graphql/types/work_items/widget_type_enum.rb14
-rw-r--r--app/graphql/types/work_items/widgets/description_input_type.rb15
-rw-r--r--app/graphql/types/work_items/widgets/description_type.rb25
-rw-r--r--app/graphql/types/work_items/widgets/hierarchy_type.rb30
39 files changed, 404 insertions, 71 deletions
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index b4cd54b1332..6aee9a5c052 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -53,6 +53,30 @@ module Types
field_authorized?(object, ctx) && resolver_authorized?(object, ctx)
end
+ # This gets called from the gem's `calculate_complexity` method, allowing us
+ # to ensure our complexity calculation is used even for connections.
+ # This code is actually a copy of the default case in `calculate_complexity`
+ # in `lib/graphql/schema/field.rb`
+ # (https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/schema/field.rb)
+ def complexity_for(child_complexity:, query:, lookahead:)
+ defined_complexity = complexity
+
+ case defined_complexity
+ when Proc
+ arguments = query.arguments_for(lookahead.ast_nodes.first, self)
+
+ if arguments.respond_to?(:keyword_arguments)
+ defined_complexity.call(query.context, arguments.keyword_arguments, child_complexity)
+ else
+ child_complexity
+ end
+ when Numeric
+ defined_complexity + child_complexity
+ else
+ raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
+ end
+ end
+
def base_complexity
complexity = DEFAULT_COMPLEXITY
complexity += 1 if calls_gitaly?
@@ -150,10 +174,9 @@ module Types
def connection_complexity_multiplier(ctx, args)
# Resolvers may add extra complexity depending on number of items being loaded.
- field_defn = to_graphql
- return 0 unless field_defn.connection?
+ return 0 unless connection?
- page_size = field_defn.connection_max_page_size || ctx.schema.default_max_page_size
+ page_size = max_page_size || ctx.schema.default_max_page_size
limit_value = [args[:first], args[:last], page_size].compact.min
multiplier = resolver&.try(:complexity_multiplier, args).to_f
limit_value * multiplier
diff --git a/app/graphql/types/ci/detailed_status_type.rb b/app/graphql/types/ci/detailed_status_type.rb
index e3413551a3f..3fab040cc0b 100644
--- a/app/graphql/types/ci/detailed_status_type.rb
+++ b/app/graphql/types/ci/detailed_status_type.rb
@@ -33,7 +33,7 @@ module Types
method: :status_tooltip
def id(parent:)
- "#{object.id}-#{parent.object.object.id}"
+ "#{object.id}-#{parent.id}"
end
def action
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index f25fc56a588..b20a671179b 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -7,7 +7,7 @@ module Types
class JobType < BaseObject
graphql_name 'CiJob'
- connection_type_class(Types::CountableConnectionType)
+ connection_type_class(Types::LimitedCountableConnectionType)
expose_permissions Types::PermissionTypes::Ci::Job
diff --git a/app/graphql/types/ci/pipeline_merge_request_event_type_enum.rb b/app/graphql/types/ci/pipeline_merge_request_event_type_enum.rb
new file mode 100644
index 00000000000..a1236b8f2c1
--- /dev/null
+++ b/app/graphql/types/ci/pipeline_merge_request_event_type_enum.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class PipelineMergeRequestEventTypeEnum < BaseEnum
+ graphql_name 'PipelineMergeRequestEventType'
+ description 'Event type of the pipeline associated with a merge request'
+
+ value 'MERGED_RESULT',
+ 'Pipeline run on the changes from the source branch combined with the target branch.',
+ value: :merged_result
+ value 'DETACHED',
+ 'Pipeline run on the changes in the merge request source branch.',
+ value: :detached
+ end
+ end
+end
+
+Types::Ci::PipelineMergeRequestEventTypeEnum.prepend_mod
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index 81afc7f0f42..60418fec6c5 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -175,6 +175,9 @@ module Types
field :warning_messages, [Types::Ci::PipelineMessageType], null: true,
description: 'Pipeline warning messages.'
+ field :merge_request_event_type, Types::Ci::PipelineMergeRequestEventTypeEnum, null: true,
+ description: "Event type of the pipeline associated with a merge request."
+
def detailed_status
object.detailed_status(current_user)
end
diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb
index 6f957d2511f..949e216a982 100644
--- a/app/graphql/types/ci/runner_type.rb
+++ b/app/graphql/types/ci/runner_type.rb
@@ -85,6 +85,15 @@ 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
+
+ def maintenance_note_html_resolver
+ ::MarkupHelper.markdown(object.maintenance_note, context.to_h.dup)
+ end
def job_count
# We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT
@@ -136,16 +145,22 @@ module Types
# 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, args|
- runner_and_owner_ids = runner_assoc_type.where(runner_id: runner_ids).pluck(:runner_id, column_name)
-
- owner_ids_by_runner_id = runner_and_owner_ids.group_by(&:first).transform_values { |v| v.pluck(1) }
- owner_ids = runner_and_owner_ids.pluck(1).uniq
-
+ 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, owner_ids_by_runner_id[runner_id]&.map { |owner_id| owners[owner_id] } || [])
+ loader.call(runner_id, runner_owner_ids_by_runner_id[runner_id]&.map { |owner_id| owners[owner_id] } || [])
end
end
end
diff --git a/app/graphql/types/ci/runner_web_url_edge.rb b/app/graphql/types/ci/runner_web_url_edge.rb
index 035d75c22c6..7dfcd1f3510 100644
--- a/app/graphql/types/ci/runner_web_url_edge.rb
+++ b/app/graphql/types/ci/runner_web_url_edge.rb
@@ -4,8 +4,6 @@ module Types
module Ci
# rubocop: disable Graphql/AuthorizeTypes
class RunnerWebUrlEdge < ::Types::BaseEdge
- include FindClosest
-
field :edit_url, GraphQL::Types::String, null: true,
description: 'Web URL of the runner edit page. The value depends on where you put this field in the query. You can use it for projects or groups.',
extras: [:parent]
@@ -19,19 +17,18 @@ module Types
@runner = node.node
end
+ # here parent is a Keyset::Connection
def edit_url(parent:)
- runner_url(parent: parent, url_type: :edit_url)
+ runner_url(owner: parent.parent, url_type: :edit_url)
end
def web_url(parent:)
- runner_url(parent: parent, url_type: :default)
+ runner_url(owner: parent.parent, url_type: :default)
end
private
- def runner_url(parent:, url_type: :default)
- owner = closest_parent([::Types::ProjectType, ::Types::GroupType], parent)
-
+ def runner_url(owner:, url_type: :default)
# Only ::Group is supported at the moment, future iterations will include ::Project.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/16338
case owner
diff --git a/app/graphql/types/ci/status_action_type.rb b/app/graphql/types/ci/status_action_type.rb
index 26ca3c1438a..c0f61cf49f2 100644
--- a/app/graphql/types/ci/status_action_type.rb
+++ b/app/graphql/types/ci/status_action_type.rb
@@ -21,7 +21,8 @@ module Types
description: 'Title for the action, for example: Retry.'
def id(parent:)
- "#{parent.parent.object.object.class.name}-#{parent.object.object.id}"
+ # parent is a SimpleDelegator
+ "#{parent.subject.class.name}-#{parent.id}"
end
def action_method
diff --git a/app/graphql/types/concerns/find_closest.rb b/app/graphql/types/concerns/find_closest.rb
deleted file mode 100644
index 3064db19ea0..00000000000
--- a/app/graphql/types/concerns/find_closest.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module FindClosest
- # Find the closest node which has any of the given types above this node, and return the domain object
- def closest_parent(types, parent)
- while parent
-
- if types.any? {|type| parent.object.instance_of? type}
- return parent.object.object
- else
- parent = parent.try(:parent)
- end
- end
- end
-end
diff --git a/app/graphql/types/customer_relations/contact_state_enum.rb b/app/graphql/types/customer_relations/contact_state_enum.rb
new file mode 100644
index 00000000000..445d2a41401
--- /dev/null
+++ b/app/graphql/types/customer_relations/contact_state_enum.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module CustomerRelations
+ class ContactStateEnum < BaseEnum
+ graphql_name 'CustomerRelationsContactState'
+
+ value 'active',
+ description: "Active contact.",
+ value: :active
+
+ value 'inactive',
+ description: "Inactive contact.",
+ value: :inactive
+ end
+ end
+end
diff --git a/app/graphql/types/customer_relations/organization_state_enum.rb b/app/graphql/types/customer_relations/organization_state_enum.rb
new file mode 100644
index 00000000000..ecdd7d092ad
--- /dev/null
+++ b/app/graphql/types/customer_relations/organization_state_enum.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module CustomerRelations
+ class OrganizationStateEnum < BaseEnum
+ graphql_name 'CustomerRelationsOrganizationState'
+
+ value 'active',
+ description: "Active organization.",
+ value: :active
+
+ value 'inactive',
+ description: "Inactive organization.",
+ value: :inactive
+ end
+ end
+end
diff --git a/app/graphql/types/global_id_type.rb b/app/graphql/types/global_id_type.rb
index 6a924c13a3c..145a5a22460 100644
--- a/app/graphql/types/global_id_type.rb
+++ b/app/graphql/types/global_id_type.rb
@@ -84,7 +84,8 @@ module Types
end
define_singleton_method(:suitable?) do |gid|
- next false if gid.nil?
+ # an argument can be nil, so allow it here
+ next true if gid.nil?
gid.model_name.safe_constantize.present? &&
gid.model_class.ancestors.include?(model_class)
diff --git a/app/graphql/types/group_member_type.rb b/app/graphql/types/group_member_type.rb
index 18242f7b8b1..c4582f31bec 100644
--- a/app/graphql/types/group_member_type.rb
+++ b/app/graphql/types/group_member_type.rb
@@ -15,7 +15,7 @@ module Types
field :notification_email,
resolver: Resolvers::GroupMembers::NotificationEmailResolver,
- description: "Group notification email for User. Only availble for admins."
+ description: "Group notification email for User. Only available for admins."
def group
Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.source_id).find
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index a94cd6fad20..49971d52a30 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -201,11 +201,13 @@ module Types
field :organizations, Types::CustomerRelations::OrganizationType.connection_type,
null: true,
- description: "Find organizations of this group."
+ description: "Find organizations of this group.",
+ resolver: Resolvers::Crm::OrganizationsResolver
field :contacts, Types::CustomerRelations::ContactType.connection_type,
null: true,
- description: "Find contacts of this group."
+ description: "Find contacts of this group.",
+ resolver: Resolvers::Crm::ContactsResolver
field :work_item_types, Types::WorkItems::TypeType.connection_type,
resolver: Resolvers::WorkItems::TypesResolver,
diff --git a/app/graphql/types/issue_sort_enum.rb b/app/graphql/types/issue_sort_enum.rb
index db51e491d4e..7dced3c8e00 100644
--- a/app/graphql/types/issue_sort_enum.rb
+++ b/app/graphql/types/issue_sort_enum.rb
@@ -14,8 +14,10 @@ module Types
value 'TITLE_DESC', 'Title by descending order.', value: :title_desc
value 'POPULARITY_ASC', 'Number of upvotes (awarded "thumbs up" emoji) by ascending order.', value: :popularity_asc
value 'POPULARITY_DESC', 'Number of upvotes (awarded "thumbs up" emoji) by descending order.', value: :popularity_desc
- value 'ESCALATION_STATUS_ASC', 'Status from triggered to resolved. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled.', value: :escalation_status_asc
- value 'ESCALATION_STATUS_DESC', 'Status from resolved to triggered. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled.', value: :escalation_status_desc
+ value 'ESCALATION_STATUS_ASC', 'Status from triggered to resolved.', value: :escalation_status_asc
+ value 'ESCALATION_STATUS_DESC', 'Status from resolved to triggered.', value: :escalation_status_desc
+ value 'CLOSED_AT_ASC', 'Closed time by ascending order.', value: :closed_at_asc
+ value 'CLOSED_AT_DESC', 'Closed time by descending order.', value: :closed_at_desc
end
end
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index c83200bd614..58729b34fc7 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -127,6 +127,9 @@ module Types
field :moved_to, Types::IssueType, null: true,
description: 'Updated Issue after it got moved to another project.'
+ field :closed_as_duplicate_of, Types::IssueType, null: true,
+ description: 'Issue this issue was closed as a duplicate of.'
+
field :create_note_email, GraphQL::Types::String, null: true,
description: 'User specific email address for the issue.'
@@ -161,6 +164,10 @@ module Types
Gitlab::Graphql::Loaders::BatchModelLoader.new(Issue, object.moved_to_id).find
end
+ def closed_as_duplicate_of
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(Issue, object.duplicated_to_id).find
+ end
+
def discussion_locked
!!object.discussion_locked
end
diff --git a/app/graphql/types/limited_countable_connection_type.rb b/app/graphql/types/limited_countable_connection_type.rb
new file mode 100644
index 00000000000..f0698222ea3
--- /dev/null
+++ b/app/graphql/types/limited_countable_connection_type.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Types
+ # rubocop: disable Graphql/AuthorizeTypes
+ class LimitedCountableConnectionType < GraphQL::Types::Relay::BaseConnection
+ COUNT_LIMIT = 1000
+ COUNT_DESCRIPTION = "Limited count of collection. Returns limit + 1 for counts greater than the limit."
+
+ field :count, GraphQL::Types::Int, null: false, description: COUNT_DESCRIPTION do
+ argument :limit, GraphQL::Types::Int,
+ required: false, default_value: COUNT_LIMIT,
+ validates: { numericality: { greater_than: 0, less_than_or_equal_to: COUNT_LIMIT } },
+ description: "Limit value to be applied to the count query. Default is 1000."
+ end
+
+ def count(limit:)
+ relation = object.items
+
+ if relation.respond_to?(:page)
+ relation.page.total_count_with_limit(:all, limit: limit)
+ else
+ [relation.size, limit.next].min
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/merge_requests/interacts_with_merge_request.rb b/app/graphql/types/merge_requests/interacts_with_merge_request.rb
index 15621ef1472..bef2d39dc5c 100644
--- a/app/graphql/types/merge_requests/interacts_with_merge_request.rb
+++ b/app/graphql/types/merge_requests/interacts_with_merge_request.rb
@@ -5,8 +5,6 @@ module Types
module InteractsWithMergeRequest
extend ActiveSupport::Concern
- include FindClosest
-
included do
field :merge_request_interaction,
type: ::Types::UserMergeRequestInteractionType,
@@ -16,11 +14,9 @@ module Types
end
def merge_request_interaction(parent:, id: nil)
- merge_request = closest_parent([::Types::MergeRequestType], parent)
-
- return unless merge_request
-
- Users::MergeRequestInteraction.new(user: object, merge_request: merge_request)
+ # need the connection parent if called from a connection node:
+ parent = parent.parent if parent.try(:field)&.connection?
+ Users::MergeRequestInteraction.new(user: object, merge_request: parent)
end
end
end
diff --git a/app/graphql/types/milestone_type.rb b/app/graphql/types/milestone_type.rb
index 18e4a5d33e3..7741fd723f0 100644
--- a/app/graphql/types/milestone_type.rb
+++ b/app/graphql/types/milestone_type.rb
@@ -59,6 +59,10 @@ module Types
field :stats, Types::MilestoneStatsType, null: true,
description: 'Milestone statistics.'
+ field :releases, ::Types::ReleaseType.connection_type,
+ null: true,
+ description: 'Releases associated with this milestone.'
+
def stats
milestone
end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 7d8ada82d40..8642957af02 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -136,12 +136,16 @@ module Types
mount_mutation Mutations::UserPreferences::Update
mount_mutation Mutations::Packages::Destroy
mount_mutation Mutations::Packages::DestroyFile
+ mount_mutation Mutations::Packages::DestroyFiles
+ mount_mutation Mutations::Packages::Cleanup::Policy::Update
mount_mutation Mutations::Echo
- mount_mutation Mutations::WorkItems::Create
- mount_mutation Mutations::WorkItems::CreateFromTask
- mount_mutation Mutations::WorkItems::Delete
- mount_mutation Mutations::WorkItems::DeleteTask
- mount_mutation Mutations::WorkItems::Update
+ mount_mutation Mutations::WorkItems::Create, deprecated: { milestone: '15.1', reason: :alpha }
+ mount_mutation Mutations::WorkItems::CreateFromTask, deprecated: { milestone: '15.1', reason: :alpha }
+ mount_mutation Mutations::WorkItems::Delete, deprecated: { milestone: '15.1', reason: :alpha }
+ mount_mutation Mutations::WorkItems::DeleteTask, deprecated: { milestone: '15.1', reason: :alpha }
+ mount_mutation Mutations::WorkItems::Update, deprecated: { milestone: '15.1', reason: :alpha }
+ mount_mutation Mutations::WorkItems::UpdateWidgets, deprecated: { milestone: '15.1', reason: :alpha }
+ mount_mutation Mutations::WorkItems::UpdateTask, deprecated: { milestone: '15.1', reason: :alpha }
mount_mutation Mutations::SavedReplies::Create
mount_mutation Mutations::SavedReplies::Update
mount_mutation Mutations::SavedReplies::Destroy
diff --git a/app/graphql/types/packages/cleanup/keep_duplicated_package_files_enum.rb b/app/graphql/types/packages/cleanup/keep_duplicated_package_files_enum.rb
new file mode 100644
index 00000000000..bf8d625a334
--- /dev/null
+++ b/app/graphql/types/packages/cleanup/keep_duplicated_package_files_enum.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ module Cleanup
+ class KeepDuplicatedPackageFilesEnum < BaseEnum
+ graphql_name 'PackagesCleanupKeepDuplicatedPackageFilesEnum'
+
+ OPTIONS_MAPPING = {
+ 'all' => 'ALL_PACKAGE_FILES',
+ '1' => 'ONE_PACKAGE_FILE',
+ '10' => 'TEN_PACKAGE_FILES',
+ '20' => 'TWENTY_PACKAGE_FILES',
+ '30' => 'THIRTY_PACKAGE_FILES',
+ '40' => 'FORTY_PACKAGE_FILES',
+ '50' => 'FIFTY_PACKAGE_FILES'
+ }.freeze
+
+ ::Packages::Cleanup::Policy::KEEP_N_DUPLICATED_PACKAGE_FILES_VALUES.each do |keep_value|
+ value OPTIONS_MAPPING[keep_value], value: keep_value, description: "Value to keep #{keep_value} package files"
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/packages/cleanup/policy_type.rb b/app/graphql/types/packages/cleanup/policy_type.rb
new file mode 100644
index 00000000000..f08aace7df9
--- /dev/null
+++ b/app/graphql/types/packages/cleanup/policy_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ module Packages
+ module Cleanup
+ class PolicyType < ::Types::BaseObject
+ graphql_name 'PackagesCleanupPolicy'
+ description 'A packages cleanup policy designed to keep only packages and packages assets that matter most'
+
+ authorize :admin_package
+
+ field :keep_n_duplicated_package_files,
+ Types::Packages::Cleanup::KeepDuplicatedPackageFilesEnum,
+ null: false,
+ description: 'Number of duplicated package files to retain.'
+ field :next_run_at,
+ Types::TimeType,
+ null: true,
+ description: 'Next time that this packages cleanup policy will be executed.'
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/permission_types/base_permission_type.rb b/app/graphql/types/permission_types/base_permission_type.rb
index a2cefb872c9..07e6e7a55d6 100644
--- a/app/graphql/types/permission_types/base_permission_type.rb
+++ b/app/graphql/types/permission_types/base_permission_type.rb
@@ -12,11 +12,7 @@ module Types
end
def self.ability_field(ability, **kword_args)
- unless resolving_keywords?(kword_args)
- kword_args[:resolve] ||= -> (object, args, context) do
- can?(context[:current_user], ability, object, args.to_h)
- end
- end
+ define_field_resolver_method(ability) unless resolving_keywords?(kword_args)
permission_field(ability, **kword_args)
end
@@ -31,6 +27,14 @@ module Types
field(**kword_args) # rubocop:disable Graphql/Descriptions
end
+ def self.define_field_resolver_method(ability)
+ unless self.respond_to?(ability)
+ define_method ability.to_sym do |*args|
+ Ability.allowed?(context[:current_user], ability, object, args.to_h)
+ end
+ end
+ end
+
def self.resolving_keywords?(arguments)
RESOLVING_KEYWORDS.intersect?(arguments.keys.to_set)
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index f1de8e985b3..603d5ead540 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -4,6 +4,8 @@ module Types
class ProjectType < BaseObject
graphql_name 'Project'
+ connection_type_class(Types::CountableConnectionType)
+
authorize :read_project
expose_permissions Types::PermissionTypes::Project
@@ -142,6 +144,14 @@ module Types
extras: [:lookahead],
resolver: Resolvers::IssuesResolver
+ field :work_items,
+ Types::WorkItemType.connection_type,
+ null: true,
+ deprecated: { milestone: '15.1', reason: :alpha },
+ description: 'Work items of the project.',
+ extras: [:lookahead],
+ resolver: Resolvers::WorkItemsResolver
+
field :issue_status_counts,
Types::IssueStatusCountsType,
null: true,
@@ -179,6 +189,11 @@ module Types
description: 'Packages of the project.',
resolver: Resolvers::ProjectPackagesResolver
+ field :packages_cleanup_policy,
+ Types::Packages::Cleanup::PolicyType,
+ null: true,
+ description: 'Packages cleanup policy for the project.'
+
field :jobs,
type: Types::Ci::JobType.connection_type,
null: true,
diff --git a/app/graphql/types/query_complexity_type.rb b/app/graphql/types/query_complexity_type.rb
index 13b618cf5ce..ddcf448c64a 100644
--- a/app/graphql/types/query_complexity_type.rb
+++ b/app/graphql/types/query_complexity_type.rb
@@ -5,7 +5,7 @@ module Types
class QueryComplexityType < ::Types::BaseObject
graphql_name 'QueryComplexity'
- ANALYZER = GraphQL::Analysis::QueryComplexity.new { |_query, complexity| complexity }
+ ANALYZER = GraphQL::Analysis::AST::QueryComplexity
alias_method :query, :object
@@ -23,7 +23,7 @@ module Types
description: 'GraphQL query complexity score.'
def score
- ::GraphQL::Analysis.analyze_query(query, [ANALYZER]).first
+ ::GraphQL::Analysis::AST.analyze_query(query, [ANALYZER]).first
end
end
# rubocop: enable Graphql/AuthorizeTypes
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 01b1a71896a..46d121f6552 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -52,6 +52,7 @@ module Types
field :milestone, ::Types::MilestoneType,
null: true,
+ extras: [:lookahead],
description: 'Find a milestone.' do
argument :id, ::Types::GlobalIDType[Milestone], required: true, description: 'Find a milestone by its ID.'
end
@@ -90,8 +91,8 @@ module Types
field :work_item, Types::WorkItemType,
null: true,
resolver: Resolvers::WorkItemResolver,
- description: 'Find a work item. Returns `null` if `work_items` feature flag is disabled.' \
- ' The feature is experimental and is subject to change without notice.'
+ deprecated: { milestone: '15.1', reason: :alpha },
+ description: 'Find a work item. Returns `null` if `work_items` feature flag is disabled.'
field :merge_request, Types::MergeRequestType,
null: true,
@@ -156,8 +157,9 @@ module Types
GitlabSchema.find_by_gid(id)
end
- def milestone(id:)
- GitlabSchema.find_by_gid(id)
+ def milestone(id:, lookahead:)
+ preloads = [:releases] if lookahead.selects?(:releases)
+ Gitlab::Graphql::Loaders::BatchModelLoader.new(id.model_class, id.model_id, preloads).find
end
def container_repository(id:)
diff --git a/app/graphql/types/release_asset_link_type.rb b/app/graphql/types/release_asset_link_type.rb
index 33dcb5125e3..29738de27e5 100644
--- a/app/graphql/types/release_asset_link_type.rb
+++ b/app/graphql/types/release_asset_link_type.rb
@@ -7,6 +7,8 @@ module Types
authorize :read_release
+ present_using Releases::LinkPresenter
+
field :external, GraphQL::Types::Boolean, null: true, method: :external?,
description: 'Indicates the link points to an external resource.'
field :id, GraphQL::Types::ID, null: false,
@@ -22,12 +24,5 @@ module Types
description: 'Relative path for the direct asset link.'
field :direct_asset_url, GraphQL::Types::String, null: true,
description: 'Direct asset URL of the link.'
-
- def direct_asset_url
- return object.url unless object.filepath
-
- release = object.release.present
- release.download_url(object.filepath)
- end
end
end
diff --git a/app/graphql/types/release_type.rb b/app/graphql/types/release_type.rb
index 95b6b43bb46..43dc0c4ce85 100644
--- a/app/graphql/types/release_type.rb
+++ b/app/graphql/types/release_type.rb
@@ -13,6 +13,9 @@ 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,
diff --git a/app/graphql/types/terraform/state_type.rb b/app/graphql/types/terraform/state_type.rb
index bce34a85f85..be17fc41c2c 100644
--- a/app/graphql/types/terraform/state_type.rb
+++ b/app/graphql/types/terraform/state_type.rb
@@ -38,6 +38,10 @@ module Types
null: false,
description: 'Timestamp the Terraform state was updated.'
+ field :deleted_at, Types::TimeType,
+ null: true,
+ description: 'Timestamp the Terraform state was deleted.'
+
def locked_by_user
Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.locked_by_user_id).find
end
diff --git a/app/graphql/types/time_type.rb b/app/graphql/types/time_type.rb
index 2db14953308..121515c04db 100644
--- a/app/graphql/types/time_type.rb
+++ b/app/graphql/types/time_type.rb
@@ -12,6 +12,9 @@ module Types
DESC
def self.coerce_input(value, ctx)
+ # arguments can be nil, so don't raise an error
+ return if value.nil?
+
Time.parse(value)
rescue ArgumentError, TypeError => e
raise GraphQL::CoercionError, e.message
diff --git a/app/graphql/types/todo_type.rb b/app/graphql/types/todo_type.rb
index f21b2b261a3..0de6b1d6f8a 100644
--- a/app/graphql/types/todo_type.rb
+++ b/app/graphql/types/todo_type.rb
@@ -53,6 +53,10 @@ module Types
description: 'Timestamp this to-do item was created.',
null: false
+ field :note, Types::Notes::NoteType,
+ description: 'Note which created this to-do item.',
+ null: true
+
def project
Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
end
diff --git a/app/graphql/types/work_item_sort_enum.rb b/app/graphql/types/work_item_sort_enum.rb
new file mode 100644
index 00000000000..e644313d409
--- /dev/null
+++ b/app/graphql/types/work_item_sort_enum.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ class WorkItemSortEnum < SortEnum
+ graphql_name 'WorkItemSort'
+ description 'Values for sorting work items'
+
+ value 'TITLE_ASC', 'Title by ascending order.', value: :title_asc
+ value 'TITLE_DESC', 'Title by descending order.', value: :title_desc
+ end
+end
diff --git a/app/graphql/types/work_item_type.rb b/app/graphql/types/work_item_type.rb
index cd784d54959..18b9bfd1c9a 100644
--- a/app/graphql/types/work_item_type.rb
+++ b/app/graphql/types/work_item_type.rb
@@ -18,6 +18,8 @@ module Types
description: 'State of the work item.'
field :title, GraphQL::Types::String, null: false,
description: 'Title of the work item.'
+ field :widgets, [Types::WorkItems::WidgetInterface], null: true,
+ description: 'Collection of widgets that belong to the work item.'
field :work_item_type, Types::WorkItems::TypeType, null: false,
description: 'Type assigned to the work item.'
diff --git a/app/graphql/types/work_items/updated_task_input_type.rb b/app/graphql/types/work_items/updated_task_input_type.rb
new file mode 100644
index 00000000000..9f8afa2ff1b
--- /dev/null
+++ b/app/graphql/types/work_items/updated_task_input_type.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ class UpdatedTaskInputType < BaseInputObject
+ graphql_name 'WorkItemUpdatedTaskInput'
+
+ include Mutations::WorkItems::UpdateArguments
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widget_interface.rb b/app/graphql/types/work_items/widget_interface.rb
new file mode 100644
index 00000000000..f3cf1d74829
--- /dev/null
+++ b/app/graphql/types/work_items/widget_interface.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module WidgetInterface
+ include Types::BaseInterface
+
+ graphql_name 'WorkItemWidget'
+
+ field :type, ::Types::WorkItems::WidgetTypeEnum, null: true,
+ description: 'Widget type.'
+
+ def self.resolve_type(object, context)
+ case object
+ when ::WorkItems::Widgets::Description
+ ::Types::WorkItems::Widgets::DescriptionType
+ when ::WorkItems::Widgets::Hierarchy
+ ::Types::WorkItems::Widgets::HierarchyType
+ else
+ raise "Unknown GraphQL type for widget #{object}"
+ end
+ end
+
+ orphan_types ::Types::WorkItems::Widgets::DescriptionType,
+ ::Types::WorkItems::Widgets::HierarchyType
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widget_type_enum.rb b/app/graphql/types/work_items/widget_type_enum.rb
new file mode 100644
index 00000000000..4e5933bff86
--- /dev/null
+++ b/app/graphql/types/work_items/widget_type_enum.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ class WidgetTypeEnum < BaseEnum
+ graphql_name 'WorkItemWidgetType'
+ description 'Type of a work item widget'
+
+ ::WorkItems::Type.available_widgets.each do |widget|
+ value widget.type.to_s.upcase, value: widget.type, description: "#{widget.type.to_s.titleize} widget."
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widgets/description_input_type.rb b/app/graphql/types/work_items/widgets/description_input_type.rb
new file mode 100644
index 00000000000..382cfdf659f
--- /dev/null
+++ b/app/graphql/types/work_items/widgets/description_input_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module WorkItems
+ module Widgets
+ class DescriptionInputType < BaseInputObject
+ graphql_name 'WorkItemWidgetDescriptionInput'
+
+ argument :description, GraphQL::Types::String,
+ required: true,
+ description: copy_field_description(Types::WorkItemType, :description)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widgets/description_type.rb b/app/graphql/types/work_items/widgets/description_type.rb
new file mode 100644
index 00000000000..79192d7c3d4
--- /dev/null
+++ b/app/graphql/types/work_items/widgets/description_type.rb
@@ -0,0 +1,25 @@
+# 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 DescriptionType < BaseObject
+ graphql_name 'WorkItemWidgetDescription'
+ description 'Represents a description widget'
+
+ implements Types::WorkItems::WidgetInterface
+
+ field :description, GraphQL::Types::String, null: true,
+ description: 'Description of the work item.'
+
+ markdown_field :description_html, null: true do |resolved_object|
+ resolved_object.work_item
+ end
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+ end
+ end
+end
diff --git a/app/graphql/types/work_items/widgets/hierarchy_type.rb b/app/graphql/types/work_items/widgets/hierarchy_type.rb
new file mode 100644
index 00000000000..057d5fbf056
--- /dev/null
+++ b/app/graphql/types/work_items/widgets/hierarchy_type.rb
@@ -0,0 +1,30 @@
+# 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 HierarchyType < BaseObject
+ graphql_name 'WorkItemWidgetHierarchy'
+ description 'Represents a hierarchy widget'
+
+ implements Types::WorkItems::WidgetInterface
+
+ field :parent, ::Types::WorkItemType, null: true,
+ description: 'Parent work item.',
+ complexity: 5
+
+ field :children, ::Types::WorkItemType.connection_type, null: true,
+ description: 'Child work items.',
+ complexity: 5
+
+ def children
+ object.children.inc_relations_for_permission_check
+ end
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+ end
+ end
+end