Commit c1b01902 authored by Alex Kalderimis's avatar Alex Kalderimis

Merge branch '351981-cablett-sort-epics-created-updated-graphql' into 'master'

Add epic filter and sort by last created or updated

See merge request gitlab-org/gitlab!79923
parents a65017e5 ee4ef6fc
......@@ -8810,6 +8810,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="boardepicancestorsauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="boardepicancestorsconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
| <a id="boardepicancestorscreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
| <a id="boardepicancestorscreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="boardepicancestorsenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="boardepicancestorsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="boardepicancestorsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
......@@ -8826,6 +8828,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="boardepicancestorsstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="boardepicancestorsstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="boardepicancestorstimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="boardepicancestorsupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
| <a id="boardepicancestorsupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `BoardEpic.children`
......@@ -8843,6 +8847,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="boardepicchildrenauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="boardepicchildrenconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
| <a id="boardepicchildrencreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
| <a id="boardepicchildrencreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="boardepicchildrenenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="boardepicchildreniid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="boardepicchildreniidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
......@@ -8859,6 +8865,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="boardepicchildrenstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="boardepicchildrenstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="boardepicchildrentimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="boardepicchildrenupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
| <a id="boardepicchildrenupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `BoardEpic.currentUserTodos`
......@@ -10326,6 +10334,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="epicancestorsauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="epicancestorsconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
| <a id="epicancestorscreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
| <a id="epicancestorscreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="epicancestorsenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="epicancestorsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="epicancestorsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
......@@ -10342,6 +10352,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="epicancestorsstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="epicancestorsstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="epicancestorstimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="epicancestorsupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
| <a id="epicancestorsupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `Epic.children`
......@@ -10359,6 +10371,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="epicchildrenauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="epicchildrenconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
| <a id="epicchildrencreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
| <a id="epicchildrencreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="epicchildrenenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="epicchildreniid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="epicchildreniidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
......@@ -10375,6 +10389,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="epicchildrenstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="epicchildrenstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="epicchildrentimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="epicchildrenupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
| <a id="epicchildrenupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `Epic.currentUserTodos`
......@@ -11049,6 +11065,8 @@ Returns [`Epic`](#epic).
| ---- | ---- | ----------- |
| <a id="groupepicauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="groupepicconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
| <a id="groupepiccreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
| <a id="groupepiccreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="groupepicenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="groupepiciid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="groupepiciidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
......@@ -11065,6 +11083,8 @@ Returns [`Epic`](#epic).
| <a id="groupepicstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="groupepicstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="groupepictimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="groupepicupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
| <a id="groupepicupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `Group.epicBoard`
......@@ -11094,6 +11114,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="groupepicsauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="groupepicsconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
| <a id="groupepicscreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
| <a id="groupepicscreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="groupepicsenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="groupepicsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="groupepicsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
......@@ -11110,6 +11132,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupepicsstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="groupepicsstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="groupepicstimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="groupepicsupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
| <a id="groupepicsupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `Group.groupMembers`
......@@ -17041,12 +17065,16 @@ Roadmap sort values.
| Value | Description |
| ----- | ----------- |
| <a id="epicsortcreated_at_asc"></a>`CREATED_AT_ASC` | Sort by created_at by ascending order. |
| <a id="epicsortcreated_at_desc"></a>`CREATED_AT_DESC` | Sort by created_at by descending order. |
| <a id="epicsortend_date_asc"></a>`END_DATE_ASC` | Sort by end date in ascending order. |
| <a id="epicsortend_date_desc"></a>`END_DATE_DESC` | Sort by end date in descending order. |
| <a id="epicsortstart_date_asc"></a>`START_DATE_ASC` | Sort by start date in ascending order. |
| <a id="epicsortstart_date_desc"></a>`START_DATE_DESC` | Sort by start date in descending order. |
| <a id="epicsorttitle_asc"></a>`TITLE_ASC` | Sort by title in ascending order. |
| <a id="epicsorttitle_desc"></a>`TITLE_DESC` | Sort by title in descending order. |
| <a id="epicsortupdated_at_asc"></a>`UPDATED_AT_ASC` | Sort by updated_at by ascending order. |
| <a id="epicsortupdated_at_desc"></a>`UPDATED_AT_DESC` | Sort by updated_at by descending order. |
| <a id="epicsortend_date_asc"></a>`end_date_asc` **{warning-solid}** | **Deprecated** in 13.11. Use END_DATE_ASC. |
| <a id="epicsortend_date_desc"></a>`end_date_desc` **{warning-solid}** | **Deprecated** in 13.11. Use END_DATE_DESC. |
| <a id="epicsortstart_date_asc"></a>`start_date_asc` **{warning-solid}** | **Deprecated** in 13.11. Use START_DATE_ASC. |
......@@ -60,6 +60,19 @@ module Resolvers
required: false,
description: 'Filter by reaction emoji applied by the current user.'
argument :created_after, Types::TimeType,
required: false,
description: 'Epics created after this date.'
argument :created_before, Types::TimeType,
required: false,
description: 'Epics created before this date.'
argument :updated_after, Types::TimeType,
required: false,
description: 'Epics updated after this date.'
argument :updated_before, Types::TimeType,
required: false,
description: 'Epics updated before this date.'
argument :not, ::Types::Epics::NegatedEpicFilterInputType,
required: false,
description: 'Negated epic arguments.'
......
......@@ -18,5 +18,9 @@ module Types
value 'END_DATE_ASC', 'Sort by end date in ascending order.', value: :end_date_asc
value 'TITLE_DESC', 'Sort by title in descending order.', value: :title_desc
value 'TITLE_ASC', 'Sort by title in ascending order.', value: :title_asc
value 'CREATED_AT_ASC', 'Sort by created_at by ascending order.', value: :created_at_asc
value 'CREATED_AT_DESC', 'Sort by created_at by descending order.', value: :created_at_desc
value 'UPDATED_AT_ASC', 'Sort by updated_at by ascending order.', value: :updated_at_asc
value 'UPDATED_AT_DESC', 'Sort by updated_at by descending order.', value: :updated_at_desc
end
end
......@@ -6,6 +6,6 @@ RSpec.describe GitlabSchema.types['EpicSort'] do
it { expect(described_class.graphql_name).to eq('EpicSort') }
it 'exposes all the existing epic sort orders' do
expect(described_class.values.keys).to include(*%w[start_date_desc start_date_asc end_date_desc end_date_asc START_DATE_DESC START_DATE_ASC END_DATE_DESC END_DATE_ASC TITLE_DESC TITLE_ASC])
expect(described_class.values.keys).to include(*%w[start_date_desc start_date_asc end_date_desc end_date_asc START_DATE_DESC START_DATE_ASC END_DATE_DESC END_DATE_ASC TITLE_DESC TITLE_ASC CREATED_AT_ASC CREATED_AT_DESC UPDATED_AT_ASC UPDATED_AT_DESC])
end
end
......@@ -52,6 +52,55 @@ RSpec.describe 'getting epics information' do
end
end
describe 'query for epics by created_at and updated_at' do
let_it_be(:epic1) { create(:epic, group: group, created_at: 2.weeks.ago, updated_at: 10.seconds.ago) }
let_it_be(:epic2) { create(:epic, group: group, created_at: 8.days.ago, updated_at: 2.minutes.ago) }
let_it_be(:epic3) { create(:epic, group: group, created_at: 2.hours.ago, updated_at: 9.minutes.ago) }
let_it_be(:epic4) { create(:epic, group: group, created_at: 15.minutes.ago, updated_at: 14.minutes.ago) }
it 'filters by createdBefore' do
post_graphql(epics_query(group, 'createdBefore', 5.days.ago), current_user: user)
expect_epics_response(epic1, epic2)
end
it 'filters by createdAfter' do
post_graphql(epics_query(group, 'createdAfter', 5.days.ago), current_user: user)
expect_epics_response(epic3, epic4)
end
it 'filters by updatedBefore' do
post_graphql(epics_query(group, 'updatedBefore', 7.minutes.ago), current_user: user)
expect_epics_response(epic3, epic4)
end
it 'filters by updatedAfter' do
post_graphql(epics_query(group, 'updatedAfter', 7.minutes.ago), current_user: user)
expect_epics_response(epic1, epic2)
end
it 'filters by a combination of created parameters provided' do
post_graphql(epics_query_by_hash(group, { 'createdBefore' => Time.zone.now, 'createdAfter' => 20.minutes.ago }), current_user: user)
expect_epics_response(epic4)
end
it 'filters by a combination of created/updated parameters provided' do
post_graphql(epics_query_by_hash(group, { 'updatedBefore' => 3.minutes.ago, 'createdAfter' => 20.minutes.ago }), current_user: user)
expect_epics_response(epic4)
end
it 'returns nothing for impossible parameters' do
post_graphql(epics_query_by_hash(group, { 'createdBefore' => 7.minutes.ago, 'createdAfter' => Time.zone.now }), current_user: user)
expect_epics_response # empty set
end
end
describe 'query for epics by time frame' do
let_it_be(:epic1) { create(:epic, group: group, state: :opened, start_date: "2019-08-13", end_date: "2019-08-20") }
let_it_be(:epic2) { create(:epic, group: group, state: :closed, start_date: "2019-08-13", end_date: "2019-08-21") }
......@@ -213,6 +262,7 @@ RSpec.describe 'getting epics information' do
end
def expect_epics_response(*epics)
epics ||= []
actual_epics = graphql_data['group']['epics']['nodes'].map { |epic| epic['id'] }
expected_epics = epics.map { |epic| epic.to_global_id.to_s }
......
......@@ -66,14 +66,14 @@ RSpec.describe 'Epics through GroupQuery' do
context 'with multiple epics' do
let_it_be(:user2) { create(:user) }
let_it_be(:epic2) { create(:epic, author: user2, group: group, title: 'foo', description: 'bar', created_at: 2.days.ago, updated_at: 3.days.ago, start_date: 3.days.ago, end_date: 3.days.ago ) }
let_it_be(:epic2) { create(:epic, author: user2, group: group, title: 'foo', description: 'bar', created_at: 5.days.ago, updated_at: 3.days.ago, start_date: 3.days.ago, end_date: 3.days.ago ) }
before do
stub_licensed_features(epics: true)
end
context 'with sort and pagination' do
let_it_be(:epic3) { create(:epic, group: group, start_date: 4.days.ago, end_date: 7.days.ago ) }
let_it_be(:epic3) { create(:epic, group: group, start_date: 4.days.ago, end_date: 7.days.ago, created_at: 4.days.ago, updated_at: 1.day.ago ) }
let_it_be(:epic4) { create(:epic, group: group, start_date: 5.days.ago, end_date: 6.days.ago ) }
let(:current_user) { user }
......@@ -126,6 +126,38 @@ RSpec.describe 'Epics through GroupQuery' do
let(:all_records) { global_ids(epic2, epic, epic4, epic3) }
end
end
context 'with created_at_asc' do
it_behaves_like 'sorted paginated query', is_reversible: true do
let(:sort_param) { :CREATED_AT_ASC }
let(:first_param) { 2 }
let(:all_records) { global_ids(epic2, epic3, epic, epic4) }
end
end
context 'with created_at_desc' do
it_behaves_like 'sorted paginated query', is_reversible: true do
let(:sort_param) { :CREATED_AT_DESC }
let(:first_param) { 2 }
let(:all_records) { global_ids(epic4, epic, epic3, epic2) }
end
end
context 'with updated_at_asc' do
it_behaves_like 'sorted paginated query', is_reversible: true do
let(:sort_param) { :UPDATED_AT_ASC }
let(:first_param) { 2 }
let(:all_records) { global_ids(epic2, epic, epic3, epic4) }
end
end
context 'with updated_at_desc' do
it_behaves_like 'sorted paginated query', is_reversible: true do
let(:sort_param) { :UPDATED_AT_DESC }
let(:first_param) { 2 }
let(:all_records) { global_ids(epic4, epic3, epic, epic2) }
end
end
end
it 'sorts by created_at descending by default' do
......@@ -212,14 +244,16 @@ RSpec.describe 'Epics through GroupQuery' do
end
end
context 'with search params' do
it 'returns only matching epics' do
filter_params = { search: 'bar', in: [:DESCRIPTION] }
graphql_query = query(filter_params)
context 'filter' do
context 'with search params' do
it 'returns only matching epics' do
filter_params = { search: 'bar', in: [:DESCRIPTION] }
graphql_query = query(filter_params)
post_graphql(graphql_query, current_user: user)
post_graphql(graphql_query, current_user: user)
expect_array_response([epic2.to_global_id.to_s])
expect_array_response([epic2.to_global_id.to_s])
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