Commit d91b9645 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'api-group-labels' into 'master'

API group labels

Closes #44901

See merge request gitlab-org/gitlab-ce!21368
parents 6e519607 7c6efc00
...@@ -8,6 +8,7 @@ module Labels ...@@ -8,6 +8,7 @@ module Labels
# returns the updated label # returns the updated label
def execute(label) def execute(label)
params[:name] = params.delete(:new_name) if params.key?(:new_name)
params[:color] = convert_color_name_to_hex if params[:color].present? params[:color] = convert_color_name_to_hex if params[:color].present?
label.update(params) label.update(params)
......
---
title: 'API: Add support for group labels'
merge_request: 21368
author: Robert Schilling
type: added
...@@ -29,6 +29,7 @@ The following API resources are available: ...@@ -29,6 +29,7 @@ The following API resources are available:
- [Group access requests](access_requests.md) - [Group access requests](access_requests.md)
- [Group badges](group_badges.md) - [Group badges](group_badges.md)
- [Group issue boards](group_boards.md) - [Group issue boards](group_boards.md)
- [Group labels](group_labels.md)
- [Group-level variables](group_level_variables.md) - [Group-level variables](group_level_variables.md)
- [Group members](members.md) - [Group members](members.md)
- [Group milestones](group_milestones.md) - [Group milestones](group_milestones.md)
......
# Group Label API
>**Note:** This feature was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21368) in GitLab 11.8.
This API supports managing of [group labels](../user/project/labels.md#project-labels-and-group-labels). It allows to list, create, update, and delete group labels. Furthermore, users can subscribe and unsubscribe to and from group labels.
## List group labels
Get all labels for a given group.
```
GET /groups/:id/labels
```
| 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 |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels
```
Example response:
```json
[
{
"id": 7,
"name": "bug",
"color": "#FF0000",
"description": null,
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
},
{
"id": 4,
"name": "feature",
"color": "#228B22",
"description": null,
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
}
]
```
## Create a new group label
Create a new group label for a given group.
```
POST /groups/:id/labels
```
| 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 |
| `name` | string | yes | The name of the label |
| `color` | string | yes | 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 description of the label |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "Feature Proposal", "color": "#FFA500", "description": "Describes new ideas" }' https://gitlab.example.com/api/v4/groups/5/labels
```
Example response:
```json
{
"id": 9,
"name": "Feature Proposal",
"color": "#FFA500",
"description": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
}
```
## Update a group label
Updates an existing group label. At least one parameter is required, to update the group label.
```
PUT /groups/:id/labels
```
| 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 |
| `name` | string | yes | The name of the label |
| `new_name` | string | no | The new name of the label |
| `color` | string | no | 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 description of the label |
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "Feature Proposal", "new_name": "Feature Idea" }' https://gitlab.example.com/api/v4/groups/5/labels
```
Example response:
```json
{
"id": 9,
"name": "Feature Idea",
"color": "#FFA500",
"description": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
}
```
## Delete a group label
Deletes a group label with a given name.
```
DELETE /groups/:id/labels
```
| 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 |
| `name` | string | yes | The name of the label |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels?name=bug
```
## Subscribe to a group label
Subscribes the authenticated user to a group label to receive notifications. If
the user is already subscribed to the label, the status code `304` is returned.
```
POST /groups/:id/labels/:label_id/subscribe
```
| 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 |
| `label_id` | integer or string | yes | The ID or title of a group's label |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels/9/subscribe
```
Example response:
```json
{
"id": 9,
"name": "Feature Idea",
"color": "#FFA500",
"description": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": true
}
```
## Unsubscribe from a group label
Unsubscribes the authenticated user from a group label to not receive
notifications from it. If the user is not subscribed to the label, the status
code `304` is returned.
```
POST /groups/:id/labels/:label_id/unsubscribe
```
| 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 |
| `label_id` | integer or string | yes | The ID or title of a group's label |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels/9/unsubscribe
```
Example response:
```json
{
"id": 9,
"name": "Feature Idea",
"color": "#FFA500",
"description": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
}
```
...@@ -109,6 +109,7 @@ module API ...@@ -109,6 +109,7 @@ module API
mount ::API::Features mount ::API::Features
mount ::API::Files mount ::API::Files
mount ::API::GroupBoards mount ::API::GroupBoards
mount ::API::GroupLabels
mount ::API::GroupMilestones mount ::API::GroupMilestones
mount ::API::Groups mount ::API::Groups
mount ::API::GroupVariables mount ::API::GroupVariables
......
...@@ -1019,12 +1019,17 @@ module API ...@@ -1019,12 +1019,17 @@ module API
label.open_merge_requests_count(options[:current_user]) label.open_merge_requests_count(options[:current_user])
end end
expose :priority do |label, options| expose :subscribed do |label, options|
label.priority(options[:project]) label.subscribed?(options[:current_user], options[:parent])
end end
end
expose :subscribed do |label, options| class GroupLabel < Label
label.subscribed?(options[:current_user], options[:project]) end
class ProjectLabel < Label
expose :priority do |label, options|
label.priority(options[:parent])
end end
end end
......
# frozen_string_literal: true
module API
class GroupLabels < Grape::API
include PaginationParams
helpers ::API::Helpers::LabelHelpers
before { authenticate! }
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get all labels of the group' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
end
params do
use :pagination
end
get ':id/labels' do
get_labels(user_group, Entities::GroupLabel)
end
desc 'Create a new label' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
end
params do
use :label_create_params
end
post ':id/labels' do
create_label(user_group, Entities::GroupLabel)
end
desc 'Update an existing label. At least one optional parameter is required.' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
end
params do
requires :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 :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'
at_least_one_of :new_name, :color, :description
end
put ':id/labels' do
update_label(user_group, Entities::GroupLabel)
end
desc 'Delete an existing label' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
end
params do
requires :name, type: String, desc: 'The name of the label to be deleted'
end
delete ':id/labels' do
delete_label(user_group)
end
end
end
end
...@@ -84,8 +84,8 @@ module API ...@@ -84,8 +84,8 @@ module API
page || not_found!('Wiki Page') page || not_found!('Wiki Page')
end end
def available_labels_for(label_parent) def available_labels_for(label_parent, include_ancestor_groups: true)
search_params = { include_ancestor_groups: true } search_params = { include_ancestor_groups: include_ancestor_groups }
if label_parent.is_a?(Project) if label_parent.is_a?(Project)
search_params[:project_id] = label_parent.id search_params[:project_id] = label_parent.id
...@@ -170,13 +170,6 @@ module API ...@@ -170,13 +170,6 @@ module API
end end
end end
def find_project_label(id)
labels = available_labels_for(user_project)
label = labels.find_by_id(id) || labels.find_by_title(id)
label || not_found!('Label')
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def find_project_issue(iid) def find_project_issue(iid)
IssuesFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid) IssuesFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
......
# frozen_string_literal: true
module API
module Helpers
module LabelHelpers
extend Grape::API::Helpers
params :label_create_params do
requires :name, type: String, desc: 'The name of the label to be created'
requires :color, type: String, desc: "The 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 description of label to be created'
end
def find_label(parent, id, include_ancestor_groups: true)
labels = available_labels_for(parent, include_ancestor_groups: include_ancestor_groups)
label = labels.find_by_id(id) || labels.find_by_title(id)
label || not_found!('Label')
end
def get_labels(parent, entity)
present paginate(available_labels_for(parent)), with: entity, current_user: current_user, parent: parent
end
def create_label(parent, entity)
authorize! :admin_label, parent
label = available_labels_for(parent).find_by_title(params[:name])
conflict!('Label already exists') if label
priority = params.delete(:priority)
label_params = declared_params(include_missing: false)
label =
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 parent.is_a?(Project)
label.prioritize!(parent, priority) if priority
end
present label, with: entity, current_user: current_user, parent: parent
else
render_validation_error!(label)
end
end
def update_label(parent, entity)
authorize! :admin_label, parent
label = find_label(parent, params[:name], include_ancestor_groups: false)
update_priority = params.key?(:priority)
priority = params.delete(:priority)
label = ::Labels::UpdateService.new(declared_params(include_missing: false)).execute(label)
render_validation_error!(label) unless label.valid?
if parent.is_a?(Project) && update_priority
if priority.nil?
label.unprioritize!(parent)
else
label.prioritize!(parent, priority)
end
end
present label, with: entity, current_user: current_user, parent: parent
end
def delete_label(parent)
authorize! :admin_label, parent
label = find_label(parent, params[:name], include_ancestor_groups: false)
destroy_conditionally!(label)
end
end
end
end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module API module API
class Labels < Grape::API class Labels < Grape::API
include PaginationParams include PaginationParams
helpers ::API::Helpers::LabelHelpers
before { authenticate! } before { authenticate! }
...@@ -11,62 +12,28 @@ module API ...@@ -11,62 +12,28 @@ module API
end end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get all labels of the project' do desc 'Get all labels of the project' do
success Entities::Label success Entities::ProjectLabel
end end
params do params do
use :pagination use :pagination
end end
get ':id/labels' do get ':id/labels' do
present paginate(available_labels_for(user_project)), with: Entities::Label, current_user: current_user, project: user_project get_labels(user_project, Entities::ProjectLabel)
end end
desc 'Create a new label' do desc 'Create a new label' do
success Entities::Label success Entities::ProjectLabel
end end
params do params do
requires :name, type: String, desc: 'The name of the label to be created' use :label_create_params
requires :color, type: String, desc: "The 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 description of label to be created'
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
end end
# rubocop: disable CodeReuse/ActiveRecord
post ':id/labels' do post ':id/labels' do
authorize! :admin_label, user_project create_label(user_project, Entities::ProjectLabel)
label = available_labels_for(user_project).find_by(title: params[:name])
conflict!('Label already exists') if label
priority = params.delete(:priority)
label = ::Labels::CreateService.new(declared_params(include_missing: false)).execute(project: user_project)
if label.valid?
label.prioritize!(user_project, priority) if priority
present label, with: Entities::Label, current_user: current_user, project: user_project
else
render_validation_error!(label)
end
end
# rubocop: enable CodeReuse/ActiveRecord
desc 'Delete an existing label' do
success Entities::Label
end
params do
requires :name, type: String, desc: 'The name of the label to be deleted'
end
# rubocop: disable CodeReuse/ActiveRecord
delete ':id/labels' do
authorize! :admin_label, user_project
label = user_project.labels.find_by(title: params[:name])
not_found!('Label') unless label
destroy_conditionally!(label)
end end
# rubocop: enable CodeReuse/ActiveRecord
desc 'Update an existing label. At least one optional parameter is required.' do desc 'Update an existing label. At least one optional parameter is required.' do
success Entities::Label success Entities::ProjectLabel
end end
params do params do
requires :name, type: String, desc: 'The name of the label to be updated' requires :name, type: String, desc: 'The name of the label to be updated'
...@@ -76,33 +43,19 @@ module API ...@@ -76,33 +43,19 @@ module API
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
at_least_one_of :new_name, :color, :description, :priority at_least_one_of :new_name, :color, :description, :priority
end end
# rubocop: disable CodeReuse/ActiveRecord
put ':id/labels' do put ':id/labels' do
authorize! :admin_label, user_project update_label(user_project, Entities::ProjectLabel)
end
label = user_project.labels.find_by(title: params[:name])
not_found!('Label not found') unless label
update_priority = params.key?(:priority)
priority = params.delete(:priority)
label_params = declared_params(include_missing: false)
# Rename new name to the actual label attribute name
label_params[:name] = label_params.delete(:new_name) if label_params.key?(:new_name)
label = ::Labels::UpdateService.new(label_params).execute(label)
render_validation_error!(label) unless label.valid?
if update_priority
if priority.nil?
label.unprioritize!(user_project)
else
label.prioritize!(user_project, priority)
end
end
present label, with: Entities::Label, current_user: current_user, project: user_project desc 'Delete an existing label' do
success Entities::ProjectLabel
end
params do
requires :name, type: String, desc: 'The name of the label to be deleted'
end
delete ':id/labels' do
delete_label(user_project)
end end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
...@@ -2,51 +2,88 @@ ...@@ -2,51 +2,88 @@
module API module API
class Subscriptions < Grape::API class Subscriptions < Grape::API
helpers ::API::Helpers::LabelHelpers
before { authenticate! } before { authenticate! }
subscribable_types = { subscribables = [
'merge_requests' => proc { |id| find_merge_request_with_access(id, :update_merge_request) }, {
'issues' => proc { |id| find_project_issue(id) }, type: 'merge_requests',
'labels' => proc { |id| find_project_label(id) } entity: Entities::MergeRequest,
} source: Project,
finder: ->(id) { find_merge_request_with_access(id, :update_merge_request) }
},
{
type: 'issues',
entity: Entities::Issue,
source: Project,
finder: ->(id) { find_project_issue(id) }
},
{
type: 'labels',
entity: Entities::ProjectLabel,
source: Project,
finder: ->(id) { find_label(user_project, id) }
},
{
type: 'labels',
entity: Entities::GroupLabel,
source: Group,
finder: ->(id) { find_label(user_group, id) }
}
]
params do subscribables.each do |subscribable|
requires :id, type: String, desc: 'The ID of a project' source_type = subscribable[:source].name.underscore
requires :subscribable_id, type: String, desc: 'The ID of a resource'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
subscribable_types.each do |type, finder|
type_singularized = type.singularize
entity_class = Entities.const_get(type_singularized.camelcase)
params do
requires :id, type: String, desc: "The #{source_type} ID"
requires :subscribable_id, type: String, desc: 'The ID of a resource'
end
resource source_type.pluralize, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Subscribe to a resource' do desc 'Subscribe to a resource' do
success entity_class success subscribable[:entity]
end end
post ":id/#{type}/:subscribable_id/subscribe" do post ":id/#{subscribable[:type]}/:subscribable_id/subscribe" do
resource = instance_exec(params[:subscribable_id], &finder) parent = parent_resource(source_type)
resource = instance_exec(params[:subscribable_id], &subscribable[:finder])
if resource.subscribed?(current_user, user_project) if resource.subscribed?(current_user, parent)
not_modified! not_modified!
else else
resource.subscribe(current_user, user_project) resource.subscribe(current_user, parent)
present resource, with: entity_class, current_user: current_user, project: user_project present resource, with: subscribable[:entity], current_user: current_user, project: parent, parent: parent
end end
end end
desc 'Unsubscribe from a resource' do desc 'Unsubscribe from a resource' do
success entity_class success subscribable[:entity]
end end
post ":id/#{type}/:subscribable_id/unsubscribe" do post ":id/#{subscribable[:type]}/:subscribable_id/unsubscribe" do
resource = instance_exec(params[:subscribable_id], &finder) parent = parent_resource(source_type)
resource = instance_exec(params[:subscribable_id], &subscribable[:finder])
if !resource.subscribed?(current_user, user_project) if !resource.subscribed?(current_user, parent)
not_modified! not_modified!
else else
resource.unsubscribe(current_user, user_project) resource.unsubscribe(current_user, parent)
present resource, with: entity_class, current_user: current_user, project: user_project present resource, with: subscribable[:entity], current_user: current_user, project: parent, parent: parent
end end
end end
end end
end end
private
helpers do
def parent_resource(source_type)
case source_type
when 'project'
user_project
else
nil
end
end
end
end end
end end
{
"type": "array",
"items": {
"type": "object",
"properties" : {
"id" : { "type": "integer" },
"name" : { "type": "string "},
"color" : { "type": "string "},
"description" : { "type": "string "},
"open_issues_count" : { "type": "integer "},
"closed_issues_count" : { "type": "integer "},
"open_merge_requests_count" : { "type": "integer "},
"subscribed" : { "type": "boolean" },
"priority" : { "type": "null" }
},
"additionalProperties": false
}
}
# frozen_string_literal: true
require 'spec_helper'
describe API::GroupLabels do
let(:user) { create(:user) }
let(:group) { create(:group) }
let!(:group_member) { create(:group_member, group: group, user: user) }
let!(:label1) { create(:group_label, title: 'feature', group: group) }
let!(:label2) { create(:group_label, title: 'bug', group: group) }
describe 'GET :id/labels' do
it 'returns all available labels for the group' do
get api("/groups/#{group.id}/labels", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/group_labels')
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(2)
expect(json_response.map {|r| r['name'] }).to contain_exactly('feature', 'bug')
end
end
describe 'POST /groups/:id/labels' do
it 'returns created label when all params are given' do
post api("/groups/#{group.id}/labels", user),
params: {
name: 'Foo',
color: '#FFAABB',
description: 'test'
}
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq('Foo')
expect(json_response['color']).to eq('#FFAABB')
expect(json_response['description']).to eq('test')
end
it 'returns created label when only required params are given' do
post api("/groups/#{group.id}/labels", user),
params: {
name: 'Foo & Bar',
color: '#FFAABB'
}
expect(response.status).to eq(201)
expect(json_response['name']).to eq('Foo & Bar')
expect(json_response['color']).to eq('#FFAABB')
expect(json_response['description']).to be_nil
end
it 'returns a 400 bad request if name not given' do
post api("/groups/#{group.id}/labels", user), params: { color: '#FFAABB' }
expect(response).to have_gitlab_http_status(400)
end
it 'returns a 400 bad request if color is not given' do
post api("/groups/#{group.id}/labels", user), params: { name: 'Foobar' }
expect(response).to have_gitlab_http_status(400)
end
it 'returns 409 if label already exists' do
post api("/groups/#{group.id}/labels", user),
params: {
name: label1.name,
color: '#FFAABB'
}
expect(response).to have_gitlab_http_status(409)
expect(json_response['message']).to eq('Label already exists')
end
end
describe 'DELETE /groups/:id/labels' do
it 'returns 204 for existing label' do
delete api("/groups/#{group.id}/labels", user), params: { name: label1.name }
expect(response).to have_gitlab_http_status(204)
end
it 'returns 404 for non existing label' do
delete api("/groups/#{group.id}/labels", user), params: { name: 'label2' }
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 Label Not Found')
end
it 'returns 400 for wrong parameters' do
delete api("/groups/#{group.id}/labels", user)
expect(response).to have_gitlab_http_status(400)
end
it "does not delete parent's group labels", :nested_groups do
subgroup = create(:group, parent: group)
subgroup_label = create(:group_label, title: 'feature', group: subgroup)
delete api("/groups/#{subgroup.id}/labels", user), params: { name: subgroup_label.name }
expect(response).to have_gitlab_http_status(204)
expect(subgroup.labels.size).to eq(0)
expect(group.labels).to include(label1)
end
it_behaves_like '412 response' do
let(:request) { api("/groups/#{group.id}/labels", user) }
let(:params) { { name: label1.name } }
end
end
describe 'PUT /groups/:id/labels' do
it 'returns 200 if name and colors and description are changed' do
put api("/groups/#{group.id}/labels", user),
params: {
name: label1.name,
new_name: 'New Label',
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 "does not update parent's group label", :nested_groups do
subgroup = create(:group, parent: group)
subgroup_label = create(:group_label, title: 'feature', group: subgroup)
put api("/groups/#{subgroup.id}/labels", user),
params: {
name: subgroup_label.name,
new_name: 'New Label'
}
expect(response).to have_gitlab_http_status(200)
expect(subgroup.labels[0].name).to eq('New Label')
expect(label1.name).to eq('feature')
end
it 'returns 404 if label does not exist' do
put api("/groups/#{group.id}/labels", user),
params: {
name: 'label2',
new_name: 'label3'
}
expect(response).to have_gitlab_http_status(404)
end
it 'returns 400 if no label name given' do
put api("/groups/#{group.id}/labels", user), params: { new_name: label1.name }
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("/groups/#{group.id}/labels", user), params: { name: label1.name }
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eq('new_name, color, description are missing, '\
'at least one parameter must be provided')
end
end
describe 'POST /groups/:id/labels/:label_id/subscribe' do
context 'when label_id is a label title' do
it 'subscribes to the label' do
post api("/groups/#{group.id}/labels/#{label1.title}/subscribe", user)
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(label1.title)
expect(json_response['subscribed']).to be_truthy
end
end
context 'when label_id is a label ID' do
it 'subscribes to the label' do
post api("/groups/#{group.id}/labels/#{label1.id}/subscribe", user)
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(label1.title)
expect(json_response['subscribed']).to be_truthy
end
end
context 'when user is already subscribed to label' do
before do
label1.subscribe(user)
end
it 'returns 304' do
post api("/groups/#{group.id}/labels/#{label1.id}/subscribe", user)
expect(response).to have_gitlab_http_status(304)
end
end
context 'when label ID is not found' do
it 'returns 404 error' do
post api("/groups/#{group.id}/labels/1234/subscribe", user)
expect(response).to have_gitlab_http_status(404)
end
end
end
describe 'POST /groups/:id/labels/:label_id/unsubscribe' do
before do
label1.subscribe(user)
end
context 'when label_id is a label title' do
it 'unsubscribes from the label' do
post api("/groups/#{group.id}/labels/#{label1.title}/unsubscribe", user)
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(label1.title)
expect(json_response['subscribed']).to be_falsey
end
end
context 'when label_id is a label ID' do
it 'unsubscribes from the label' do
post api("/groups/#{group.id}/labels/#{label1.id}/unsubscribe", user)
expect(response).to have_gitlab_http_status(201)
expect(json_response['name']).to eq(label1.title)
expect(json_response['subscribed']).to be_falsey
end
end
context 'when user is already unsubscribed from label' do
before do
label1.unsubscribe(user)
end
it 'returns 304' do
post api("/groups/#{group.id}/labels/#{label1.id}/unsubscribe", user)
expect(response).to have_gitlab_http_status(304)
end
end
context 'when label ID is not found' do
it 'returns 404 error' do
post api("/groups/#{group.id}/labels/1234/unsubscribe", user)
expect(response).to have_gitlab_http_status(404)
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