Skip to content
Snippets Groups Projects
Commit c5c9dce2 authored by Felipe Artur's avatar Felipe Artur
Browse files

Add group milestones API endpoint

parent 05329d4a
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -20,7 +20,7 @@
#
class IssuableFinder
include CreatedAtFilter
NONE = '0'.freeze
IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze
 
Loading
Loading
Loading
Loading
@@ -29,7 +29,8 @@ following locations:
- [Keys](keys.md)
- [Labels](labels.md)
- [Merge Requests](merge_requests.md)
- [Milestones](milestones.md)
- [Project milestones](milestones.md)
- [Group milestones](group_milestones.md)
- [Namespaces](namespaces.md)
- [Notes](notes.md) (comments)
- [Notification settings](notification_settings.md)
Loading
Loading
# Group milestones API
## List group milestones
Returns a list of group milestones.
```
GET /groups/:id/milestones
GET /groups/:id/milestones?iids=42
GET /groups/:id/milestones?iids[]=42&iids[]=43
GET /groups/:id/milestones?state=active
GET /groups/:id/milestones?state=closed
GET /groups/:id/milestones?search=version
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `iids` | Array[integer] | optional | Return only the milestones having the given `iids` |
| `state` | string | optional | Return only `active` or `closed` milestones` |
| `search` | string | optional | Return only milestones with a title or description matching the provided string |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/5/milestones
```
Example Response:
```json
[
{
"id": 12,
"iid": 3,
"group_id": 16,
"title": "10.0",
"description": "Version",
"due_date": "2013-11-29",
"start_date": "2013-11-10",
"state": "active",
"updated_at": "2013-10-02T09:24:18Z",
"created_at": "2013-10-02T09:24:18Z"
}
]
```
## Get single milestone
Gets a single group milestone.
```
GET /groups/:id/milestones/:milestone_id
```
Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of the group milestone
## Create new milestone
Creates a new group milestone.
```
POST /groups/:id/milestones
```
Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user
- `title` (required) - The title of an milestone
- `description` (optional) - The description of the milestone
- `due_date` (optional) - The due date of the milestone
- `start_date` (optional) - The start date of the milestone
## Edit milestone
Updates an existing group milestone.
```
PUT /groups/:id/milestones/:milestone_id
```
Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of a group milestone
- `title` (optional) - The title of a milestone
- `description` (optional) - The description of a milestone
- `due_date` (optional) - The due date of the milestone
- `start_date` (optional) - The start date of the milestone
- `state_event` (optional) - The state event of the milestone (close|activate)
## Get all issues assigned to a single milestone
Gets all issues assigned to a single group milestone.
```
GET /groups/:id/milestones/:milestone_id/issues
```
Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of a group milestone
## Get all merge requests assigned to a single milestone
Gets all merge requests assigned to a single group milestone.
```
GET /groups/:id/milestones/:milestone_id/merge_requests
```
Parameters:
- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of a group milestone
Loading
Loading
@@ -109,7 +109,8 @@ module API
mount ::API::Members
mount ::API::MergeRequestDiffs
mount ::API::MergeRequests
mount ::API::Milestones
mount ::API::ProjectMilestones
mount ::API::GroupMilestones
mount ::API::Namespaces
mount ::API::Notes
mount ::API::NotificationSettings
Loading
Loading
Loading
Loading
@@ -269,8 +269,8 @@ module API
 
class Milestone < Grape::Entity
expose :id, :iid
expose(:project_id) { |entity| entity&.project_id }
expose(:group_id) { |entity| entity&.group_id }
expose :project_id, if: -> (entity, options) { entity&.project_id }
expose :group_id, if: -> (entity, options) { entity&.group_id }
expose :title, :description
expose :state, :created_at, :updated_at
expose :due_date
Loading
Loading
module API
class GroupMilestones < Grape::API
include MilestoneResponses
include PaginationParams
before do
authenticate!
end
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups, requirements: { id: %r{[^/]+} } do
desc 'Get a list of group milestones' do
success Entities::Milestone
end
params do
use :list_params
end
get ":id/milestones" do
list_milestones_for(user_group)
end
desc 'Get a single group milestone' do
success Entities::Milestone
end
params do
requires :milestone_id, type: Integer, desc: 'The ID of a group milestone'
end
get ":id/milestones/:milestone_id" do
authorize! :read_group, user_group
get_milestone_for(user_group)
end
desc 'Create a new group milestone' do
success Entities::Milestone
end
params do
requires :title, type: String, desc: 'The title of the milestone'
use :optional_params
end
post ":id/milestones" do
authorize! :admin_milestones, user_group
create_milestone_for(user_group)
end
desc 'Update an existing group milestone' do
success Entities::Milestone
end
params do
use :update_params
end
put ":id/milestones/:milestone_id" do
authorize! :admin_milestones, user_group
update_milestone_for(user_group)
end
desc 'Get all issues for a single group milestone' do
success Entities::IssueBasic
end
params do
requires :milestone_id, type: Integer, desc: 'The ID of a group milestone'
use :pagination
end
get ":id/milestones/:milestone_id/issues" do
milestone_issuables_for(user_group, :issue)
end
desc 'Get all merge requests for a single group milestone' do
detail 'This feature was introduced in GitLab 9.'
success Entities::MergeRequestBasic
end
params do
requires :milestone_id, type: Integer, desc: 'The ID of a group milestone'
use :pagination
end
get ':id/milestones/:milestone_id/merge_requests' do
milestone_issuables_for(user_group, :merge_request)
end
end
end
end
Loading
Loading
@@ -25,6 +25,10 @@ module API
initial_current_user != current_user
end
 
def user_group
@group ||= find_group!(params[:id])
end
def user_project
@project ||= find_project!(params[:id])
end
Loading
Loading
module API
module MilestoneResponses
extend ActiveSupport::Concern
included do
helpers do
params :optional_params do
optional :description, type: String, desc: 'The description of the milestone'
optional :due_date, type: String, desc: 'The due date of the milestone. The ISO 8601 date format (%Y-%m-%d)'
optional :start_date, type: String, desc: 'The start date of the milestone. The ISO 8601 date format (%Y-%m-%d)'
end
params :list_params do
optional :state, type: String, values: %w[active closed all], default: 'all',
desc: 'Return "active", "closed", or "all" milestones'
optional :iids, type: Array[Integer], desc: 'The IIDs of the milestones'
optional :search, type: String, desc: 'The search criteria for the title or description of the milestone'
use :pagination
end
params :update_params do
requires :milestone_id, type: Integer, desc: 'The milestone ID number'
optional :title, type: String, desc: 'The title of the milestone'
optional :state_event, type: String, values: %w[close activate],
desc: 'The state event of the milestone '
use :optional_params
at_least_one_of :title, :description, :due_date, :state_event
end
def list_milestones_for(parent)
milestones = parent.milestones
milestones = Milestone.filter_by_state(milestones, params[:state])
milestones = filter_by_iid(milestones, params[:iids]) if params[:iids].present?
milestones = filter_by_search(milestones, params[:search]) if params[:search]
present paginate(milestones), with: Entities::Milestone
end
def get_milestone_for(parent)
milestone = parent.milestones.find(params[:milestone_id])
present milestone, with: Entities::Milestone
end
def create_milestone_for(parent)
milestone = ::Milestones::CreateService.new(parent, current_user, declared_params).execute
if milestone.valid?
present milestone, with: Entities::Milestone
else
render_api_error!("Failed to create milestone #{milestone.errors.messages}", 400)
end
end
def update_milestone_for(parent)
milestone = parent.milestones.find(params.delete(:milestone_id))
milestone_params = declared_params(include_missing: false)
milestone = ::Milestones::UpdateService.new(parent, current_user, milestone_params).execute(milestone)
if milestone.valid?
present milestone, with: Entities::Milestone
else
render_api_error!("Failed to update milestone #{milestone.errors.messages}", 400)
end
end
def milestone_issuables_for(parent, type)
milestone = parent.milestones.find(params[:milestone_id])
finder_klass, entity = get_finder_and_entity(type)
params = build_finder_params(milestone, parent)
issuables = finder_klass.new(current_user, params).execute
present paginate(issuables), with: entity, current_user: current_user
end
def build_finder_params(milestone, parent)
finder_params = { milestone_title: milestone.title, sort: 'label_priority' }
if parent.is_a?(Group)
finder_params.merge(group_id: parent.id)
else
finder_params.merge(project_id: parent.id)
end
end
def get_finder_and_entity(type)
if type == :issue
[IssuesFinder, Entities::IssueBasic]
else
[MergeRequestsFinder, Entities::MergeRequestBasic]
end
end
end
end
end
end
module API
class Milestones < Grape::API
class ProjectMilestones < Grape::API
include PaginationParams
include MilestoneResponses
 
before { authenticate! }
helpers do
def filter_milestones_state(milestones, state)
case state
when 'active' then milestones.active
when 'closed' then milestones.closed
else milestones
end
end
params :optional_params do
optional :description, type: String, desc: 'The description of the milestone'
optional :due_date, type: String, desc: 'The due date of the milestone. The ISO 8601 date format (%Y-%m-%d)'
optional :start_date, type: String, desc: 'The start date of the milestone. The ISO 8601 date format (%Y-%m-%d)'
end
before do
authenticate!
end
 
params do
Loading
Loading
@@ -28,21 +15,12 @@ module API
success Entities::Milestone
end
params do
optional :state, type: String, values: %w[active closed all], default: 'all',
desc: 'Return "active", "closed", or "all" milestones'
optional :iids, type: Array[Integer], desc: 'The IIDs of the milestones'
optional :search, type: String, desc: 'The search criteria for the title or description of the milestone'
use :pagination
use :list_params
end
get ":id/milestones" do
authorize! :read_milestone, user_project
 
milestones = user_project.milestones
milestones = filter_milestones_state(milestones, params[:state])
milestones = filter_by_iid(milestones, params[:iids]) if params[:iids].present?
milestones = filter_by_search(milestones, params[:search]) if params[:search]
present paginate(milestones), with: Entities::Milestone
list_milestones_for(user_project)
end
 
desc 'Get a single project milestone' do
Loading
Loading
@@ -54,8 +32,7 @@ module API
get ":id/milestones/:milestone_id" do
authorize! :read_milestone, user_project
 
milestone = user_project.milestones.find(params[:milestone_id])
present milestone, with: Entities::Milestone
get_milestone_for(user_project)
end
 
desc 'Create a new project milestone' do
Loading
Loading
@@ -68,38 +45,19 @@ module API
post ":id/milestones" do
authorize! :admin_milestone, user_project
 
milestone = ::Milestones::CreateService.new(user_project, current_user, declared_params).execute
if milestone.valid?
present milestone, with: Entities::Milestone
else
render_api_error!("Failed to create milestone #{milestone.errors.messages}", 400)
end
create_milestone_for(user_project)
end
 
desc 'Update an existing project milestone' do
success Entities::Milestone
end
params do
requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
optional :title, type: String, desc: 'The title of the milestone'
optional :state_event, type: String, values: %w[close activate],
desc: 'The state event of the milestone '
use :optional_params
at_least_one_of :title, :description, :due_date, :state_event
use :update_params
end
put ":id/milestones/:milestone_id" do
authorize! :admin_milestone, user_project
milestone = user_project.milestones.find(params.delete(:milestone_id))
 
milestone_params = declared_params(include_missing: false)
milestone = ::Milestones::UpdateService.new(user_project, current_user, milestone_params).execute(milestone)
if milestone.valid?
present milestone, with: Entities::Milestone
else
render_api_error!("Failed to update milestone #{milestone.errors.messages}", 400)
end
update_milestone_for(user_project)
end
 
desc 'Get all issues for a single project milestone' do
Loading
Loading
@@ -112,16 +70,7 @@ module API
get ":id/milestones/:milestone_id/issues" do
authorize! :read_milestone, user_project
 
milestone = user_project.milestones.find(params[:milestone_id])
finder_params = {
project_id: user_project.id,
milestone_title: milestone.title,
sort: 'label_priority'
}
issues = IssuesFinder.new(current_user, finder_params).execute
present paginate(issues), with: Entities::IssueBasic, current_user: current_user, project: user_project
milestone_issuables_for(user_project, :issue)
end
 
desc 'Get all merge requests for a single project milestone' do
Loading
Loading
@@ -135,19 +84,7 @@ module API
get ':id/milestones/:milestone_id/merge_requests' do
authorize! :read_milestone, user_project
 
milestone = user_project.milestones.find(params[:milestone_id])
finder_params = {
project_id: user_project.id,
milestone_title: milestone.title,
sort: 'label_priority'
}
merge_requests = MergeRequestsFinder.new(current_user, finder_params).execute
present paginate(merge_requests),
with: Entities::MergeRequestBasic,
current_user: current_user,
project: user_project
milestone_issuables_for(user_project, :merge_request)
end
end
end
Loading
Loading
require 'spec_helper'
describe API::GroupMilestones do
let(:user) { create(:user) }
let(:group) { create(:group, :private) }
let(:project) { create(:empty_project, namespace: group) }
let!(:group_member) { create(:group_member, group: group, user: user) }
let!(:closed_milestone) { create(:closed_milestone, group: group, title: 'version1', description: 'closed milestone') }
let!(:milestone) { create(:milestone, group: group, title: 'version2', description: 'open milestone') }
it_behaves_like 'group and project milestones', "/groups/:id/milestones" do
let(:route) { "/groups/#{group.id}/milestones" }
end
def setup_for_group
context_group.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
context_group.add_developer(user)
public_project.update(namespace: context_group)
context_group.reload
end
end
require 'spec_helper'
describe API::ProjectMilestones do
let(:user) { create(:user) }
let!(:project) { create(:empty_project, namespace: user.namespace ) }
let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') }
let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') }
before do
project.team << [user, :developer]
end
it_behaves_like 'group and project milestones', "/projects/:id/milestones" do
let(:route) { "/projects/#{project.id}/milestones" }
end
describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
it 'creates an activity event when an milestone is closed' do
expect(Event).to receive(:create)
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
state_event: 'close'
end
end
end
require 'spec_helper'
describe API::Milestones do
let(:user) { create(:user) }
let!(:project) { create(:empty_project, namespace: user.namespace ) }
let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') }
let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') }
shared_examples_for 'group and project milestones' do |route_definition|
let(:resource_route) { "#{route}/#{milestone.id}" }
let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) }
let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) }
let(:label_3) { create(:label, title: 'label_3', project: project) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:another_merge_request) { create(:merge_request, :simple, source_project: project) }
 
before do
project.team << [user, :developer]
end
describe 'GET /projects/:id/milestones' do
it 'returns project milestones' do
get api("/projects/#{project.id}/milestones", user)
describe "GET #{route_definition}" do
it 'returns milestones list' do
get api(route, user)
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
@@ -24,13 +17,13 @@ describe API::Milestones do
end
 
it 'returns a 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones")
get api(route)
 
expect(response).to have_http_status(401)
end
 
it 'returns an array of active milestones' do
get api("/projects/#{project.id}/milestones?state=active", user)
get api("#{route}/?state=active", user)
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
@@ -40,7 +33,7 @@ describe API::Milestones do
end
 
it 'returns an array of closed milestones' do
get api("/projects/#{project.id}/milestones?state=closed", user)
get api("#{route}/?state=closed", user)
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
@@ -50,9 +43,9 @@ describe API::Milestones do
end
 
it 'returns an array of milestones specified by iids' do
other_milestone = create(:milestone, project: project)
other_milestone = create(:milestone, project: try(:project), group: try(:group))
 
get api("/projects/#{project.id}/milestones", user), iids: [closed_milestone.iid, other_milestone.iid]
get api(route, user), iids: [closed_milestone.iid, other_milestone.iid]
 
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
Loading
Loading
@@ -61,25 +54,15 @@ describe API::Milestones do
end
 
it 'does not return any milestone if none found' do
get api("/projects/#{project.id}/milestones", user), iids: [Milestone.maximum(:iid).succ]
get api(route, user), iids: [Milestone.maximum(:iid).succ]
 
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end
end
describe 'GET /projects/:id/milestones/:milestone_id' do
it 'returns a project milestone by id' do
get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(milestone.title)
expect(json_response['iid']).to eq(milestone.iid)
end
 
it 'returns a project milestone by iids array' do
get api("/projects/#{project.id}/milestones?iids=#{closed_milestone.iid}", user)
it 'returns a milestone by iids array' do
get api("#{route}?iids=#{closed_milestone.iid}", user)
 
expect(response.status).to eq 200
expect(response).to include_pagination_headers
Loading
Loading
@@ -89,8 +72,8 @@ describe API::Milestones do
expect(json_response.first['id']).to eq closed_milestone.id
end
 
it 'returns a project milestone by searching for title' do
get api("/projects/#{project.id}/milestones", user), search: 'version2'
it 'returns a milestone by searching for title' do
get api(route, user), search: 'version2'
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
@@ -99,8 +82,8 @@ describe API::Milestones do
expect(json_response.first['id']).to eq milestone.id
end
 
it 'returns a project milestones by searching for description' do
get api("/projects/#{project.id}/milestones", user), search: 'open'
it 'returns a milestones by searching for description' do
get api(route, user), search: 'open'
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
@@ -110,9 +93,17 @@ describe API::Milestones do
end
end
 
describe 'GET /projects/:id/milestones/:milestone_id' do
it 'returns a project milestone by id' do
get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
describe "GET #{route_definition}/:milestone_id" do
it 'returns a milestone by id' do
get api(resource_route, user)
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(milestone.title)
expect(json_response['iid']).to eq(milestone.iid)
end
it 'returns a milestone by id' do
get api(resource_route, user)
 
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(milestone.title)
Loading
Loading
@@ -120,29 +111,29 @@ describe API::Milestones do
end
 
it 'returns 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones/#{milestone.id}")
get api(resource_route)
 
expect(response).to have_http_status(401)
end
 
it 'returns a 404 error if milestone id not found' do
get api("/projects/#{project.id}/milestones/1234", user)
get api("#{route}/1234", user)
 
expect(response).to have_http_status(404)
end
end
 
describe 'POST /projects/:id/milestones' do
it 'creates a new project milestone' do
post api("/projects/#{project.id}/milestones", user), title: 'new milestone'
describe "POST #{route_definition}" do
it 'creates a new milestone' do
post api(route, user), title: 'new milestone'
 
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('new milestone')
expect(json_response['description']).to be_nil
end
 
it 'creates a new project milestone with description and dates' do
post api("/projects/#{project.id}/milestones", user),
it 'creates a new milestone with description and dates' do
post api(route, user),
title: 'new milestone', description: 'release', due_date: '2013-03-02', start_date: '2013-02-02'
 
expect(response).to have_http_status(201)
Loading
Loading
@@ -152,20 +143,20 @@ describe API::Milestones do
end
 
it 'returns a 400 error if title is missing' do
post api("/projects/#{project.id}/milestones", user)
post api(route, user)
 
expect(response).to have_http_status(400)
end
 
it 'returns a 400 error if params are invalid (duplicate title)' do
post api("/projects/#{project.id}/milestones", user),
post api(route, user),
title: milestone.title, description: 'release', due_date: '2013-03-02'
 
expect(response).to have_http_status(400)
end
 
it 'creates a new project with reserved html characters' do
post api("/projects/#{project.id}/milestones", user), title: 'foo & bar 1.1 -> 2.2'
it 'creates a new milestone with reserved html characters' do
post api(route, user), title: 'foo & bar 1.1 -> 2.2'
 
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('foo & bar 1.1 -> 2.2')
Loading
Loading
@@ -173,9 +164,9 @@ describe API::Milestones do
end
end
 
describe 'PUT /projects/:id/milestones/:milestone_id' do
it 'updates a project milestone' do
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
describe "PUT #{route_definition}/:milestone_id" do
it 'updates a milestone' do
put api(resource_route, user),
title: 'updated title'
 
expect(response).to have_http_status(200)
Loading
Loading
@@ -185,23 +176,21 @@ describe API::Milestones do
it 'removes a due date if nil is passed' do
milestone.update!(due_date: "2016-08-05")
 
put api("/projects/#{project.id}/milestones/#{milestone.id}", user), due_date: nil
put api(resource_route, user), due_date: nil
 
expect(response).to have_http_status(200)
expect(json_response['due_date']).to be_nil
end
 
it 'returns a 404 error if milestone id not found' do
put api("/projects/#{project.id}/milestones/1234", user),
put api("#{route}/1234", user),
title: 'updated title'
 
expect(response).to have_http_status(404)
end
end
 
describe 'PUT /projects/:id/milestones/:milestone_id to close milestone' do
it 'updates a project milestone' do
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
it 'closes milestone' do
put api(resource_route, user),
state_event: 'close'
expect(response).to have_http_status(200)
 
Loading
Loading
@@ -209,21 +198,14 @@ describe API::Milestones do
end
end
 
describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
it 'creates an activity event when an milestone is closed' do
expect(Event).to receive(:create)
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
state_event: 'close'
end
end
describe "GET #{route_definition}/:milestone_id/issues" do
let(:issues_route) { "#{route}/#{milestone.id}/issues" }
 
describe 'GET /projects/:id/milestones/:milestone_id/issues' do
before do
milestone.issues << create(:issue, project: project)
end
it 'returns project issues for a particular milestone' do
get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
it 'returns issues for a particular milestone' do
get api(issues_route, user)
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
@@ -231,12 +213,12 @@ describe API::Milestones do
expect(json_response.first['milestone']['title']).to eq(milestone.title)
end
 
it 'returns project issues sorted by label priority' do
it 'returns issues sorted by label priority' do
issue_1 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_3])
issue_2 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_1])
issue_3 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_2])
 
get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
get api(issues_route, user)
 
expect(json_response.first['id']).to eq(issue_2.id)
expect(json_response.second['id']).to eq(issue_3.id)
Loading
Loading
@@ -244,44 +226,58 @@ describe API::Milestones do
end
 
it 'matches V4 response schema for a list of issues' do
get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
get api(issues_route, user)
 
expect(response).to have_http_status(200)
expect(response).to match_response_schema('public_api/v4/issues')
end
 
it 'returns a 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones/#{milestone.id}/issues")
get api(issues_route)
 
expect(response).to have_http_status(401)
end
 
describe 'confidential issues' do
let(:public_project) { create(:empty_project, :public) }
let(:milestone) { create(:milestone, project: public_project) }
let(:issue) { create(:issue, project: public_project) }
let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
let!(:public_project) { create(:empty_project, :public) }
let!(:context_group) { try(:group) }
let!(:milestone) do
context_group ? create(:milestone, group: context_group) : create(:milestone, project: public_project)
end
let!(:issue) { create(:issue, project: public_project) }
let!(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
let!(:issues_route) do
if context_group
"#{route}/#{milestone.id}/issues"
else
"/projects/#{public_project.id}/milestones/#{milestone.id}/issues"
end
end
 
before do
# Add public project to the group in context
setup_for_group if context_group
public_project.team << [user, :developer]
milestone.issues << issue << confidential_issue
end
 
it 'returns confidential issues to team members' do
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user)
get api(issues_route, user)
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(2)
# 2 for projects, 3 for group(which has another project with an issue)
expect(json_response.size).to be_between(2, 3)
expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id)
end
 
it 'does not return confidential issues to team members with guest role' do
member = create(:user)
project.team << [member, :guest]
public_project.team << [member, :guest]
 
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member)
get api(issues_route, member)
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
@@ -291,7 +287,7 @@ describe API::Milestones do
end
 
it 'does not return confidential issues to regular users' do
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user))
get api(issues_route, create(:user))
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
@@ -304,30 +300,30 @@ describe API::Milestones do
issue.labels << label_2
confidential_issue.labels << label_1
 
get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user)
get api(issues_route, user)
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(2)
# 2 for projects, 3 for group(which has another project with an issue)
expect(json_response.size).to be_between(2, 3)
expect(json_response.first['id']).to eq(confidential_issue.id)
expect(json_response.second['id']).to eq(issue.id)
end
end
end
 
describe 'GET /projects/:id/milestones/:milestone_id/merge_requests' do
let(:merge_request) { create(:merge_request, source_project: project) }
let(:another_merge_request) { create(:merge_request, :simple, source_project: project) }
describe "GET #{route_definition}/:milestone_id/merge_requests" do
let(:merge_requests_route) { "#{route}/#{milestone.id}/merge_requests" }
 
before do
milestone.merge_requests << merge_request
end
 
it 'returns project merge_requests for a particular milestone' do
it 'returns merge_requests for a particular milestone' do
# eager-load another_merge_request
another_merge_request
get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user)
get api(merge_requests_route, user)
 
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
Loading
Loading
@@ -336,12 +332,12 @@ describe API::Milestones do
expect(json_response.first['milestone']['title']).to eq(milestone.title)
end
 
it 'returns project merge_requests sorted by label priority' do
it 'returns merge_requests sorted by label priority' do
merge_request_1 = create(:labeled_merge_request, source_branch: 'branch_1', source_project: project, milestone: milestone, labels: [label_2])
merge_request_2 = create(:labeled_merge_request, source_branch: 'branch_2', source_project: project, milestone: milestone, labels: [label_1])
merge_request_3 = create(:labeled_merge_request, source_branch: 'branch_3', source_project: project, milestone: milestone, labels: [label_3])
 
get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user)
get api(merge_requests_route, user)
 
expect(json_response.first['id']).to eq(merge_request_2.id)
expect(json_response.second['id']).to eq(merge_request_1.id)
Loading
Loading
@@ -349,20 +345,22 @@ describe API::Milestones do
end
 
it 'returns a 404 error if milestone id not found' do
get api("/projects/#{project.id}/milestones/1234/merge_requests", user)
not_found_route = "#{route}/1234/merge_requests"
get api(not_found_route, user)
 
expect(response).to have_http_status(404)
end
 
it 'returns a 404 if the user has no access to the milestone' do
new_user = create :user
get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", new_user)
get api(merge_requests_route, new_user)
 
expect(response).to have_http_status(404)
end
 
it 'returns a 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests")
get api(merge_requests_route)
 
expect(response).to have_http_status(401)
end
Loading
Loading
@@ -372,7 +370,7 @@ describe API::Milestones do
another_merge_request.labels << label_1
merge_request.labels << label_2
 
get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user)
get api(merge_requests_route, user)
 
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment