Commit 6b2673bc authored by Robert Schilling's avatar Robert Schilling

Add new to test bulk_update endpoint for issues and MRs

Add documentation for issues and MRs. Add shared tests
for both bulk updating issues and MRs.
parent 9e521ca7
--- ---
title: 'API: Bulk update for issues and MRs' title: 'API: Bulk update for issues and MRs'
merge_request: merge_request: 25201
author: author: Robert Schilling
type: added type: added
...@@ -635,6 +635,37 @@ Example response: ...@@ -635,6 +635,37 @@ Example response:
**Note**: The `closed_by` attribute was [introduced in GitLab 10.6][ce-17042]. This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists. **Note**: The `closed_by` attribute was [introduced in GitLab 10.6][ce-17042]. This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists.
## Bulk update issues
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21368) in GitLab 11.8.
Update multiple issues using a single API call. Returns the number of successfully updated issues.
```
PUT /projects/:id/issues/bulk_update
```
| Attribute | Type | Required | Description **** |
|----------------|---------|----------|------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `issuable_ids` | Array[integer] | yes | The IDs of issues to be updated. |
| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
| `milestone_id` | integer | no | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
| `add_label_ids` | Array[integer] | no | Comma-separated label IDs to be added. |
| `remove_label_ids` | Array[integer] | no | Comma-separated label IDs to be added. |
| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it. |
| `subscription_event` | string | no | The subscription_event event of an issue. Set `subscribe` to subscribe to the issue and `unsubscribe` to unsubscribe from it. |
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues/bulk_update?issuable_ids[]=1&issuable_ids[]=2&state_event=close
```
Example response:
```json
{ "message": "2 issues updated" }
```
## Delete an issue ## Delete an issue
Only for admins and project owners. Soft deletes the issue in question. Only for admins and project owners. Soft deletes the issue in question.
......
...@@ -956,6 +956,37 @@ Must include at least one non-required attribute from above. ...@@ -956,6 +956,37 @@ Must include at least one non-required attribute from above.
} }
``` ```
## Bulk update merge requests
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21368) in GitLab 11.8.
Update multiple merge requests using a single API call. Returns the number of successfully updated merge requests.
```
PUT /projects/:id/merge_requests/bulk_update
```
| Attribute | Type | Required | Description **** |
|----------------|---------|----------|------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `issuable_ids` | Array[integer] | yes | The IDs of merge requests to be updated. |
| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
| `milestone_id` | integer | no | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
| `add_label_ids` | Array[integer] | no | Comma-separated label IDs to be added. |
| `remove_label_ids` | Array[integer] | no | Comma-separated label IDs to be added. |
| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it. |
| `subscription_event` | string | no | The subscription_event event of an issue. Set `subscribe` to subscribe to the issue and `unsubscribe` to unsubscribe from it. |
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/merge_requests/bulk_update?issuable_ids[]=1&issuable_ids[]=2&state_event=close
```
Example response:
```json
{ "message": "2 merge_requests updated" }
```
## Delete a merge request ## Delete a merge request
Only for admins and project owners. Soft deletes the merge request in question. Only for admins and project owners. Soft deletes the merge request in question.
......
...@@ -11,6 +11,7 @@ module API ...@@ -11,6 +11,7 @@ module API
detail 'This feature was introduced in 11.9' detail 'This feature was introduced in 11.9'
end end
params do params do
requires :issuable_ids, type: Array[Integer], desc: "Array or #{issuable.pluralize} IDs to be updates"
optional :state_event, type: String, values: %w(reopen close), desc: 'Reopens or closes a resource' optional :state_event, type: String, values: %w(reopen close), desc: 'Reopens or closes a resource'
optional :milestone_id, type: Integer, desc: 'The milestone ID number' optional :milestone_id, type: Integer, desc: 'The milestone ID number'
optional :add_label_ids, type: Array[Integer], desc: 'IDs of labels to be added' optional :add_label_ids, type: Array[Integer], desc: 'IDs of labels to be added'
...@@ -28,16 +29,18 @@ module API ...@@ -28,16 +29,18 @@ module API
:subscription_event, :assignee_id :subscription_event, :assignee_id
end end
end end
put ":id/#{issuable}/bulk_update" do put ":id/#{issuable.pluralize}/bulk_update" do
authorize! :"admin_#{issuable}", user_project
update_params = declared_params(include_missing: false) update_params = declared_params(include_missing: false)
result = Issuable::BulkUpdateService.new(user_project, current_user, update_params) result = Issuable::BulkUpdateService.new(user_project, current_user, update_params)
.execute(issuable) .execute(issuable)
quantity = result[:count]
if result[:success] if result[:success]
status 200 status 200
{ notice: "#{quantity} #{issuable.pluralize(quantity)} updated" } quantity = result[:count]
{ message: "#{quantity} #{issuable.pluralize(quantity)} updated" }
else else
render_api_error!('Bulk update failed', 400) render_api_error!('Bulk update failed', 400)
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe API::IssuableBulkUpdate do
set(:user) { create(:user) }
set(:project) do
create(:project, :public, creator_id: user.id, namespace: user.namespace)
end
%w(issue merge_request).each do |issuable|
describe "PUT /projects/:id/#{issuable.pluralize}/bulk_update" do
let(:merge_request_1) { create(:merge_request, source_project: project) }
let(:merge_request_2) { create(:merge_request, :simple, source_project: project) }
let!(:issuables) { issuable == 'issue' ? create_list(:issue, 2, project: project) : [merge_request_1, merge_request_2] }
def bulk_update(issuable, issuables, params, update_user = user)
put api("/projects/#{project.id}/#{issuable.pluralize}/bulk_update", update_user),
params: { issuable_ids: Array(issuables).map(&:id) }.merge(params)
end
context 'with not enough permissions' do
it 'returns 403 for guest users' do
guest = create(:user)
project.add_guest(guest)
bulk_update(issuable, issuables, { state_event: 'close' }, guest)
expect(response).to have_gitlab_http_status(403)
end
end
context 'when modifying the state' do
it "closes #{issuable}" do
bulk_update(issuable, issuables, { state_event: 'close' })
expect(response).to have_gitlab_http_status(200)
expect(json_response['message']).to eq("#{issuables.count} #{issuable.pluralize(issuables.count)} updated")
expect(project.public_send(issuable.pluralize).opened).to be_empty
expect(project.public_send(issuable.pluralize).closed).not_to be_empty
end
it "opens #{issuable}" do
closed_issuables = create_list("closed_#{issuable}".to_sym, 2)
bulk_update(issuable, closed_issuables, { state_event: 'reopen' })
expect(response).to have_gitlab_http_status(200)
expect(project.public_send(issuable.pluralize).closed).to be_empty
end
end
context 'when modifying the milestone' do
let(:milestone) { create(:milestone, project: project) }
it "adds a milestone #{issuable}" do
bulk_update(issuable, issuables, { milestone_id: milestone.id })
expect(response).to have_gitlab_http_status(200)
issuables.each do |issuable|
expect(issuable.reload.milestone).to eq(milestone)
end
end
it 'removes a milestone' do
issuables.first.milestone = milestone
milestone_issuable = issuables.first
bulk_update(issuable, [milestone_issuable], { milestone_id: 0 })
expect(response).to have_gitlab_http_status(200)
expect(milestone_issuable.reload.milestone).to eq(nil)
end
end
context 'when modifying the subscription state' do
it "subscribes to #{issuable}" do
bulk_update(issuable, issuables, { subscription_event: 'subscribe' })
expect(response).to have_gitlab_http_status(200)
expect(issuables).to all(be_subscribed(user, project))
end
it 'unsubscribes from issues' do
issuables.each do |issuable|
issuable.subscriptions.create(user: user, project: project, subscribed: true)
end
bulk_update(issuable, issuables, { subscription_event: 'unsubscribe' })
expect(response).to have_gitlab_http_status(200)
issuables.each do |issuable|
expect(issuable).not_to be_subscribed(user, project)
end
end
end
context 'when modifying the assignee' do
it 'adds assignee to issues' do
params = issuable == 'issue' ? { assignee_ids: [user.id] } : { assignee_id: user.id }
bulk_update(issuable, issuables, params)
expect(response).to have_gitlab_http_status(200)
issuables.each do |issuable|
expect(issuable.reload.assignees).to eq([user])
end
end
it 'removes assignee' do
assigned_issuable = issuables.first
if issuable == 'issue'
params = { assignee_ids: 0 }
assigned_issuable.assignees << user
else
params = { assignee_id: 0 }
assigned_issuable.update_attribute(:assignee, user)
end
bulk_update(issuable, [assigned_issuable], params)
expect(assigned_issuable.reload.assignees).to eq([])
end
end
context 'when modifying labels' do
let(:bug) { create(:label, project: project) }
let(:regression) { create(:label, project: project) }
let(:feature) { create(:label, project: project) }
it 'adds new labels' do
bulk_update(issuable, issuables, { add_label_ids: [bug.id, regression.id, feature.id] })
issuables.each do |issusable|
expect(issusable.reload.label_ids).to contain_exactly(bug.id, regression.id, feature.id)
end
end
it 'removes labels' do
labled_issuable = issuables.first
labled_issuable.labels << bug
labled_issuable.labels << regression
labled_issuable.labels << feature
bulk_update(issuable, [labled_issuable], { remove_label_ids: [bug.id, regression.id] })
expect(labled_issuable.reload.label_ids).to contain_exactly(feature.id)
end
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