Commit e65724ea authored by Michael Kozono's avatar Michael Kozono

Merge branch 'add-sorting-to-releases' into 'master'

Add sorting parameters to Releases API

See merge request gitlab-org/gitlab!44118
parents f205bf25 d6d27990
...@@ -9,6 +9,9 @@ class ReleasesFinder ...@@ -9,6 +9,9 @@ class ReleasesFinder
@parent = parent @parent = parent
@current_user = current_user @current_user = current_user
@params = params @params = params
params[:order_by] ||= 'released_at'
params[:sort] ||= 'desc'
end end
def execute(preload: true) def execute(preload: true)
...@@ -17,7 +20,8 @@ class ReleasesFinder ...@@ -17,7 +20,8 @@ class ReleasesFinder
releases = get_releases releases = get_releases
releases = by_tag(releases) releases = by_tag(releases)
releases = releases.preloaded if preload releases = releases.preloaded if preload
releases.sorted releases = order_releases(releases)
releases
end end
private private
...@@ -57,4 +61,8 @@ class ReleasesFinder ...@@ -57,4 +61,8 @@ class ReleasesFinder
releases.where(tag: params[:tag]) releases.where(tag: params[:tag])
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def order_releases(releases)
releases.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
end
end end
...@@ -30,6 +30,12 @@ class Release < ApplicationRecord ...@@ -30,6 +30,12 @@ class Release < ApplicationRecord
scope :with_project_and_namespace, -> { includes(project: :namespace) } scope :with_project_and_namespace, -> { includes(project: :namespace) }
scope :recent, -> { sorted.limit(MAX_NUMBER_TO_DISPLAY) } scope :recent, -> { sorted.limit(MAX_NUMBER_TO_DISPLAY) }
# Sorting
scope :order_created, -> { reorder('created_at ASC') }
scope :order_created_desc, -> { reorder('created_at DESC') }
scope :order_released, -> { reorder('released_at ASC') }
scope :order_released_desc, -> { reorder('released_at DESC') }
delegate :repository, to: :project delegate :repository, to: :project
MAX_NUMBER_TO_DISPLAY = 3 MAX_NUMBER_TO_DISPLAY = 3
...@@ -92,6 +98,17 @@ class Release < ApplicationRecord ...@@ -92,6 +98,17 @@ class Release < ApplicationRecord
def set_released_at def set_released_at
self.released_at ||= created_at self.released_at ||= created_at
end end
def self.sort_by_attribute(method)
case method.to_s
when 'created_at_asc' then order_created
when 'created_at_desc' then order_created_desc
when 'released_at_asc' then order_released
when 'released_at_desc' then order_released_desc
else
order_created_desc
end
end
end end
Release.prepend_if_ee('EE::Release') Release.prepend_if_ee('EE::Release')
---
title: Add sorting parameters to Releases API
merge_request: 44118
author:
type: added
...@@ -22,6 +22,8 @@ GET /projects/:id/releases ...@@ -22,6 +22,8 @@ GET /projects/:id/releases
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------- | | ------------- | -------------- | -------- | ----------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `order_by` | string | no | The field to use as order. Either `released_at` (default) or `created_at`. |
| `sort` | string | no | The direction of the order. Either `desc` (default) for descending order or `asc` for ascending order. |
Example request: Example request:
......
...@@ -19,9 +19,13 @@ module API ...@@ -19,9 +19,13 @@ module API
end end
params do params do
use :pagination use :pagination
optional :order_by, type: String, values: %w[released_at created_at], default: 'released_at',
desc: 'Return releases ordered by `released_at` or `created_at`.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return releases sorted in `asc` or `desc` order.'
end end
get ':id/releases' do get ':id/releases' do
releases = ::ReleasesFinder.new(user_project, current_user).execute releases = ::ReleasesFinder.new(user_project, current_user, declared_params.slice(:order_by, :sort)).execute
present paginate(releases), with: Entities::Release, current_user: current_user present paginate(releases), with: Entities::Release, current_user: current_user
end end
......
...@@ -77,6 +77,34 @@ RSpec.describe ReleasesFinder do ...@@ -77,6 +77,34 @@ RSpec.describe ReleasesFinder do
expect(subject).to eq([v1_1_0, v1_0_0]) expect(subject).to eq([v1_1_0, v1_0_0])
end end
context 'with sorting parameters' do
before do
v1_1_0.update_attribute(:created_at, 3.days.ago)
end
context 'by default is released_at in descending order' do
it { is_expected.to eq([v1_1_0, v1_0_0]) }
end
context 'released_at in ascending order' do
let(:params) { { sort: 'asc' } }
it { is_expected.to eq([v1_0_0, v1_1_0]) }
end
context 'order by created_at in descending order' do
let(:params) { { order_by: 'created_at' } }
it { is_expected.to eq([v1_0_0, v1_1_0]) }
end
context 'order by created_at in ascending order' do
let(:params) { { order_by: 'created_at', sort: 'asc' } }
it { is_expected.to eq([v1_1_0, v1_0_0]) }
end
end
it_behaves_like 'preload' it_behaves_like 'preload'
it_behaves_like 'when tag is nil' it_behaves_like 'when tag is nil'
it_behaves_like 'when a tag parameter is passed' it_behaves_like 'when a tag parameter is passed'
......
...@@ -118,10 +118,10 @@ Object { ...@@ -118,10 +118,10 @@ Object {
}, },
], ],
"paginationInfo": Object { "paginationInfo": Object {
"endCursor": "eyJpZCI6IjEiLCJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyJ9", "endCursor": "eyJpZCI6IjEifQ",
"hasNextPage": false, "hasNextPage": false,
"hasPreviousPage": false, "hasPreviousPage": false,
"startCursor": "eyJpZCI6IjEiLCJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyJ9", "startCursor": "eyJpZCI6IjEifQ",
}, },
} }
`; `;
...@@ -53,6 +53,49 @@ RSpec.describe API::Releases do ...@@ -53,6 +53,49 @@ RSpec.describe API::Releases do
expect(json_response.second['tag_name']).to eq(release_1.tag) expect(json_response.second['tag_name']).to eq(release_1.tag)
end end
RSpec.shared_examples 'release sorting' do |order_by|
subject { get api(url, access_level), params: { sort: sort, order_by: order_by } }
context "sorting by #{order_by}" do
context 'ascending order' do
let(:sort) { 'asc' }
it 'returns the sorted releases' do
subject
expect(json_response.map { |release| release['name'] }).to eq(releases.map(&:name))
end
end
context 'descending order' do
let(:sort) { 'desc' }
it 'returns the sorted releases' do
subject
expect(json_response.map { |release| release['name'] }).to eq(releases.reverse.map(&:name))
end
end
end
end
context 'return releases in sorted order' do
before do
release_2.update_attribute(:created_at, 3.days.ago)
end
let(:url) { "/projects/#{project.id}/releases" }
let(:access_level) { maintainer }
it_behaves_like 'release sorting', 'released_at' do
let(:releases) { [release_1, release_2] }
end
it_behaves_like 'release sorting', 'created_at' do
let(:releases) { [release_2, release_1] }
end
end
it 'matches response schema' do it 'matches response schema' do
get api("/projects/#{project.id}/releases", maintainer) get api("/projects/#{project.id}/releases", maintainer)
......
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