Commit 8c22ebb1 authored by Corinna Wiesner's avatar Corinna Wiesner Committed by Vitali Tatarintev

Sort projects by total repository size excess

Sort projects on the usage quota page by the total repository size
excess.
parent e7bc806a
...@@ -23,7 +23,6 @@ module Resolvers ...@@ -23,7 +23,6 @@ module Resolvers
# The namespace could have been loaded in batch by `BatchLoader`. # The namespace could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` or the `full_path` of the namespace # 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. # 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? return Project.none if namespace.nil?
query = include_subgroups ? namespace.all_projects.with_route : namespace.projects.with_route query = include_subgroups ? namespace.all_projects.with_route : namespace.projects.with_route
...@@ -41,6 +40,14 @@ module Resolvers ...@@ -41,6 +40,14 @@ module Resolvers
complexity = super complexity = super
complexity + 10 complexity + 10
end end
private
def namespace
strong_memoize(:namespace) do
object.respond_to?(:sync) ? object.sync : object
end
end
end end
end end
......
...@@ -7,6 +7,7 @@ module Types ...@@ -7,6 +7,7 @@ module Types
description 'Values for sorting projects' description 'Values for sorting projects'
value 'SIMILARITY', 'Most similar to the search query', value: :similarity value 'SIMILARITY', 'Most similar to the search query', value: :similarity
value 'STORAGE', 'Sort by storage size', value: :storage
end end
end end
end end
...@@ -13125,6 +13125,11 @@ enum NamespaceProjectSort { ...@@ -13125,6 +13125,11 @@ enum NamespaceProjectSort {
Most similar to the search query Most similar to the search query
""" """
SIMILARITY SIMILARITY
"""
Sort by storage size
"""
STORAGE
} }
input NegatedBoardIssueInput { input NegatedBoardIssueInput {
......
...@@ -38584,6 +38584,12 @@ ...@@ -38584,6 +38584,12 @@
"description": "Most similar to the search query", "description": "Most similar to the search query",
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
},
{
"name": "STORAGE",
"description": "Sort by storage size",
"isDeprecated": false,
"deprecationReason": null
} }
], ],
"possibleTypes": null "possibleTypes": null
...@@ -3690,6 +3690,7 @@ Values for sorting projects. ...@@ -3690,6 +3690,7 @@ Values for sorting projects.
| Value | Description | | Value | Description |
| ----- | ----------- | | ----- | ----------- |
| `SIMILARITY` | Most similar to the search query | | `SIMILARITY` | Most similar to the search query |
| `STORAGE` | Sort by storage size |
### PackageTypeEnum ### PackageTypeEnum
......
...@@ -19,7 +19,7 @@ query getStorageCounter($fullPath: ID!, $withExcessStorageData: Boolean = false) ...@@ -19,7 +19,7 @@ query getStorageCounter($fullPath: ID!, $withExcessStorageData: Boolean = false)
wikiSize wikiSize
snippetsSize snippetsSize
} }
projects(includeSubgroups: true) { projects(includeSubgroups: true, sort: STORAGE) {
edges { edges {
node { node {
id id
......
...@@ -14,8 +14,9 @@ module EE ...@@ -14,8 +14,9 @@ module EE
def resolve(include_subgroups:, search:, sort:, has_vulnerabilities: false) def resolve(include_subgroups:, search:, sort:, has_vulnerabilities: false)
projects = super(include_subgroups: include_subgroups, search: search, sort: sort) projects = super(include_subgroups: include_subgroups, search: search, sort: sort)
projects = projects.has_vulnerabilities if has_vulnerabilities
has_vulnerabilities ? projects.has_vulnerabilities : projects projects = projects.order_by_total_repository_size_excess_desc(namespace.actual_size_limit) if sort == :storage
projects
end end
end end
end end
......
...@@ -165,6 +165,16 @@ module EE ...@@ -165,6 +165,16 @@ module EE
scope :without_unlimited_repository_size_limit, -> { where.not(repository_size_limit: 0) } scope :without_unlimited_repository_size_limit, -> { where.not(repository_size_limit: 0) }
scope :without_repository_size_limit, -> { where(repository_size_limit: nil) } 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, delegate :shared_runners_minutes, :shared_runners_seconds, :shared_runners_seconds_last_reset,
to: :statistics, allow_nil: true to: :statistics, allow_nil: true
......
...@@ -19,27 +19,51 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do ...@@ -19,27 +19,51 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
end end
describe '#resolve' do 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 context 'when the `has_vulnerabilities` parameter is not truthy' do
let(:has_vulnerabilities) { false } 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 end
context 'when the `has_vulnerabilities` parameter is truthy' do context 'sorting' do
let(:has_vulnerabilities) { true } 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 end
end end
def resolve_projects(has_vulnerabilities) def resolve_projects(has_vulnerabilities: false, sort: :similarity)
args = { args = {
include_subgroups: false, include_subgroups: false,
has_vulnerabilities: has_vulnerabilities, has_vulnerabilities: has_vulnerabilities,
sort: :similarity, sort: sort,
search: nil search: nil
} }
......
...@@ -279,6 +279,17 @@ RSpec.describe Project do ...@@ -279,6 +279,17 @@ RSpec.describe Project do
expect(described_class.not_aimed_for_deletion).to contain_exactly(project) expect(described_class.not_aimed_for_deletion).to contain_exactly(project)
end end
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 end
describe 'validations' do 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