Commit a715d2de authored by Nathan Friend's avatar Nathan Friend Committed by Mark Chao

Add ability to sort releases from GraphQL

This commit updates our GraphQL endpiont to allow releases to be
sorted by released_at or created_at.
parent c88dcfa3
......@@ -4,6 +4,10 @@ module Resolvers
class ReleasesResolver < BaseResolver
type Types::ReleaseType.connection_type, null: true
argument :sort, Types::ReleaseSortEnum,
required: false, default_value: :released_at_desc,
description: 'Sort releases by this criteria'
alias_method :project, :object
# This resolver has a custom singular resolver
......@@ -11,12 +15,20 @@ module Resolvers
Resolvers::ReleaseResolver
end
def resolve(**args)
SORT_TO_PARAMS_MAP = {
released_at_desc: { order_by: 'released_at', sort: 'desc' },
released_at_asc: { order_by: 'released_at', sort: 'asc' },
created_desc: { order_by: 'created_at', sort: 'desc' },
created_asc: { order_by: 'created_at', sort: 'asc' }
}.freeze
def resolve(sort:)
return unless Feature.enabled?(:graphql_release_data, project, default_enabled: true)
ReleasesFinder.new(
project,
current_user
current_user,
SORT_TO_PARAMS_MAP[sort]
).execute
end
end
......
# frozen_string_literal: true
module Types
# Not inheriting from Types::SortEnum since we only want
# to implement a subset of the sort values it defines.
class ReleaseSortEnum < BaseEnum
graphql_name 'ReleaseSort'
description 'Values for sorting releases'
# Borrowed from Types::SortEnum
# These values/descriptions should stay in-sync as much as possible.
value 'CREATED_DESC', 'Created at descending order', value: :created_desc
value 'CREATED_ASC', 'Created at ascending order', value: :created_asc
value 'RELEASED_AT_DESC', 'Released at by descending order', value: :released_at_desc
value 'RELEASED_AT_ASC', 'Released at by ascending order', value: :released_at_asc
end
end
---
title: Allow sorting of releases from GraphQL
merge_request: 45577
author:
type: added
......@@ -14745,6 +14745,11 @@ type Project {
Returns the last _n_ elements from the list.
"""
last: Int
"""
Sort releases by this criteria
"""
sort: ReleaseSort = RELEASED_AT_DESC
): ReleaseConnection
"""
......@@ -16519,6 +16524,31 @@ type ReleaseLinks {
selfUrl: String
}
"""
Values for sorting releases
"""
enum ReleaseSort {
"""
Created at ascending order
"""
CREATED_ASC
"""
Created at descending order
"""
CREATED_DESC
"""
Released at by ascending order
"""
RELEASED_AT_ASC
"""
Released at by descending order
"""
RELEASED_AT_DESC
}
"""
Represents the source code attached to a release in a particular format
"""
......
......@@ -42602,6 +42602,16 @@
"name": "releases",
"description": "Releases of the project",
"args": [
{
"name": "sort",
"description": "Sort releases by this criteria",
"type": {
"kind": "ENUM",
"name": "ReleaseSort",
"ofType": null
},
"defaultValue": "RELEASED_AT_DESC"
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
......@@ -47517,6 +47527,41 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "ReleaseSort",
"description": "Values for sorting releases",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "CREATED_DESC",
"description": "Created at descending order",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "CREATED_ASC",
"description": "Created at ascending order",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "RELEASED_AT_DESC",
"description": "Released at by descending order",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "RELEASED_AT_ASC",
"description": "Released at by ascending order",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseSource",
......@@ -3643,6 +3643,17 @@ Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`.
| `PACKAGE` | Package link type |
| `RUNBOOK` | Runbook link type |
### ReleaseSort
Values for sorting releases.
| Value | Description |
| ----- | ----------- |
| `CREATED_ASC` | Created at ascending order |
| `CREATED_DESC` | Created at descending order |
| `RELEASED_AT_ASC` | Released at by ascending order |
| `RELEASED_AT_DESC` | Released at by descending order |
### RequirementState
State of a requirement.
......
......@@ -5,12 +5,19 @@ require 'spec_helper'
RSpec.describe Resolvers::ReleasesResolver do
include GraphqlHelpers
let_it_be(:today) { Time.now }
let_it_be(:yesterday) { today - 1.day }
let_it_be(:tomorrow) { today + 1.day }
let_it_be(:project) { create(:project, :private) }
let_it_be(:release_v1) { create(:release, project: project, tag: 'v1.0.0') }
let_it_be(:release_v2) { create(:release, project: project, tag: 'v2.0.0') }
let_it_be(:release_v1) { create(:release, project: project, tag: 'v1.0.0', released_at: yesterday, created_at: tomorrow) }
let_it_be(:release_v2) { create(:release, project: project, tag: 'v2.0.0', released_at: today, created_at: yesterday) }
let_it_be(:release_v3) { create(:release, project: project, tag: 'v3.0.0', released_at: tomorrow, created_at: today) }
let_it_be(:developer) { create(:user) }
let_it_be(:public_user) { create(:user) }
let(:args) { { sort: :released_at_desc } }
before do
project.add_developer(developer)
end
......@@ -28,7 +35,41 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:current_user) { developer }
it 'returns all releases associated to the project' do
expect(resolve_releases).to eq([release_v1, release_v2])
expect(resolve_releases).to eq([release_v3, release_v2, release_v1])
end
describe 'sorting behavior' do
context 'with sort: :released_at_desc' do
let(:args) { { sort: :released_at_desc } }
it 'returns the releases ordered by released_at in descending order' do
expect(resolve_releases).to eq([release_v3, release_v2, release_v1])
end
end
context 'with sort: :released_at_asc' do
let(:args) { { sort: :released_at_asc } }
it 'returns the releases ordered by released_at in ascending order' do
expect(resolve_releases).to eq([release_v1, release_v2, release_v3])
end
end
context 'with sort: :created_desc' do
let(:args) { { sort: :created_desc } }
it 'returns the releases ordered by created_at in descending order' do
expect(resolve_releases).to eq([release_v1, release_v3, release_v2])
end
end
context 'with sort: :created_asc' do
let(:args) { { sort: :created_asc } }
it 'returns the releases ordered by created_at in ascending order' do
expect(resolve_releases).to eq([release_v2, release_v3, release_v1])
end
end
end
end
end
......@@ -37,6 +78,6 @@ RSpec.describe Resolvers::ReleasesResolver do
def resolve_releases
context = { current_user: current_user }
resolve(described_class, obj: project, args: {}, ctx: context)
resolve(described_class, obj: project, args: args, ctx: context)
end
end
......@@ -300,4 +300,77 @@ RSpec.describe 'Query.project(fullPath).releases()' do
it_behaves_like 'no access to any release data'
end
end
describe 'sorting behavior' do
let_it_be(:today) { Time.now }
let_it_be(:yesterday) { today - 1.day }
let_it_be(:tomorrow) { today + 1.day }
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:release_v1) { create(:release, project: project, tag: 'v1', released_at: yesterday, created_at: tomorrow) }
let_it_be(:release_v2) { create(:release, project: project, tag: 'v2', released_at: today, created_at: yesterday) }
let_it_be(:release_v3) { create(:release, project: project, tag: 'v3', released_at: tomorrow, created_at: today) }
let(:current_user) { developer }
let(:params) { nil }
let(:sorted_tags) do
graphql_data.dig('project', 'releases', 'nodes').map { |release| release['tagName'] }
end
let(:query) do
graphql_query_for(:project, { fullPath: project.full_path },
%{
releases#{params ? "(#{params})" : ""} {
nodes {
tagName
}
}
})
end
before do
post_query
end
context 'when no sort: parameter is provided' do
it 'returns the results with the default sort applied (sort: RELEASED_AT_DESC)' do
expect(sorted_tags).to eq(%w(v3 v2 v1))
end
end
context 'with sort: RELEASED_AT_DESC' do
let(:params) { 'sort: RELEASED_AT_DESC' }
it 'returns the releases ordered by released_at in descending order' do
expect(sorted_tags).to eq(%w(v3 v2 v1))
end
end
context 'with sort: RELEASED_AT_ASC' do
let(:params) { 'sort: RELEASED_AT_ASC' }
it 'returns the releases ordered by released_at in ascending order' do
expect(sorted_tags).to eq(%w(v1 v2 v3))
end
end
context 'with sort: CREATED_DESC' do
let(:params) { 'sort: CREATED_DESC' }
it 'returns the releases ordered by created_at in descending order' do
expect(sorted_tags).to eq(%w(v1 v3 v2))
end
end
context 'with sort: CREATED_ASC' do
let(:params) { 'sort: CREATED_ASC' }
it 'returns the releases ordered by created_at in ascending order' do
expect(sorted_tags).to eq(%w(v2 v3 v1))
end
end
end
end
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