summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--app/models/label.rb4
-rw-r--r--app/models/project.rb4
-rw-r--r--doc/api/README.md1
-rw-r--r--doc/api/labels.md85
-rw-r--r--doc/api/projects.md26
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/entities.rb2
-rw-r--r--lib/api/labels.rb100
-rw-r--r--lib/api/projects.rb11
-rw-r--r--spec/requests/api/labels_spec.rb127
11 files changed, 321 insertions, 41 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 50dc6b8945b..c19826bc5c1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -11,6 +11,7 @@ v 7.2.0
- Fix bug when MR download patch return invalid diff
- Test gitlab-shell integration
- Repository import timeout increased from 2 to 4 minutes allowing larger repos to be imported
+ - API for labels (Robert Schilling)
v 7.1.1
- Fix cpu usage issue in Firefox
diff --git a/app/models/label.rb b/app/models/label.rb
index c32efc7c47f..819d6cefa41 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -18,9 +18,7 @@ class Label < ActiveRecord::Base
scope :order_by_name, -> { reorder("labels.title ASC") }
- def name
- title
- end
+ alias_attribute :name, :title
def open_issues_count
issues.opened.count
diff --git a/app/models/project.rb b/app/models/project.rb
index a24eae7d26b..7f6aa6d4249 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -577,4 +577,8 @@ class Project < ActiveRecord::Base
def forks_count
ForkedProjectLink.where(forked_from_project_id: self.id).count
end
+
+ def find_label(name)
+ labels.find_by(name: name)
+ end
end
diff --git a/doc/api/README.md b/doc/api/README.md
index a0a9ba6f4b6..44e95ed8258 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -12,6 +12,7 @@
- [Branches](branches.md)
- [Merge Requests](merge_requests.md)
- [Issues](issues.md)
+- [Labels](labels.md)
- [Milestones](milestones.md)
- [Notes](notes.md) (comments)
- [Deploy Keys](deploy_keys.md)
diff --git a/doc/api/labels.md b/doc/api/labels.md
new file mode 100644
index 00000000000..95fd4e84119
--- /dev/null
+++ b/doc/api/labels.md
@@ -0,0 +1,85 @@
+# Labels
+
+## List labels
+
+Get all labels for given project.
+
+```
+GET /projects/:id/labels
+```
+
+```json
+[
+ {
+ "name": "Awesome",
+ "color": "#DD10AA"
+ },
+ {
+ "name": "Documentation",
+ "color": "#1E80DD"
+ },
+ {
+ "name": "Feature",
+ "color": "#11FF22"
+ },
+ {
+ "name": "Bug",
+ "color": "#EE1122"
+ }
+]
+```
+
+## Create a new label
+
+Creates a new label for given repository with given name and color.
+
+```
+POST /projects/:id/labels
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `name` (required) - The name of the label
+- `color` (required) - Color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)
+
+It returns 200 and the newly created label, if the operation succeeds.
+If the label already exists, 409 and an error message is returned.
+If label parameters are invalid, 405 and an explaining error message is returned.
+
+## Delete a label
+
+Deletes a label given by its name.
+
+```
+DELETE /projects/:id/labels
+```
+
+- `id` (required) - The ID of a project
+- `name` (required) - The name of the label to be deleted
+
+It returns 200 if the label successfully was deleted, 404 for wrong parameters
+and 400 if the label does not exist.
+In case of an error, additionally an error message is returned.
+
+## Edit an existing label
+
+Updates an existing label with new name or now color. At least one parameter
+is required, to update the label.
+
+```
+PUT /projects/:id/labels
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `name` (required) - The name of the existing label
+- `new_name` (optional) - The new name of the label
+- `color` (optional) - New color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)
+
+On success, this method returns 200 with the updated label.
+If required parameters are missing, 400 is returned.
+If the label to be updated is missing, 404 is returned.
+If parameters are invalid, 405 is returned. In case of an error,
+additionally an error message is returned.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index b8876e8e104..894c2fd76a4 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -632,29 +632,3 @@ Parameters:
+ query (required) - A string contained in the project name
+ per_page (optional) - number of projects to return per page
+ page (optional) - the page to retrieve
-
-
-## Labels
-
-### List project labels
-
-Get a list of project labels.
-
-```
-GET /projects/:id/labels
-```
-
-Parameters:
-
-+ `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project
-
-```json
-[
- {
- "name": "feature"
- },
- {
- "name": "bug"
- }
-]
-```
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ce4cc8b34f7..2c7cd9038c3 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -46,5 +46,6 @@ module API
mount Commits
mount Namespaces
mount Branches
+ mount Labels
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index fc7d391fd30..74fdef93543 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -194,7 +194,7 @@ module API
end
class Label < Grape::Entity
- expose :name
+ expose :name, :color
end
class RepoDiff < Grape::Entity
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
new file mode 100644
index 00000000000..c73a4dbe916
--- /dev/null
+++ b/lib/api/labels.rb
@@ -0,0 +1,100 @@
+module API
+ # Labels API
+ class Labels < Grape::API
+ before { authenticate! }
+
+ resource :projects do
+ # Get all labels of the project
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # Example Request:
+ # GET /projects/:id/labels
+ get ':id/labels' do
+ present user_project.labels, with: Entities::Label
+ end
+
+ # Creates a new label
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # name (required) - The name of the label to be deleted
+ # color (required) - Color of the label given in 6-digit hex
+ # notation with leading '#' sign (e.g. #FFAABB)
+ # Example Request:
+ # POST /projects/:id/labels
+ post ':id/labels' do
+ required_attributes! [:name, :color]
+
+ attrs = attributes_for_keys [:name, :color]
+ label = user_project.find_label(attrs[:name])
+
+ if label
+ return render_api_error!('Label already exists', 409)
+ end
+
+ label = user_project.labels.create(attrs)
+
+ if label.valid?
+ present label, with: Entities::Label
+ else
+ render_api_error!(label.errors.full_messages.join(', '), 405)
+ end
+ end
+
+ # Deletes an existing label
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # name (required) - The name of the label to be deleted
+ #
+ # Example Request:
+ # DELETE /projects/:id/labels
+ delete ':id/labels' do
+ required_attributes! [:name]
+
+ label = user_project.find_label(params[:name])
+ if !label
+ return render_api_error!('Label not found', 404)
+ end
+
+ label.destroy
+ end
+
+ # Updates an existing label. At least one optional parameter is required.
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # name (optional) - The name of the label to be deleted
+ # color (optional) - Color of the label given in 6-digit hex
+ # notation with leading '#' sign (e.g. #FFAABB)
+ # Example Request:
+ # PUT /projects/:id/labels
+ put ':id/labels' do
+ required_attributes! [:name]
+
+ label = user_project.find_label(params[:name])
+ if !label
+ return render_api_error!('Label not found', 404)
+ end
+
+ attrs = attributes_for_keys [:new_name, :color]
+
+ if attrs.empty?
+ return render_api_error!('Required parameters "name" or "color" ' \
+ 'missing',
+ 400)
+ end
+
+ # Rename new name to the actual label attribute name
+ attrs[:name] = attrs.delete(:new_name) if attrs.key?(:new_name)
+
+ if label.update(attrs)
+ present label, with: Entities::Label
+ else
+ render_api_error!(label.errors.full_messages.join(', '), 405)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 149678e6803..55f7975bbf7 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -224,17 +224,6 @@ module API
@users = paginate @users
present @users, with: Entities::UserBasic
end
-
- # Get a project labels
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # GET /projects/:id/labels
- get ':id/labels' do
- @labels = user_project.labels
- present @labels, with: Entities::Label
- end
end
end
end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index d40c2c21cec..b06b353333d 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -21,4 +21,131 @@ describe API::API, api: true do
json_response.first['name'].should == label1.name
end
end
+
+ describe 'POST /projects/:id/labels' do
+ it 'should return created label' do
+ post api("/projects/#{project.id}/labels", user),
+ name: 'Foo',
+ color: '#FFAABB'
+ response.status.should == 201
+ json_response['name'].should == 'Foo'
+ json_response['color'].should == '#FFAABB'
+ end
+
+ it 'should return a 400 bad request if name not given' do
+ post api("/projects/#{project.id}/labels", user), color: '#FFAABB'
+ response.status.should == 400
+ end
+
+ it 'should return a 400 bad request if color not given' do
+ post api("/projects/#{project.id}/labels", user), name: 'Foobar'
+ response.status.should == 400
+ end
+
+ it 'should return 405 for invalid color' do
+ post api("/projects/#{project.id}/labels", user),
+ name: 'Foo',
+ color: '#FFAA'
+ response.status.should == 405
+ json_response['message'].should == 'Color is invalid'
+ end
+
+ it 'should return 405 for invalid name' do
+ post api("/projects/#{project.id}/labels", user),
+ name: '?',
+ color: '#FFAABB'
+ response.status.should == 405
+ json_response['message'].should == 'Title is invalid'
+ end
+
+ it 'should return 409 if label already exists' do
+ post api("/projects/#{project.id}/labels", user),
+ name: 'label1',
+ color: '#FFAABB'
+ response.status.should == 409
+ json_response['message'].should == 'Label already exists'
+ end
+ end
+
+ describe 'DELETE /projects/:id/labels' do
+ it 'should return 200 for existing label' do
+ delete api("/projects/#{project.id}/labels", user), name: 'label1'
+ response.status.should == 200
+ end
+
+ it 'should return 404 for non existing label' do
+ delete api("/projects/#{project.id}/labels", user), name: 'label2'
+ response.status.should == 404
+ json_response['message'].should == 'Label not found'
+ end
+
+ it 'should return 400 for wrong parameters' do
+ delete api("/projects/#{project.id}/labels", user)
+ response.status.should == 400
+ end
+ end
+
+ describe 'PUT /projects/:id/labels' do
+ it 'should return 200 if name and colors are changed' do
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
+ new_name: 'New Label',
+ color: '#FFFFFF'
+ response.status.should == 200
+ json_response['name'].should == 'New Label'
+ json_response['color'].should == '#FFFFFF'
+ end
+
+ it 'should return 200 if name is changed' do
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
+ new_name: 'New Label'
+ response.status.should == 200
+ json_response['name'].should == 'New Label'
+ json_response['color'].should == label1.color
+ end
+
+ it 'should return 200 if colors is changed' do
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
+ color: '#FFFFFF'
+ response.status.should == 200
+ json_response['name'].should == label1.name
+ json_response['color'].should == '#FFFFFF'
+ end
+
+ it 'should return 404 if label does not exist' do
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label2',
+ new_name: 'label3'
+ response.status.should == 404
+ end
+
+ it 'should return 400 if no label name given' do
+ put api("/projects/#{project.id}/labels", user), new_name: 'label2'
+ response.status.should == 400
+ end
+
+ it 'should return 400 if no new parameters given' do
+ put api("/projects/#{project.id}/labels", user), name: 'label1'
+ response.status.should == 400
+ end
+
+ it 'should return 405 for invalid name' do
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
+ new_name: '?',
+ color: '#FFFFFF'
+ response.status.should == 405
+ json_response['message'].should == 'Title is invalid'
+ end
+
+ it 'should return 405 for invalid name' do
+ put api("/projects/#{project.id}/labels", user),
+ name: 'label1',
+ color: '#FF'
+ response.status.should == 405
+ json_response['message'].should == 'Color is invalid'
+ end
+ end
end