summaryrefslogtreecommitdiff
path: root/app/graphql/resolvers
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 14:34:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 14:34:42 +0000
commit9f46488805e86b1bc341ea1620b866016c2ce5ed (patch)
treef9748c7e287041e37d6da49e0a29c9511dc34768 /app/graphql/resolvers
parentdfc92d081ea0332d69c8aca2f0e745cb48ae5e6d (diff)
downloadgitlab-ce-9f46488805e86b1bc341ea1620b866016c2ce5ed.tar.gz
Add latest changes from gitlab-org/gitlab@13-0-stable-ee
Diffstat (limited to 'app/graphql/resolvers')
-rw-r--r--app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb13
-rw-r--r--app/graphql/resolvers/alert_management_alert_resolver.rb31
-rw-r--r--app/graphql/resolvers/board_lists_resolver.rb38
-rw-r--r--app/graphql/resolvers/branch_commit_resolver.rb17
-rw-r--r--app/graphql/resolvers/design_management/design_at_version_resolver.rb46
-rw-r--r--app/graphql/resolvers/design_management/design_resolver.rb57
-rw-r--r--app/graphql/resolvers/design_management/designs_resolver.rb50
-rw-r--r--app/graphql/resolvers/design_management/version/design_at_version_resolver.rb95
-rw-r--r--app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb60
-rw-r--r--app/graphql/resolvers/design_management/version_in_collection_resolver.rb45
-rw-r--r--app/graphql/resolvers/design_management/version_resolver.rb25
-rw-r--r--app/graphql/resolvers/design_management/versions_resolver.rb76
-rw-r--r--app/graphql/resolvers/issues_resolver.rb18
-rw-r--r--app/graphql/resolvers/metrics/dashboards/annotation_resolver.rb1
-rw-r--r--app/graphql/resolvers/milestone_resolver.rb31
-rw-r--r--app/graphql/resolvers/namespace_projects_resolver.rb2
-rw-r--r--app/graphql/resolvers/projects_resolver.rb31
-rw-r--r--app/graphql/resolvers/release_resolver.rb25
-rw-r--r--app/graphql/resolvers/releases_resolver.rb21
19 files changed, 677 insertions, 5 deletions
diff --git a/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb b/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb
new file mode 100644
index 00000000000..7f4346632ca
--- /dev/null
+++ b/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module AlertManagement
+ class AlertStatusCountsResolver < BaseResolver
+ type Types::AlertManagement::AlertStatusCountsType, null: true
+
+ def resolve(**args)
+ ::Gitlab::AlertManagement::AlertStatusCounts.new(context[:current_user], object, args)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/alert_management_alert_resolver.rb b/app/graphql/resolvers/alert_management_alert_resolver.rb
new file mode 100644
index 00000000000..51ebbb96476
--- /dev/null
+++ b/app/graphql/resolvers/alert_management_alert_resolver.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class AlertManagementAlertResolver < BaseResolver
+ argument :iid, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'IID of the alert. For example, "1"'
+
+ argument :statuses, [Types::AlertManagement::StatusEnum],
+ as: :status,
+ required: false,
+ description: 'Alerts with the specified statues. For example, [TRIGGERED]'
+
+ argument :sort, Types::AlertManagement::AlertSortEnum,
+ description: 'Sort alerts by this criteria',
+ required: false
+
+ argument :search, GraphQL::STRING_TYPE,
+ description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.',
+ required: false
+
+ type Types::AlertManagement::AlertType, null: true
+
+ def resolve(**args)
+ parent = object.respond_to?(:sync) ? object.sync : object
+ return ::AlertManagement::Alert.none if parent.nil?
+
+ ::AlertManagement::AlertsFinder.new(context[:current_user], parent, args).execute
+ end
+ end
+end
diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb
new file mode 100644
index 00000000000..f8d62ba86af
--- /dev/null
+++ b/app/graphql/resolvers/board_lists_resolver.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class BoardListsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::BoardListType, null: true
+
+ alias_method :board, :object
+
+ def resolve(lookahead: nil)
+ authorize!(board)
+
+ lists = board_lists
+
+ if load_preferences?(lookahead)
+ List.preload_preferences_for_user(lists, context[:current_user])
+ end
+
+ Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(lists)
+ end
+
+ private
+
+ def board_lists
+ service = Boards::Lists::ListService.new(board.resource_parent, context[:current_user])
+ service.execute(board, create_default_lists: false)
+ end
+
+ def authorized_resource?(board)
+ Ability.allowed?(context[:current_user], :read_list, board)
+ end
+
+ def load_preferences?(lookahead)
+ lookahead&.selection(:edges)&.selection(:node)&.selects?(:collapsed)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/branch_commit_resolver.rb b/app/graphql/resolvers/branch_commit_resolver.rb
new file mode 100644
index 00000000000..11c49e17bc5
--- /dev/null
+++ b/app/graphql/resolvers/branch_commit_resolver.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class BranchCommitResolver < BaseResolver
+ type Types::CommitType, null: true
+
+ alias_method :branch, :object
+
+ def resolve(**args)
+ return unless branch
+
+ commit = branch.dereferenced_target
+
+ ::Commit.new(commit, context[:branch_project]) if commit
+ end
+ end
+end
diff --git a/app/graphql/resolvers/design_management/design_at_version_resolver.rb b/app/graphql/resolvers/design_management/design_at_version_resolver.rb
new file mode 100644
index 00000000000..fd9b349f974
--- /dev/null
+++ b/app/graphql/resolvers/design_management/design_at_version_resolver.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module DesignManagement
+ class DesignAtVersionResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::DesignManagement::DesignAtVersionType, null: false
+
+ authorize :read_design
+
+ argument :id, GraphQL::ID_TYPE,
+ required: true,
+ description: 'The Global ID of the design at this version'
+
+ def resolve(id:)
+ authorized_find!(id: id)
+ end
+
+ def find_object(id:)
+ dav = GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::DesignAtVersion)
+ return unless consistent?(dav)
+
+ dav
+ end
+
+ def self.single
+ self
+ end
+
+ private
+
+ # If this resolver is mounted on something that has an issue
+ # (such as design collection for instance), then we should check
+ # that the DesignAtVersion as found by its ID does in fact belong
+ # to this issue.
+ def consistent?(dav)
+ issue.nil? || (dav&.design&.issue_id == issue.id)
+ end
+
+ def issue
+ object&.issue
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/design_management/design_resolver.rb b/app/graphql/resolvers/design_management/design_resolver.rb
new file mode 100644
index 00000000000..05bdbbbe407
--- /dev/null
+++ b/app/graphql/resolvers/design_management/design_resolver.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module DesignManagement
+ class DesignResolver < BaseResolver
+ argument :id, GraphQL::ID_TYPE,
+ required: false,
+ description: 'Find a design by its ID'
+
+ argument :filename, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Find a design by its filename'
+
+ def resolve(filename: nil, id: nil)
+ params = parse_args(filename, id)
+
+ build_finder(params).execute.first
+ end
+
+ def self.single
+ self
+ end
+
+ private
+
+ def issue
+ object.issue
+ end
+
+ def build_finder(params)
+ ::DesignManagement::DesignsFinder.new(issue, current_user, params)
+ end
+
+ def error(msg)
+ raise ::Gitlab::Graphql::Errors::ArgumentError, msg
+ end
+
+ def parse_args(filename, id)
+ provided = [filename, id].map(&:present?)
+
+ if provided.none?
+ error('one of id or filename must be passed')
+ elsif provided.all?
+ error('only one of id or filename may be passed')
+ elsif filename.present?
+ { filenames: [filename] }
+ else
+ { ids: [parse_gid(id)] }
+ end
+ end
+
+ def parse_gid(gid)
+ GitlabSchema.parse_gid(gid, expected_type: ::DesignManagement::Design).model_id
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/design_management/designs_resolver.rb b/app/graphql/resolvers/design_management/designs_resolver.rb
new file mode 100644
index 00000000000..81f94d5cb30
--- /dev/null
+++ b/app/graphql/resolvers/design_management/designs_resolver.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module DesignManagement
+ class DesignsResolver < BaseResolver
+ argument :ids,
+ [GraphQL::ID_TYPE],
+ required: false,
+ description: 'Filters designs by their ID'
+ argument :filenames,
+ [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Filters designs by their filename'
+ argument :at_version,
+ GraphQL::ID_TYPE,
+ required: false,
+ description: 'Filters designs to only those that existed at the version. ' \
+ 'If argument is omitted or nil then all designs will reflect the latest version'
+
+ def self.single
+ ::Resolvers::DesignManagement::DesignResolver
+ end
+
+ def resolve(ids: nil, filenames: nil, at_version: nil)
+ ::DesignManagement::DesignsFinder.new(
+ issue,
+ current_user,
+ ids: design_ids(ids),
+ filenames: filenames,
+ visible_at_version: version(at_version),
+ order: :id
+ ).execute
+ end
+
+ private
+
+ def version(at_version)
+ GitlabSchema.object_from_id(at_version)&.sync if at_version
+ end
+
+ def design_ids(ids)
+ ids&.map { |id| GlobalID.parse(id).model_id }
+ end
+
+ def issue
+ object.issue
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
new file mode 100644
index 00000000000..03f7908780c
--- /dev/null
+++ b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module DesignManagement
+ module Version
+ # Resolver for a DesignAtVersion object given an implicit version context
+ class DesignAtVersionResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::DesignManagement::DesignAtVersionType, null: true
+
+ authorize :read_design
+
+ argument :id, GraphQL::ID_TYPE,
+ required: false,
+ as: :design_at_version_id,
+ description: 'The ID of the DesignAtVersion'
+ argument :design_id, GraphQL::ID_TYPE,
+ required: false,
+ description: 'The ID of a specific design'
+ argument :filename, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'The filename of a specific design'
+
+ def self.single
+ self
+ end
+
+ def resolve(design_id: nil, filename: nil, design_at_version_id: nil)
+ validate_arguments(design_id, filename, design_at_version_id)
+
+ return unless Ability.allowed?(current_user, :read_design, issue)
+ return specific_design_at_version(design_at_version_id) if design_at_version_id
+
+ find(design_id, filename).map { |d| make(d) }.first
+ end
+
+ private
+
+ def validate_arguments(design_id, filename, design_at_version_id)
+ args = { filename: filename, id: design_at_version_id, design_id: design_id }
+ passed = args.compact.keys
+
+ return if passed.size == 1
+
+ msg = "Exactly one of #{args.keys.join(', ')} expected, got #{passed}"
+
+ raise Gitlab::Graphql::Errors::ArgumentError, msg
+ end
+
+ def specific_design_at_version(id)
+ dav = GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::DesignAtVersion)
+ return unless consistent?(dav)
+
+ dav
+ end
+
+ # Test that the DAV found by ID actually belongs on this version, and
+ # that it is visible at this version.
+ def consistent?(dav)
+ return false unless dav.present?
+
+ dav.design.issue_id == issue.id &&
+ dav.version.id == version.id &&
+ dav.design.visible_in?(version)
+ end
+
+ def find(id, filename)
+ ids = [parse_design_id(id).model_id] if id
+ filenames = [filename] if filename
+
+ ::DesignManagement::DesignsFinder
+ .new(issue, current_user, ids: ids, filenames: filenames, visible_at_version: version)
+ .execute
+ end
+
+ def parse_design_id(id)
+ GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Design)
+ end
+
+ def issue
+ version.issue
+ end
+
+ def version
+ object
+ end
+
+ def make(design)
+ ::DesignManagement::DesignAtVersion.new(design: design, version: version)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb b/app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb
new file mode 100644
index 00000000000..5ccb2f3e311
--- /dev/null
+++ b/app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module DesignManagement
+ module Version
+ # Resolver for DesignAtVersion objects given an implicit version context
+ class DesignsAtVersionResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::DesignManagement::DesignAtVersionType, null: true
+
+ authorize :read_design
+
+ argument :ids,
+ [GraphQL::ID_TYPE],
+ required: false,
+ description: 'Filters designs by their ID'
+ argument :filenames,
+ [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Filters designs by their filename'
+
+ def self.single
+ ::Resolvers::DesignManagement::Version::DesignAtVersionResolver
+ end
+
+ def resolve(ids: nil, filenames: nil)
+ find(ids, filenames).execute.map { |d| make(d) }
+ end
+
+ private
+
+ def find(ids, filenames)
+ ids = ids&.map { |id| parse_design_id(id).model_id }
+
+ ::DesignManagement::DesignsFinder.new(issue, current_user,
+ ids: ids,
+ filenames: filenames,
+ visible_at_version: version)
+ end
+
+ def parse_design_id(id)
+ GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Design)
+ end
+
+ def issue
+ version.issue
+ end
+
+ def version
+ object
+ end
+
+ def make(design)
+ ::DesignManagement::DesignAtVersion.new(design: design, version: version)
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/design_management/version_in_collection_resolver.rb b/app/graphql/resolvers/design_management/version_in_collection_resolver.rb
new file mode 100644
index 00000000000..9e729172881
--- /dev/null
+++ b/app/graphql/resolvers/design_management/version_in_collection_resolver.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module DesignManagement
+ class VersionInCollectionResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::DesignManagement::VersionType, null: true
+
+ authorize :read_design
+
+ alias_method :collection, :object
+
+ argument :sha, GraphQL::STRING_TYPE,
+ required: false,
+ description: "The SHA256 of a specific version"
+ argument :id, GraphQL::ID_TYPE,
+ required: false,
+ description: 'The Global ID of the version'
+
+ def resolve(id: nil, sha: nil)
+ check_args(id, sha)
+
+ gid = GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Version) if id
+
+ ::DesignManagement::VersionsFinder
+ .new(collection, current_user, sha: sha, version_id: gid&.model_id)
+ .execute
+ .first
+ end
+
+ def self.single
+ self
+ end
+
+ private
+
+ def check_args(id, sha)
+ return if id.present? || sha.present?
+
+ raise ::Gitlab::Graphql::Errors::ArgumentError, 'one of id or sha is required'
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/design_management/version_resolver.rb b/app/graphql/resolvers/design_management/version_resolver.rb
new file mode 100644
index 00000000000..b0e0843e6c8
--- /dev/null
+++ b/app/graphql/resolvers/design_management/version_resolver.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module DesignManagement
+ class VersionResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ type Types::DesignManagement::VersionType, null: true
+
+ authorize :read_design
+
+ argument :id, GraphQL::ID_TYPE,
+ required: true,
+ description: 'The Global ID of the version'
+
+ def resolve(id:)
+ authorized_find!(id: id)
+ end
+
+ def find_object(id:)
+ GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::Version)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/design_management/versions_resolver.rb b/app/graphql/resolvers/design_management/versions_resolver.rb
new file mode 100644
index 00000000000..a62258dad5c
--- /dev/null
+++ b/app/graphql/resolvers/design_management/versions_resolver.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module DesignManagement
+ class VersionsResolver < BaseResolver
+ type Types::DesignManagement::VersionType.connection_type, null: false
+
+ alias_method :design_or_collection, :object
+
+ argument :earlier_or_equal_to_sha, GraphQL::STRING_TYPE,
+ as: :sha,
+ required: false,
+ description: 'The SHA256 of the most recent acceptable version'
+
+ argument :earlier_or_equal_to_id, GraphQL::ID_TYPE,
+ as: :id,
+ required: false,
+ description: 'The Global ID of the most recent acceptable version'
+
+ # This resolver has a custom singular resolver
+ def self.single
+ ::Resolvers::DesignManagement::VersionInCollectionResolver
+ end
+
+ def resolve(parent: nil, id: nil, sha: nil)
+ version = cutoff(parent, id, sha)
+
+ raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, 'cutoff not found' unless version.present?
+
+ if version == :unconstrained
+ find
+ else
+ find(earlier_or_equal_to: version)
+ end
+ end
+
+ private
+
+ # Find the most recent version that the client will accept
+ def cutoff(parent, id, sha)
+ if sha.present? || id.present?
+ specific_version(id, sha)
+ elsif at_version = at_version_arg(parent)
+ by_id(at_version)
+ else
+ :unconstrained
+ end
+ end
+
+ def specific_version(id, sha)
+ gid = GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Version) if id
+ find(sha: sha, version_id: gid&.model_id).first
+ end
+
+ def find(**params)
+ ::DesignManagement::VersionsFinder
+ .new(design_or_collection, current_user, params)
+ .execute
+ end
+
+ def by_id(id)
+ GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::Version).sync
+ end
+
+ # Find an `at_version` argument passed to a parent node.
+ #
+ # If one is found, then a design collection further up the AST
+ # has been filtered to reflect designs at that version, and so
+ # for consistency we should only present versions up to the given
+ # version here.
+ def at_version_arg(parent)
+ ::Gitlab::Graphql::FindArgumentInParent.find(parent, :at_version, limit_depth: 4)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index 04da54a6bb6..f103da07666 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -52,6 +52,10 @@ module Resolvers
type Types::IssueType, null: true
+ NON_STABLE_CURSOR_SORTS = %i[priority_asc priority_desc
+ label_priority_asc label_priority_desc
+ milestone_due_asc milestone_due_desc].freeze
+
def resolve(**args)
# The project could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` of the project to query for issues, so
@@ -70,7 +74,15 @@ module Resolvers
args[:iids] ||= [args[:iid]].compact
args[:attempt_project_search_optimizations] = args[:search].present?
- IssuesFinder.new(context[:current_user], args).execute
+ issues = IssuesFinder.new(context[:current_user], args).execute
+
+ if non_stable_cursor_sort?(args[:sort])
+ # Certain complex sorts are not supported by the stable cursor pagination yet.
+ # In these cases, we use offset pagination, so we return the correct connection.
+ Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(issues)
+ else
+ issues
+ end
end
def self.resolver_complexity(args, child_complexity:)
@@ -79,5 +91,9 @@ module Resolvers
complexity
end
+
+ def non_stable_cursor_sort?(sort)
+ NON_STABLE_CURSOR_SORTS.include?(sort)
+ end
end
end
diff --git a/app/graphql/resolvers/metrics/dashboards/annotation_resolver.rb b/app/graphql/resolvers/metrics/dashboards/annotation_resolver.rb
index 068323a3073..2dd224bb17b 100644
--- a/app/graphql/resolvers/metrics/dashboards/annotation_resolver.rb
+++ b/app/graphql/resolvers/metrics/dashboards/annotation_resolver.rb
@@ -18,7 +18,6 @@ module Resolvers
def resolve(**args)
return [] unless dashboard
- return [] unless Feature.enabled?(:metrics_dashboard_annotations, dashboard.environment&.project)
::Metrics::Dashboards::AnnotationsFinder.new(dashboard: dashboard, params: args).execute
end
diff --git a/app/graphql/resolvers/milestone_resolver.rb b/app/graphql/resolvers/milestone_resolver.rb
index 2e7b6fdfd5f..6c6513e0ee4 100644
--- a/app/graphql/resolvers/milestone_resolver.rb
+++ b/app/graphql/resolvers/milestone_resolver.rb
@@ -9,6 +9,10 @@ module Resolvers
required: false,
description: 'Filter milestones by state'
+ argument :include_descendants, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Return also milestones in all subgroups and subprojects'
+
type Types::MilestoneType, null: true
def resolve(**args)
@@ -26,16 +30,16 @@ module Resolvers
state: args[:state] || 'all',
start_date: args[:start_date],
end_date: args[:end_date]
- }.merge(parent_id_parameter)
+ }.merge(parent_id_parameter(args))
end
def parent
@parent ||= object.respond_to?(:sync) ? object.sync : object
end
- def parent_id_parameter
+ def parent_id_parameter(args)
if parent.is_a?(Group)
- { group_ids: parent.id }
+ group_parameters(args)
elsif parent.is_a?(Project)
{ project_ids: parent.id }
end
@@ -46,5 +50,26 @@ module Resolvers
def authorize!
Ability.allowed?(context[:current_user], :read_milestone, parent) || raise_resource_not_available_error!
end
+
+ def group_parameters(args)
+ return { group_ids: parent.id } unless include_descendants?(args)
+
+ {
+ group_ids: parent.self_and_descendants.public_or_visible_to_user(current_user).select(:id),
+ project_ids: group_projects.with_issues_or_mrs_available_for_user(current_user)
+ }
+ end
+
+ def include_descendants?(args)
+ args[:include_descendants].present? && Feature.enabled?(:group_milestone_descendants, parent)
+ end
+
+ def group_projects
+ GroupProjectsFinder.new(
+ group: parent,
+ current_user: current_user,
+ options: { include_subgroups: true }
+ ).execute
+ end
end
end
diff --git a/app/graphql/resolvers/namespace_projects_resolver.rb b/app/graphql/resolvers/namespace_projects_resolver.rb
index f5b60f91be6..e841132eea7 100644
--- a/app/graphql/resolvers/namespace_projects_resolver.rb
+++ b/app/graphql/resolvers/namespace_projects_resolver.rb
@@ -29,3 +29,5 @@ module Resolvers
end
end
end
+
+Resolvers::NamespaceProjectsResolver.prepend_if_ee('::EE::Resolvers::NamespaceProjectsResolver')
diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb
new file mode 100644
index 00000000000..068546cd39f
--- /dev/null
+++ b/app/graphql/resolvers/projects_resolver.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class ProjectsResolver < BaseResolver
+ type Types::ProjectType, null: true
+
+ argument :membership, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Limit projects that the current user is a member of'
+
+ argument :search, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Search criteria'
+
+ def resolve(**args)
+ ProjectsFinder
+ .new(current_user: current_user, params: project_finder_params(args))
+ .execute
+ end
+
+ private
+
+ def project_finder_params(params)
+ {
+ without_deleted: true,
+ non_public: params[:membership],
+ search: params[:search]
+ }.compact
+ end
+ end
+end
diff --git a/app/graphql/resolvers/release_resolver.rb b/app/graphql/resolvers/release_resolver.rb
new file mode 100644
index 00000000000..9bae8b8cd13
--- /dev/null
+++ b/app/graphql/resolvers/release_resolver.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class ReleaseResolver < BaseResolver
+ type Types::ReleaseType, null: true
+
+ argument :tag_name, GraphQL::STRING_TYPE,
+ required: true,
+ description: 'The name of the tag associated to the release'
+
+ alias_method :project, :object
+
+ def self.single
+ self
+ end
+
+ def resolve(tag_name:)
+ ReleasesFinder.new(
+ project,
+ current_user,
+ { tag: tag_name }
+ ).execute.first
+ end
+ end
+end
diff --git a/app/graphql/resolvers/releases_resolver.rb b/app/graphql/resolvers/releases_resolver.rb
new file mode 100644
index 00000000000..b2afbb92684
--- /dev/null
+++ b/app/graphql/resolvers/releases_resolver.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class ReleasesResolver < BaseResolver
+ type Types::ReleaseType.connection_type, null: true
+
+ alias_method :project, :object
+
+ # This resolver has a custom singular resolver
+ def self.single
+ Resolvers::ReleaseResolver
+ end
+
+ def resolve(**args)
+ ReleasesFinder.new(
+ project,
+ current_user
+ ).execute
+ end
+ end
+end