Commit ce9225c0 authored by Vitali Tatarintev's avatar Vitali Tatarintev

Merge branch '258202_sort_projects_by_exceeded_storage' into 'master'

Sort a namespace's projects by exceeded storage

See merge request gitlab-org/gitlab!44124
parents 9e32cc91 8c22ebb1
......@@ -23,7 +23,6 @@ module Resolvers
# The namespace could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` or the `full_path` of the namespace
# to query for projects, so make sure it's loaded and not `nil` before continuing.
namespace = object.respond_to?(:sync) ? object.sync : object
return Project.none if namespace.nil?
query = include_subgroups ? namespace.all_projects.with_route : namespace.projects.with_route
......@@ -41,6 +40,14 @@ module Resolvers
complexity = super
complexity + 10
end
private
def namespace
strong_memoize(:namespace) do
object.respond_to?(:sync) ? object.sync : object
end
end
end
end
......
......@@ -7,6 +7,7 @@ module Types
description 'Values for sorting projects'
value 'SIMILARITY', 'Most similar to the search query', value: :similarity
value 'STORAGE', 'Sort by storage size', value: :storage
end
end
end
......@@ -13276,6 +13276,11 @@ enum NamespaceProjectSort {
Most similar to the search query
"""
SIMILARITY
"""
Sort by storage size
"""
STORAGE
}
input NegatedBoardIssueInput {
......
......@@ -39133,6 +39133,12 @@
"description": "Most similar to the search query",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "STORAGE",
"description": "Sort by storage size",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
......@@ -3771,6 +3771,7 @@ Values for sorting projects.
| Value | Description |
| ----- | ----------- |
| `SIMILARITY` | Most similar to the search query |
| `STORAGE` | Sort by storage size |
### PackageTypeEnum
......
......@@ -19,7 +19,7 @@ query getStorageCounter($fullPath: ID!, $withExcessStorageData: Boolean = false)
wikiSize
snippetsSize
}
projects(includeSubgroups: true) {
projects(includeSubgroups: true, sort: STORAGE) {
edges {
node {
id
......
......@@ -14,8 +14,9 @@ module EE
def resolve(include_subgroups:, search:, sort:, has_vulnerabilities: false)
projects = super(include_subgroups: include_subgroups, search: search, sort: sort)
has_vulnerabilities ? projects.has_vulnerabilities : projects
projects = projects.has_vulnerabilities if has_vulnerabilities
projects = projects.order_by_total_repository_size_excess_desc(namespace.actual_size_limit) if sort == :storage
projects
end
end
end
......
......@@ -165,6 +165,16 @@ module EE
scope :without_unlimited_repository_size_limit, -> { where.not(repository_size_limit: 0) }
scope :without_repository_size_limit, -> { where(repository_size_limit: nil) }
scope :order_by_total_repository_size_excess_desc, -> (limit) do
excess = ::ProjectStatistics.arel_table[:repository_size] +
::ProjectStatistics.arel_table[:lfs_objects_size] -
::Project.arel_table.coalesce(::Project.arel_table[:repository_size_limit], limit, 0)
joins(:statistics).order(
Arel.sql(Arel::Nodes::Descending.new(excess).to_sql)
)
end
delegate :shared_runners_minutes, :shared_runners_seconds, :shared_runners_seconds_last_reset,
to: :statistics, allow_nil: true
......
......@@ -19,27 +19,51 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
end
describe '#resolve' do
subject(:projects) { resolve_projects(has_vulnerabilities) }
context 'has_vulnerabilities' do
subject(:projects) { resolve_projects(has_vulnerabilities: has_vulnerabilities) }
context 'when the `has_vulnerabilities` parameter is not truthy' do
let(:has_vulnerabilities) { false }
context 'when the `has_vulnerabilities` parameter is not truthy' do
let(:has_vulnerabilities) { false }
it { is_expected.to contain_exactly(project_1, project_2) }
it { is_expected.to contain_exactly(project_1, project_2) }
end
context 'when the `has_vulnerabilities` parameter is truthy' do
let(:has_vulnerabilities) { true }
it { is_expected.to contain_exactly(project_1) }
end
end
context 'when the `has_vulnerabilities` parameter is truthy' do
let(:has_vulnerabilities) { true }
context 'sorting' do
let(:project_3) { create(:project, namespace: group) }
before do
project_1.statistics.update!(lfs_objects_size: 11, repository_size: 10)
project_2.statistics.update!(lfs_objects_size: 10, repository_size: 12)
project_3.statistics.update!(lfs_objects_size: 12, repository_size: 11)
end
context 'when sort equals :storage' do
subject(:projects) { resolve_projects(sort: :storage) }
it { is_expected.to eq([project_3, project_2, project_1]) }
end
context 'when sort does not equal :storage' do
subject(:projects) { resolve_projects }
it { is_expected.to contain_exactly(project_1) }
it { is_expected.to eq([project_1, project_2, project_3]) }
end
end
end
end
def resolve_projects(has_vulnerabilities)
def resolve_projects(has_vulnerabilities: false, sort: :similarity)
args = {
include_subgroups: false,
has_vulnerabilities: has_vulnerabilities,
sort: :similarity,
sort: sort,
search: nil
}
......
......@@ -279,6 +279,17 @@ RSpec.describe Project do
expect(described_class.not_aimed_for_deletion).to contain_exactly(project)
end
end
describe '.order_by_total_repository_size_excess_desc' do
let_it_be(:project_1) { create(:project_statistics, lfs_objects_size: 10, repository_size: 10).project }
let_it_be(:project_2) { create(:project_statistics, lfs_objects_size: 5, repository_size: 55).project }
let_it_be(:project_3) { create(:project, repository_size_limit: 30, statistics: create(:project_statistics, lfs_objects_size: 8, repository_size: 32)) }
let(:limit) { 20 }
subject { described_class.order_by_total_repository_size_excess_desc(limit) }
it { is_expected.to eq([project_2, project_3, project_1]) }
end
end
describe 'validations' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment