Commit f1e24d4d authored by Patrick Derichs's avatar Patrick Derichs Committed by Kamil Trzciński

Add label_id parameter to label API for PUT and DELETE

Add specs for new parameter and updated documentation as well.
parent e8492290
---
title: Add optional label_id parameter to label API for PUT and DELETE
merge_request: 31804
author:
type: changed
...@@ -138,7 +138,8 @@ DELETE /projects/:id/labels ...@@ -138,7 +138,8 @@ DELETE /projects/:id/labels
| Attribute | Type | Required | Description | | 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 | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the label | | `label_id` | integer | yes (or `name`) | The id of the existing label |
| `name` | string | yes (or `label_id`) | The name of the existing label |
```bash ```bash
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels?name=bug" curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels?name=bug"
...@@ -156,7 +157,8 @@ PUT /projects/:id/labels ...@@ -156,7 +157,8 @@ PUT /projects/:id/labels
| Attribute | Type | Required | Description | | 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 | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the existing label | | `label_id` | integer | yes (or `name`) | The id of the existing label |
| `name` | string | yes (or `label_id`) | The name of the existing label |
| `new_name` | string | yes if `color` is not provided | The new name of the label | | `new_name` | string | yes if `color` is not provided | The new name of the label |
| `color` | string | yes if `new_name` is not provided | The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords) | | `color` | string | yes if `new_name` is not provided | The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords) |
| `description` | string | no | The new description of the label | | `description` | string | no | The new description of the label |
......
...@@ -11,9 +11,9 @@ module API ...@@ -11,9 +11,9 @@ module API
optional :description, type: String, desc: 'The description of label to be created' optional :description, type: String, desc: 'The description of label to be created'
end end
def find_label(parent, id, include_ancestor_groups: true) def find_label(parent, id_or_title, include_ancestor_groups: true)
labels = available_labels_for(parent, include_ancestor_groups: include_ancestor_groups) labels = available_labels_for(parent, include_ancestor_groups: include_ancestor_groups)
label = labels.find_by_id(id) || labels.find_by_title(id) label = labels.find_by_id(id_or_title) || labels.find_by_title(id_or_title)
label || not_found!('Label') label || not_found!('Label')
end end
...@@ -35,12 +35,7 @@ module API ...@@ -35,12 +35,7 @@ module API
priority = params.delete(:priority) priority = params.delete(:priority)
label_params = declared_params(include_missing: false) label_params = declared_params(include_missing: false)
label = label = ::Labels::CreateService.new(label_params).execute(create_service_params(parent))
if parent.is_a?(Project)
::Labels::CreateService.new(label_params).execute(project: parent)
else
::Labels::CreateService.new(label_params).execute(group: parent)
end
if label.persisted? if label.persisted?
if parent.is_a?(Project) if parent.is_a?(Project)
...@@ -56,10 +51,13 @@ module API ...@@ -56,10 +51,13 @@ module API
def update_label(parent, entity) def update_label(parent, entity)
authorize! :admin_label, parent authorize! :admin_label, parent
label = find_label(parent, params[:name], include_ancestor_groups: false) label = find_label(parent, params_id_or_title, include_ancestor_groups: false)
update_priority = params.key?(:priority) update_priority = params.key?(:priority)
priority = params.delete(:priority) priority = params.delete(:priority)
# params is used to update the label so we need to remove this field here
params.delete(:label_id)
label = ::Labels::UpdateService.new(declared_params(include_missing: false)).execute(label) label = ::Labels::UpdateService.new(declared_params(include_missing: false)).execute(label)
render_validation_error!(label) unless label.valid? render_validation_error!(label) unless label.valid?
...@@ -77,10 +75,24 @@ module API ...@@ -77,10 +75,24 @@ module API
def delete_label(parent) def delete_label(parent)
authorize! :admin_label, parent authorize! :admin_label, parent
label = find_label(parent, params[:name], include_ancestor_groups: false) label = find_label(parent, params_id_or_title, include_ancestor_groups: false)
destroy_conditionally!(label) destroy_conditionally!(label)
end end
def params_id_or_title
@params_id_or_title ||= params[:label_id] || params[:name]
end
def create_service_params(parent)
if parent.is_a?(Project)
{ project: parent }
elsif parent.is_a?(Group)
{ group: parent }
else
raise TypeError, 'Parent type is not supported'
end
end
end end
end end
end end
...@@ -38,11 +38,13 @@ module API ...@@ -38,11 +38,13 @@ module API
success Entities::ProjectLabel success Entities::ProjectLabel
end end
params do params do
requires :name, type: String, desc: 'The name of the label to be updated' optional :label_id, type: Integer, desc: 'The id of the label to be updated'
optional :name, type: String, desc: 'The name of the label to be updated'
optional :new_name, type: String, desc: 'The new name of the label' optional :new_name, type: String, desc: 'The new name of the label'
optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names" optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
optional :description, type: String, desc: 'The new description of label' optional :description, type: String, desc: 'The new description of label'
optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
exactly_one_of :label_id, :name
at_least_one_of :new_name, :color, :description, :priority at_least_one_of :new_name, :color, :description, :priority
end end
put ':id/labels' do put ':id/labels' do
...@@ -53,7 +55,9 @@ module API ...@@ -53,7 +55,9 @@ module API
success Entities::ProjectLabel success Entities::ProjectLabel
end end
params do params do
requires :name, type: String, desc: 'The name of the label to be deleted' optional :label_id, type: Integer, desc: 'The id of the label to be deleted'
optional :name, type: String, desc: 'The name of the label to be deleted'
exactly_one_of :label_id, :name
end end
delete ':id/labels' do delete ':id/labels' do
delete_label(user_project) delete_label(user_project)
......
# frozen_string_literal: true
require 'spec_helper'
describe API::Helpers::LabelHelpers do
describe 'create_service_params' do
let(:label_helper) do
Class.new do
include API::Helpers::LabelHelpers
end.new
end
context 'when a project is given' do
it 'returns the expected params' do
project = create(:project)
expect(label_helper.create_service_params(project)).to eq({ project: project })
end
end
context 'when a group is given' do
it 'returns the expected params' do
group = create(:group)
expect(label_helper.create_service_params(group)).to eq({ group: group })
end
end
context 'when something else is given' do
it 'raises a type error' do
expect { label_helper.create_service_params(Class.new) }.to raise_error(TypeError)
end
end
end
end
...@@ -6,6 +6,180 @@ describe API::Labels do ...@@ -6,6 +6,180 @@ describe API::Labels do
let!(:label1) { create(:label, title: 'label1', project: project) } let!(:label1) { create(:label, title: 'label1', project: project) }
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) } let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
shared_examples 'label update API' do
it 'returns 200 if name is changed' do
request_params = {
new_name: 'New Label'
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq('New Label')
expect(json_response['color']).to eq(label1.color)
end
it 'returns 200 if colors is changed' do
request_params = {
color: '#FFFFFF'
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq(label1.name)
expect(json_response['color']).to eq('#FFFFFF')
end
it 'returns 200 if a priority is added' do
request_params = {
priority: 3
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response.status).to eq(200)
expect(json_response['name']).to eq(label1.name)
expect(json_response['priority']).to eq(3)
end
it 'returns 400 if no new parameters given' do
put api("/projects/#{project.id}/labels", user), params: spec_params
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eq('new_name, color, description, priority are missing, '\
'at least one parameter must be provided')
end
it 'returns 400 when color code is too short' do
request_params = {
color: '#FF'
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
it 'returns 400 for too long color code' do
request_params = {
color: '#FFAAFFFF'
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
it 'returns 400 for invalid priority' do
request_params = {
priority: 'foo'
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response).to have_gitlab_http_status(400)
end
it 'returns 200 if name and colors and description are changed' do
request_params = {
new_name: 'New Label',
color: '#FFFFFF',
description: 'test'
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq('New Label')
expect(json_response['color']).to eq('#FFFFFF')
expect(json_response['description']).to eq('test')
end
it 'returns 400 for invalid name' do
request_params = {
new_name: ',',
color: '#FFFFFF'
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['title']).to eq(['is invalid'])
end
it 'returns 200 if description is changed' do
request_params = {
description: 'test'
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response).to have_gitlab_http_status(200)
expect(json_response['id']).to eq(expected_response_label_id)
expect(json_response['description']).to eq('test')
end
it 'returns 200 if priority is changed' do
request_params = {
priority: 10
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response.status).to eq(200)
expect(json_response['id']).to eq(expected_response_label_id)
expect(json_response['priority']).to eq(10)
end
it 'returns 200 if a priority is removed' do
label = find_by_spec_params(spec_params)
expect(label).not_to be_nil
label.priorities.create(project: label.project, priority: 1)
label.save!
request_params = {
priority: nil
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
params: request_params
expect(response.status).to eq(200)
expect(json_response['id']).to eq(expected_response_label_id)
expect(json_response['priority']).to be_nil
end
def find_by_spec_params(params)
if params.key?(:label_id)
Label.find(params[:label_id])
else
Label.find_by(name: params[:name])
end
end
end
shared_examples 'label delete API' do
it 'returns 204 for existing label' do
delete api("/projects/#{project.id}/labels", user), params: spec_params
expect(response).to have_gitlab_http_status(204)
end
end
before do before do
project.add_maintainer(user) project.add_maintainer(user)
end end
...@@ -208,113 +382,56 @@ describe API::Labels do ...@@ -208,113 +382,56 @@ describe API::Labels do
end end
describe 'DELETE /projects/:id/labels' do describe 'DELETE /projects/:id/labels' do
it 'returns 204 for existing label' do it_behaves_like 'label delete API' do
delete api("/projects/#{project.id}/labels", user), params: { name: 'label1' } let(:spec_params) { { name: 'label1' } }
end
expect(response).to have_gitlab_http_status(204) it_behaves_like 'label delete API' do
let(:spec_params) { { label_id: label1.id } }
end end
it 'returns 404 for non existing label' do it 'returns 404 for non existing label' do
delete api("/projects/#{project.id}/labels", user), params: { name: 'label2' } delete api("/projects/#{project.id}/labels", user), params: { name: 'label2' }
expect(response).to have_gitlab_http_status(404) expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 Label Not Found') expect(json_response['message']).to eq('404 Label Not Found')
end end
it 'returns 400 for wrong parameters' do it 'returns 400 for wrong parameters' do
delete api("/projects/#{project.id}/labels", user) delete api("/projects/#{project.id}/labels", user)
expect(response).to have_gitlab_http_status(400)
end
it_behaves_like '412 response' do expect(response).to have_gitlab_http_status(400)
let(:request) { api("/projects/#{project.id}/labels", user) }
let(:params) { { name: 'label1' } }
end
end end
describe 'PUT /projects/:id/labels' do it 'fails if label_id and name are given in params' do
it 'returns 200 if name and colors and description are changed' do delete api("/projects/#{project.id}/labels", user),
put api("/projects/#{project.id}/labels", user),
params: { params: {
name: 'label1', label_id: label1.id,
new_name: 'New Label', name: priority_label.name
color: '#FFFFFF',
description: 'test'
} }
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq('New Label')
expect(json_response['color']).to eq('#FFFFFF')
expect(json_response['description']).to eq('test')
end
it 'returns 200 if name is changed' do expect(response).to have_gitlab_http_status(400)
put api("/projects/#{project.id}/labels", user),
params: {
name: 'label1',
new_name: 'New Label'
}
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq('New Label')
expect(json_response['color']).to eq(label1.color)
end end
it 'returns 200 if colors is changed' do it_behaves_like '412 response' do
put api("/projects/#{project.id}/labels", user), let(:request) { api("/projects/#{project.id}/labels", user) }
params: { let(:params) { { name: 'label1' } }
name: 'label1',
color: '#FFFFFF'
}
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq(label1.name)
expect(json_response['color']).to eq('#FFFFFF')
end end
it 'returns 200 if description is changed' do
put api("/projects/#{project.id}/labels", user),
params: {
name: 'bug',
description: 'test'
}
expect(response).to have_gitlab_http_status(200)
expect(json_response['name']).to eq(priority_label.name)
expect(json_response['description']).to eq('test')
expect(json_response['priority']).to eq(3)
end end
it 'returns 200 if priority is changed' do describe 'PUT /projects/:id/labels' do
put api("/projects/#{project.id}/labels", user), context 'when using name' do
params: { it_behaves_like 'label update API' do
name: 'bug', let(:spec_params) { { name: 'label1' } }
priority: 10 let(:expected_response_label_id) { label1.id }
}
expect(response.status).to eq(200)
expect(json_response['name']).to eq(priority_label.name)
expect(json_response['priority']).to eq(10)
end end
it 'returns 200 if a priority is added' do
put api("/projects/#{project.id}/labels", user),
params: {
name: 'label1',
priority: 3
}
expect(response.status).to eq(200)
expect(json_response['name']).to eq(label1.name)
expect(json_response['priority']).to eq(3)
end end
it 'returns 200 if the priority is removed' do context 'when using label_id' do
put api("/projects/#{project.id}/labels", user), it_behaves_like 'label update API' do
params: { let(:spec_params) { { label_id: label1.id } }
name: priority_label.name, let(:expected_response_label_id) { label1.id }
priority: nil end
}
expect(response.status).to eq(200)
expect(json_response['name']).to eq(priority_label.name)
expect(json_response['priority']).to be_nil
end end
it 'returns 404 if label does not exist' do it 'returns 404 if label does not exist' do
...@@ -323,58 +440,33 @@ describe API::Labels do ...@@ -323,58 +440,33 @@ describe API::Labels do
name: 'label2', name: 'label2',
new_name: 'label3' new_name: 'label3'
} }
expect(response).to have_gitlab_http_status(404)
end
it 'returns 400 if no label name given' do expect(response).to have_gitlab_http_status(404)
put api("/projects/#{project.id}/labels", user), params: { new_name: 'label2' }
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eq('name is missing')
end
it 'returns 400 if no new parameters given' do
put api("/projects/#{project.id}/labels", user), params: { name: 'label1' }
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eq('new_name, color, description, priority are missing, '\
'at least one parameter must be provided')
end end
it 'returns 400 for invalid name' do it 'returns 404 if label by id does not exist' do
put api("/projects/#{project.id}/labels", user), put api("/projects/#{project.id}/labels", user),
params: { params: {
name: 'label1', label_id: 0,
new_name: ',', new_name: 'label3'
color: '#FFFFFF'
} }
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['title']).to eq(['is invalid'])
end
it 'returns 400 when color code is too short' do expect(response).to have_gitlab_http_status(404)
put api("/projects/#{project.id}/labels", user),
params: {
name: 'label1',
color: '#FF'
}
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['color']).to eq(['must be a valid color code'])
end end
it 'returns 400 for too long color code' do it 'returns 400 if no label name and id is given' do
put api("/projects/#{project.id}/labels", user), put api("/projects/#{project.id}/labels", user), params: { new_name: 'label2' }
params: {
name: 'label1',
color: '#FFAAFFFF'
}
expect(response).to have_gitlab_http_status(400) expect(response).to have_gitlab_http_status(400)
expect(json_response['message']['color']).to eq(['must be a valid color code']) expect(json_response['error']).to eq('label_id, name are missing, exactly one parameter must be provided')
end end
it 'returns 400 for invalid priority' do it 'fails if label_id and name are given in params' do
put api("/projects/#{project.id}/labels", user), put api("/projects/#{project.id}/labels", user),
params: { params: {
name: 'label1', label_id: label1.id,
priority: 'foo' name: priority_label.name,
new_name: 'New Label'
} }
expect(response).to have_gitlab_http_status(400) expect(response).to have_gitlab_http_status(400)
......
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