diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 0a5fe24b5af5c57dca858ac0a8253c56baff7db8..5c9b44812be654dbb1ffa3eb42a91ca7e839f0de 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -138,6 +138,10 @@ module Issuable labels.order('title ASC').pluck(:title) end + def remove_labels + labels.delete_all + end + def add_labels_by_names(label_names) label_names.each do |label_name| label = project.labels.create_with( diff --git a/app/models/label.rb b/app/models/label.rb index 3ff52416c24f8ae2770b4f3eba214b016173618e..233f477444fb0f05ffffed041f31b8e30c2fc9d9 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -6,14 +6,14 @@ class Label < ActiveRecord::Base has_many :issues, through: :label_links, source: :target, source_type: 'Issue' validates :color, - format: { with: /\A\#[0-9A-Fa-f]{6}+\Z/ }, + format: { with: /\A#[0-9A-Fa-f]{6}\Z/ }, allow_blank: false validates :project, presence: true # Don't allow '?', '&', and ',' for label titles validates :title, presence: true, - format: { with: /\A[^&\?,&]*\z/ }, + format: { with: /\A[^&\?,&]+\z/ }, uniqueness: { scope: :project_id } scope :order_by_name, -> { reorder("labels.title ASC") } diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index d36b29a00b1c8dd4dd16c1525ca8daf4b69c5b02..6af0f6d1b252b3a296bb289779b771511dee55ca 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -114,17 +114,21 @@ module API # Helper method for validating all labels against its names def validate_label_params(params) + errors = {} + if params[:labels].present? params[:labels].split(',').each do |label_name| label = user_project.labels.create_with( color: Label::DEFAULT_COLOR).find_or_initialize_by( title: label_name.strip) + if label.invalid? - return true + errors[label.title] = label.errors end end end - false + + errors end # error helpers diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 055529ccbd89a10ff8f42697f3f1741ac336acb2..eb6a74cd2bc21be5944d090ab5f57e9dd95635c4 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -52,8 +52,8 @@ module API attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id] # Validate label names in advance - if validate_label_params(params) - return render_api_error!('Label names invalid', 405) + if (errors = validate_label_params(params)).any? + render_api_error!({ labels: errors }, 400) end issue = ::Issues::CreateService.new(user_project, current_user, attrs).execute @@ -90,8 +90,8 @@ module API attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event] # Validate label names in advance - if validate_label_params(params) - return render_api_error!('Label names invalid', 405) + if (errors = validate_label_params(params)).any? + render_api_error!({ labels: errors }, 400) end issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue) @@ -99,7 +99,8 @@ module API if issue.valid? # Find or create labels and attach to issue. Labels are valid because # we already checked its name, so there can't be an error here - if params[:labels].present? + unless params[:labels].nil? + issue.remove_labels # Create and add labels to the new created issue issue.add_labels_by_names(params[:labels].split(',')) end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index d8e8e4f5035e54958b3a6680c98487e11c40467e..08dc94ebdf3f4994e18b5ef6f46c43fe277114cd 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -73,12 +73,12 @@ describe API::API, api: true do response.status.should == 400 end - it 'should return 405 on invalid label names' do + it 'should return 400 on invalid label names' do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, ?' - response.status.should == 405 - json_response['message'].should == 'Label names invalid' + response.status.should == 400 + json_response['message']['labels']['?']['title'].should == ['is invalid'] end end @@ -97,12 +97,56 @@ describe API::API, api: true do response.status.should == 404 end - it 'should return 405 on invalid label names' do + it 'should return 400 on invalid label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title', labels: 'label, ?' - response.status.should == 405 - json_response['message'].should == 'Label names invalid' + response.status.should == 400 + json_response['message']['labels']['?']['title'].should == ['is invalid'] + end + end + + describe 'PUT /projects/:id/issues/:issue_id to update labels' do + let!(:label) { create(:label, title: 'dummy', project: project) } + let!(:label_link) { create(:label_link, label: label, target: issue) } + + it 'should not update labels if not present' do + put api("/projects/#{project.id}/issues/#{issue.id}", user), + title: 'updated title' + response.status.should == 200 + json_response['labels'].should == [label.title] + end + + it 'should remove all labels' do + put api("/projects/#{project.id}/issues/#{issue.id}", user), + labels: '' + response.status.should == 200 + json_response['labels'].should == [] + end + + it 'should update labels' do + put api("/projects/#{project.id}/issues/#{issue.id}", user), + labels: 'foo,bar' + response.status.should == 200 + json_response['labels'].should include 'foo' + json_response['labels'].should include 'bar' + end + + it 'should return 400 on invalid label names' do + put api("/projects/#{project.id}/issues/#{issue.id}", user), + labels: 'label, ?' + response.status.should == 400 + json_response['message']['labels']['?']['title'].should == ['is invalid'] + end + + it 'should allow special label names' do + put api("/projects/#{project.id}/issues/#{issue.id}", user), + labels: 'label:foo, label-bar,label_bar,label/bar' + response.status.should == 200 + json_response['labels'].should include 'label:foo' + json_response['labels'].should include 'label-bar' + json_response['labels'].should include 'label_bar' + json_response['labels'].should include 'label/bar' end end diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index cd1b84c53c9eda6189a42d1d3930414b3d6b0175..ee9088933a1cf609fda1864779d8be4bb284c0d6 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -50,6 +50,14 @@ describe API::API, api: true do json_response['message'].should == 'Color is invalid' end + it 'should return 400 for too long color code' do + post api("/projects/#{project.id}/labels", user), + name: 'Foo', + color: '#FFAAFFFF' + response.status.should == 400 + json_response['message'].should == 'Color is invalid' + end + it 'should return 400 for invalid name' do post api("/projects/#{project.id}/labels", user), name: '?', @@ -147,5 +155,13 @@ describe API::API, api: true do response.status.should == 400 json_response['message'].should == 'Color is invalid' end + + it 'should return 400 for too long color code' do + post api("/projects/#{project.id}/labels", user), + name: 'Foo', + color: '#FFAAFFFF' + response.status.should == 400 + json_response['message'].should == 'Color is invalid' + end end end