BigW Consortium Gitlab

Commit 5a1da682 by Sean McGivern

Merge branch '41616-api-issues-between-date' into 'master'

Resolve "API support for selecting only updated issues before/after a given date" Closes #41616 See merge request gitlab-org/gitlab-ce!17417
parents cb9148fc 168ff285
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
# non_archived: boolean # non_archived: boolean
# iids: integer[] # iids: integer[]
# my_reaction_emoji: string # my_reaction_emoji: string
# created_after: datetime
# created_before: datetime
# updated_after: datetime
# updated_before: datetime
# #
class IssuableFinder class IssuableFinder
prepend FinderWithCrossProjectAccess prepend FinderWithCrossProjectAccess
...@@ -79,6 +83,7 @@ class IssuableFinder ...@@ -79,6 +83,7 @@ class IssuableFinder
def filter_items(items) def filter_items(items)
items = by_scope(items) items = by_scope(items)
items = by_created_at(items) items = by_created_at(items)
items = by_updated_at(items)
items = by_state(items) items = by_state(items)
items = by_group(items) items = by_group(items)
items = by_search(items) items = by_search(items)
...@@ -283,6 +288,13 @@ class IssuableFinder ...@@ -283,6 +288,13 @@ class IssuableFinder
end end
end end
def by_updated_at(items)
items = items.updated_after(params[:updated_after]) if params[:updated_after].present?
items = items.updated_before(params[:updated_before]) if params[:updated_before].present?
items
end
def by_state(items) def by_state(items)
case params[:state].to_s case params[:state].to_s
when 'closed' when 'closed'
......
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
# my_reaction_emoji: string # my_reaction_emoji: string
# public_only: boolean # public_only: boolean
# due_date: date or '0', '', 'overdue', 'week', or 'month' # due_date: date or '0', '', 'overdue', 'week', or 'month'
# created_after: datetime
# created_before: datetime
# updated_after: datetime
# updated_before: datetime
# #
class IssuesFinder < IssuableFinder class IssuesFinder < IssuableFinder
CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER
......
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
# my_reaction_emoji: string # my_reaction_emoji: string
# source_branch: string # source_branch: string
# target_branch: string # target_branch: string
# created_after: datetime
# created_before: datetime
# updated_after: datetime
# updated_before: datetime
# #
class MergeRequestsFinder < IssuableFinder class MergeRequestsFinder < IssuableFinder
def klass def klass
......
...@@ -19,6 +19,7 @@ module Issuable ...@@ -19,6 +19,7 @@ module Issuable
include AfterCommitQueue include AfterCommitQueue
include Sortable include Sortable
include CreatedAtFilterable include CreatedAtFilterable
include UpdatedAtFilterable
# This object is used to gather issuable meta data for displaying # This object is used to gather issuable meta data for displaying
# upvotes, downvotes, notes and closing merge requests count for issues and merge requests # upvotes, downvotes, notes and closing merge requests count for issues and merge requests
......
module UpdatedAtFilterable
extend ActiveSupport::Concern
included do
scope :updated_before, ->(date) { where(scoped_table[:updated_at].lteq(date)) }
scope :updated_after, ->(date) { where(scoped_table[:updated_at].gteq(date)) }
def self.scoped_table
arel_table.alias(table_name)
end
end
end
---
title: Adds updated_at filter to issues and merge_requests API
merge_request: 17417
author: Jacopo Beschi @jacopo-beschi
type: added
...@@ -46,6 +46,10 @@ GET /issues?my_reaction_emoji=star ...@@ -46,6 +46,10 @@ GET /issues?my_reaction_emoji=star
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search issues against their `title` and `description` | | `search` | string | no | Search issues against their `title` and `description` |
| `created_after` | datetime | no | Return issues created on or after the given time |
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
```bash ```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/issues curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/issues
...@@ -152,6 +156,10 @@ GET /groups/:id/issues?my_reaction_emoji=star ...@@ -152,6 +156,10 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search group issues against their `title` and `description` | | `search` | string | no | Search group issues against their `title` and `description` |
| `created_after` | datetime | no | Return issues created on or after the given time |
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
```bash ```bash
...@@ -259,8 +267,10 @@ GET /projects/:id/issues?my_reaction_emoji=star ...@@ -259,8 +267,10 @@ GET /projects/:id/issues?my_reaction_emoji=star
| `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search project issues against their `title` and `description` | | `search` | string | no | Search project issues against their `title` and `description` |
| `created_after` | datetime | no | Return issues created after the given time (inclusive) | | `created_after` | datetime | no | Return issues created on or after the given time |
| `created_before` | datetime | no | Return issues created before the given time (inclusive) | | `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
```bash ```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues
......
...@@ -41,8 +41,10 @@ Parameters: ...@@ -41,8 +41,10 @@ Parameters:
| `milestone` | string | no | Return merge requests for a specific milestone | | `milestone` | string | no | Return merge requests for a specific milestone |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request | | `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels | | `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created after the given time (inclusive) | | `created_after` | datetime | no | Return merge requests created on or after the given time |
| `created_before` | datetime | no | Return merge requests created before the given time (inclusive) | | `created_before` | datetime | no | Return merge requests created on or before the given time |
| `updated_after` | datetime | no | Return merge requests updated on or after the given time |
| `updated_before` | datetime | no | Return merge requests updated on or before the given time |
| `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`. Defaults to `created-by-me` | | `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`. Defaults to `created-by-me` |
| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me` | | `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me` |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` | | `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` |
...@@ -158,8 +160,10 @@ Parameters: ...@@ -158,8 +160,10 @@ Parameters:
| `milestone` | string | no | Return merge requests for a specific milestone | | `milestone` | string | no | Return merge requests for a specific milestone |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request | | `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels | | `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created after the given time (inclusive) | | `created_after` | datetime | no | Return merge requests created on or after the given time |
| `created_before` | datetime | no | Return merge requests created before the given time (inclusive) | | `created_before` | datetime | no | Return merge requests created on or before the given time |
| `updated_after` | datetime | no | Return merge requests updated on or after the given time |
| `updated_before` | datetime | no | Return merge requests updated on or before the given time |
| `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13060] in GitLab 9.5)_ | | `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13060] in GitLab 9.5)_ |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ | | `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ | | `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
......
...@@ -32,6 +32,8 @@ module API ...@@ -32,6 +32,8 @@ module API
optional :search, type: String, desc: 'Search issues for text present in the title or description' optional :search, type: String, desc: 'Search issues for text present in the title or description'
optional :created_after, type: DateTime, desc: 'Return issues created after the specified time' optional :created_after, type: DateTime, desc: 'Return issues created after the specified time'
optional :created_before, type: DateTime, desc: 'Return issues created before the specified time' optional :created_before, type: DateTime, desc: 'Return issues created before the specified time'
optional :updated_after, type: DateTime, desc: 'Return issues updated after the specified time'
optional :updated_before, type: DateTime, desc: 'Return issues updated before the specified time'
optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID' optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID'
optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID' optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me all], optional :scope, type: String, values: %w[created-by-me assigned-to-me all],
......
...@@ -42,6 +42,8 @@ module API ...@@ -42,6 +42,8 @@ module API
optional :labels, type: String, desc: 'Comma-separated list of label names' optional :labels, type: String, desc: 'Comma-separated list of label names'
optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time' optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time' optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
optional :updated_after, type: DateTime, desc: 'Return merge requests updated after the specified time'
optional :updated_before, type: DateTime, desc: 'Return merge requests updated before the specified time'
optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request' optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID' optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID'
optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID' optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID'
......
...@@ -10,9 +10,9 @@ describe IssuesFinder do ...@@ -10,9 +10,9 @@ describe IssuesFinder do
set(:project3) { create(:project, group: subgroup) } set(:project3) { create(:project, group: subgroup) }
set(:milestone) { create(:milestone, project: project1) } set(:milestone) { create(:milestone, project: project1) }
set(:label) { create(:label, project: project2) } set(:label) { create(:label, project: project2) }
set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab', created_at: 1.week.ago) } set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab', created_at: 1.week.ago, updated_at: 1.week.ago) }
set(:issue2) { create(:issue, author: user, assignees: [user], project: project2, description: 'gitlab') } set(:issue2) { create(:issue, author: user, assignees: [user], project: project2, description: 'gitlab', created_at: 1.week.from_now, updated_at: 1.week.from_now) }
set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki', created_at: 1.week.from_now) } set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki', created_at: 2.weeks.from_now, updated_at: 2.weeks.from_now) }
set(:issue4) { create(:issue, project: project3) } set(:issue4) { create(:issue, project: project3) }
set(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue1) } set(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue1) }
set(:award_emoji2) { create(:award_emoji, name: 'thumbsup', user: user2, awardable: issue2) } set(:award_emoji2) { create(:award_emoji, name: 'thumbsup', user: user2, awardable: issue2) }
...@@ -275,12 +275,46 @@ describe IssuesFinder do ...@@ -275,12 +275,46 @@ describe IssuesFinder do
end end
context 'through created_before' do context 'through created_before' do
let(:params) { { created_before: issue1.created_at + 1.second } } let(:params) { { created_before: issue1.created_at } }
it 'returns issues created on or before the given date' do it 'returns issues created on or before the given date' do
expect(issues).to contain_exactly(issue1) expect(issues).to contain_exactly(issue1)
end end
end end
context 'through created_after and created_before' do
let(:params) { { created_after: issue2.created_at, created_before: issue3.created_at } }
it 'returns issues created between the given dates' do
expect(issues).to contain_exactly(issue2, issue3)
end
end
end
context 'filtering by updated_at' do
context 'through updated_after' do
let(:params) { { updated_after: issue3.updated_at } }
it 'returns issues updated on or after the given date' do
expect(issues).to contain_exactly(issue3)
end
end
context 'through updated_before' do
let(:params) { { updated_before: issue1.updated_at } }
it 'returns issues updated on or before the given date' do
expect(issues).to contain_exactly(issue1)
end
end
context 'through updated_after and updated_before' do
let(:params) { { updated_after: issue2.updated_at, updated_before: issue3.updated_at } }
it 'returns issues updated between the given dates' do
expect(issues).to contain_exactly(issue2, issue3)
end
end
end end
context 'filtering by reaction name' do context 'filtering by reaction name' do
......
...@@ -109,7 +109,7 @@ describe MergeRequestsFinder do ...@@ -109,7 +109,7 @@ describe MergeRequestsFinder do
end end
end end
context 'with created_after and created_before params' do context 'filtering by created_at/updated_at' do
let(:new_project) { create(:project, forked_from_project: project1) } let(:new_project) { create(:project, forked_from_project: project1) }
let!(:new_merge_request) do let!(:new_merge_request) do
...@@ -117,15 +117,18 @@ describe MergeRequestsFinder do ...@@ -117,15 +117,18 @@ describe MergeRequestsFinder do
:simple, :simple,
author: user, author: user,
created_at: 1.week.from_now, created_at: 1.week.from_now,
updated_at: 1.week.from_now,
source_project: new_project, source_project: new_project,
target_project: project1) target_project: new_project)
end end
let!(:old_merge_request) do let!(:old_merge_request) do
create(:merge_request, create(:merge_request,
:simple, :simple,
author: user, author: user,
source_branch: 'feature_1',
created_at: 1.week.ago, created_at: 1.week.ago,
updated_at: 1.week.ago,
source_project: new_project, source_project: new_project,
target_project: new_project) target_project: new_project)
end end
...@@ -135,7 +138,7 @@ describe MergeRequestsFinder do ...@@ -135,7 +138,7 @@ describe MergeRequestsFinder do
end end
it 'filters by created_after' do it 'filters by created_after' do
params = { project_id: project1.id, created_after: new_merge_request.created_at } params = { project_id: new_project.id, created_after: new_merge_request.created_at }
merge_requests = described_class.new(user, params).execute merge_requests = described_class.new(user, params).execute
...@@ -143,12 +146,52 @@ describe MergeRequestsFinder do ...@@ -143,12 +146,52 @@ describe MergeRequestsFinder do
end end
it 'filters by created_before' do it 'filters by created_before' do
params = { project_id: new_project.id, created_before: old_merge_request.created_at + 1.second } params = { project_id: new_project.id, created_before: old_merge_request.created_at }
merge_requests = described_class.new(user, params).execute merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(old_merge_request) expect(merge_requests).to contain_exactly(old_merge_request)
end end
it 'filters by created_after and created_before' do
params = {
project_id: new_project.id,
created_after: old_merge_request.created_at,
created_before: new_merge_request.created_at
}
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(old_merge_request, new_merge_request)
end
it 'filters by updated_after' do
params = { project_id: new_project.id, updated_after: new_merge_request.updated_at }
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(new_merge_request)
end
it 'filters by updated_before' do
params = { project_id: new_project.id, updated_before: old_merge_request.updated_at }
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(old_merge_request)
end
it 'filters by updated_after and updated_before' do
params = {
project_id: new_project.id,
updated_after: old_merge_request.updated_at,
updated_before: new_merge_request.updated_at
}
merge_requests = described_class.new(user, params).execute
expect(merge_requests).to contain_exactly(old_merge_request, new_merge_request)
end
end end
end end
......
...@@ -163,6 +163,42 @@ describe API::Issues do ...@@ -163,6 +163,42 @@ describe API::Issues do
expect(first_issue['id']).to eq(issue.id) expect(first_issue['id']).to eq(issue.id)
end end
context 'filtering before a specific date' do
let!(:issue2) { create(:issue, project: project, author: user, created_at: Date.new(2000, 1, 1), updated_at: Date.new(2000, 1, 1)) }
it 'returns issues created before a specific date' do
get api('/issues?created_before=2000-01-02T00:00:00.060Z', user)
expect(json_response.size).to eq(1)
expect(first_issue['id']).to eq(issue2.id)
end
it 'returns issues updated before a specific date' do
get api('/issues?updated_before=2000-01-02T00:00:00.060Z', user)
expect(json_response.size).to eq(1)
expect(first_issue['id']).to eq(issue2.id)
end
end
context 'filtering after a specific date' do
let!(:issue2) { create(:issue, project: project, author: user, created_at: 1.week.from_now, updated_at: 1.week.from_now) }
it 'returns issues created after a specific date' do
get api("/issues?created_after=#{issue2.created_at}", user)
expect(json_response.size).to eq(1)
expect(first_issue['id']).to eq(issue2.id)
end
it 'returns issues updated after a specific date' do
get api("/issues?updated_after=#{issue2.updated_at}", user)
expect(json_response.size).to eq(1)
expect(first_issue['id']).to eq(issue2.id)
end
end
it 'returns an array of labeled issues' do it 'returns an array of labeled issues' do
get api("/issues", user), labels: label.title get api("/issues", user), labels: label.title
......
...@@ -172,6 +172,42 @@ describe API::MergeRequests do ...@@ -172,6 +172,42 @@ describe API::MergeRequests do
end end
end end
it 'returns merge requests created before a specific date' do
merge_request2 = create(:merge_request, :simple, source_project: project, target_project: project, source_branch: 'feature_1', created_at: Date.new(2000, 1, 1))
get api('/merge_requests?created_before=2000-01-02T00:00:00.060Z', user)
expect(json_response.size).to eq(1)
expect(json_response.first['id']).to eq(merge_request2.id)
end
it 'returns merge requests created after a specific date' do
merge_request2 = create(:merge_request, :simple, source_project: project, target_project: project, source_branch: 'feature_1', created_at: 1.week.from_now)
get api("/merge_requests?created_after=#{merge_request2.created_at}", user)
expect(json_response.size).to eq(1)
expect(json_response.first['id']).to eq(merge_request2.id)
end
it 'returns merge requests updated before a specific date' do
merge_request2 = create(:merge_request, :simple, source_project: project, target_project: project, source_branch: 'feature_1', updated_at: Date.new(2000, 1, 1))
get api('/merge_requests?updated_before=2000-01-02T00:00:00.060Z', user)
expect(json_response.size).to eq(1)
expect(json_response.first['id']).to eq(merge_request2.id)
end
it 'returns merge requests updated after a specific date' do
merge_request2 = create(:merge_request, :simple, source_project: project, target_project: project, source_branch: 'feature_1', updated_at: 1.week.from_now)
get api("/merge_requests?updated_after=#{merge_request2.updated_at}", user)
expect(json_response.size).to eq(1)
expect(json_response.first['id']).to eq(merge_request2.id)
end
context 'search params' do context 'search params' do
before do before do
merge_request.update(title: 'Search title', description: 'Search description') merge_request.update(title: 'Search title', description: 'Search description')
......
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