Commit 012093ff authored by Mark Chao's avatar Mark Chao

Merge branch 'nfriend-add-graphql-release-sorting' into 'master'

Update GraphQL endpoint to support sorting releases

See merge request gitlab-org/gitlab!45577
parents e05c66a4 a715d2de
......@@ -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