Commit d6d27990 authored by Jake Burden's avatar Jake Burden Committed by Michael Kozono

Add sorting parameters to Releases API

Adds sort and sort_by params to Release API
Adds sort and sort_by params to ReleaseFinder

Add order_by and sort attributes to Release API docs

Update release/util snapshot
parent b4141ba1
......@@ -9,6 +9,9 @@ class ReleasesFinder
@parent = parent
@current_user = current_user
@params = params
params[:order_by] ||= 'released_at'
params[:sort] ||= 'desc'
end
def execute(preload: true)
......@@ -17,7 +20,8 @@ class ReleasesFinder
releases = get_releases
releases = by_tag(releases)
releases = releases.preloaded if preload
releases.sorted
releases = order_releases(releases)
releases
end
private
......@@ -57,4 +61,8 @@ class ReleasesFinder
releases.where(tag: params[:tag])
end
# rubocop: enable CodeReuse/ActiveRecord
def order_releases(releases)
releases.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
end
end
......@@ -30,6 +30,12 @@ class Release < ApplicationRecord
scope :with_project_and_namespace, -> { includes(project: :namespace) }
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
MAX_NUMBER_TO_DISPLAY = 3
......@@ -92,6 +98,17 @@ class Release < ApplicationRecord
def set_released_at
self.released_at ||= created_at
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
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
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------- |
| `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:
......
......@@ -19,9 +19,13 @@ module API
end
params do
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
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
end
......
......@@ -77,6 +77,34 @@ RSpec.describe ReleasesFinder do
expect(subject).to eq([v1_1_0, v1_0_0])
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 'when tag is nil'
it_behaves_like 'when a tag parameter is passed'
......
......@@ -118,10 +118,10 @@ Object {
},
],
"paginationInfo": Object {
"endCursor": "eyJpZCI6IjEiLCJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyJ9",
"endCursor": "eyJpZCI6IjEifQ",
"hasNextPage": false,
"hasPreviousPage": false,
"startCursor": "eyJpZCI6IjEiLCJyZWxlYXNlZF9hdCI6IjIwMTgtMTItMTAgMDA6MDA6MDAuMDAwMDAwMDAwIFVUQyJ9",
"startCursor": "eyJpZCI6IjEifQ",
},
}
`;
......@@ -53,6 +53,49 @@ RSpec.describe API::Releases do
expect(json_response.second['tag_name']).to eq(release_1.tag)
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
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