summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKamil TrzciƄski <ayufan@ayufan.eu>2018-02-14 23:09:15 +0000
committerMark Fletcher <mark@gitlab.com>2018-02-15 11:43:32 +0000
commit7a7e6a26d2e07a9cedb730b6f2ad0a4e8332d33a (patch)
tree3b6d282b005ef1b7394b77586a917ac91ba5001e
parent80daee8fa82c24c957e7f56987d0b2503e5dc672 (diff)
downloadgitlab-ce-7a7e6a26d2e07a9cedb730b6f2ad0a4e8332d33a.tar.gz
Merge branch 'mc/fix/project-variables-scope' into 'master'
Resolve "Project variables validate without any scopes disregarding environment_scope" Closes #43191 See merge request gitlab-org/gitlab-ce!17086
-rw-r--r--app/models/project.rb2
-rw-r--r--app/validators/variable_duplicates_validator.rb21
-rw-r--r--changelogs/unreleased/fix-validation-of-environment-scope-for-variables.yml5
-rw-r--r--spec/fixtures/api/schemas/variable.json3
-rw-r--r--spec/support/features/variable_list_shared_examples.rb2
-rw-r--r--spec/validators/variable_duplicates_validator_spec.rb67
6 files changed, 94 insertions, 6 deletions
diff --git a/app/models/project.rb b/app/models/project.rb
index 3893b1818f3..2ba6a863500 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -261,7 +261,7 @@ class Project < ActiveRecord::Base
validates :repository_storage,
presence: true,
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
- validates :variables, variable_duplicates: true
+ validates :variables, variable_duplicates: { scope: :environment_scope }
has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
diff --git a/app/validators/variable_duplicates_validator.rb b/app/validators/variable_duplicates_validator.rb
index 8a9d8892e9b..4bfa3c45303 100644
--- a/app/validators/variable_duplicates_validator.rb
+++ b/app/validators/variable_duplicates_validator.rb
@@ -1,13 +1,28 @@
# VariableDuplicatesValidator
#
-# This validtor is designed for especially the following condition
+# This validator is designed for especially the following condition
# - Use `accepts_nested_attributes_for :xxx` in a parent model
# - Use `validates :xxx, uniqueness: { scope: :xxx_id }` in a child model
class VariableDuplicatesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- duplicates = value.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first)
+ if options[:scope]
+ scoped = value.group_by do |variable|
+ Array(options[:scope]).map { |attr| variable.send(attr) } # rubocop:disable GitlabSecurity/PublicSend
+ end
+ scoped.each_value { |scope| validate_duplicates(record, attribute, scope) }
+ else
+ validate_duplicates(record, attribute, value)
+ end
+ end
+
+ private
+
+ def validate_duplicates(record, attribute, values)
+ duplicates = values.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first)
if duplicates.any?
- record.errors.add(attribute, "Duplicate variables: #{duplicates.join(", ")}")
+ error_message = "have duplicate values (#{duplicates.join(", ")})"
+ error_message += " for #{values.first.send(options[:scope])} scope" if options[:scope] # rubocop:disable GitlabSecurity/PublicSend
+ record.errors.add(attribute, error_message)
end
end
end
diff --git a/changelogs/unreleased/fix-validation-of-environment-scope-for-variables.yml b/changelogs/unreleased/fix-validation-of-environment-scope-for-variables.yml
new file mode 100644
index 00000000000..5424c15a8ae
--- /dev/null
+++ b/changelogs/unreleased/fix-validation-of-environment-scope-for-variables.yml
@@ -0,0 +1,5 @@
+---
+title: Fix validation of environment scope of variables
+merge_request:
+author:
+type: fixed
diff --git a/spec/fixtures/api/schemas/variable.json b/spec/fixtures/api/schemas/variable.json
index 78977118b0a..6f6b044115b 100644
--- a/spec/fixtures/api/schemas/variable.json
+++ b/spec/fixtures/api/schemas/variable.json
@@ -10,7 +10,8 @@
"id": { "type": "integer" },
"key": { "type": "string" },
"value": { "type": "string" },
- "protected": { "type": "boolean" }
+ "protected": { "type": "boolean" },
+ "environment_scope": { "type": "string", "optional": true }
},
"additionalProperties": false
}
diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb
index 4315bf5d037..0d8f7a7aae6 100644
--- a/spec/support/features/variable_list_shared_examples.rb
+++ b/spec/support/features/variable_list_shared_examples.rb
@@ -263,7 +263,7 @@ shared_examples 'variable list' do
# We check the first row because it re-sorts to alphabetical order on refresh
page.within('.js-ci-variable-list-section') do
- expect(find('.js-ci-variable-error-box')).to have_content('Validation failed Variables Duplicate variables: samekey')
+ expect(find('.js-ci-variable-error-box')).to have_content(/Validation failed Variables have duplicate values \(.+\)/)
end
end
end
diff --git a/spec/validators/variable_duplicates_validator_spec.rb b/spec/validators/variable_duplicates_validator_spec.rb
new file mode 100644
index 00000000000..0b71a67f94d
--- /dev/null
+++ b/spec/validators/variable_duplicates_validator_spec.rb
@@ -0,0 +1,67 @@
+require 'spec_helper'
+
+describe VariableDuplicatesValidator do
+ let(:validator) { described_class.new(attributes: [:variables], **options) }
+
+ describe '#validate_each' do
+ let(:project) { build(:project) }
+
+ subject { validator.validate_each(project, :variables, project.variables) }
+
+ context 'with no scope' do
+ let(:options) { {} }
+ let(:variables) { build_list(:ci_variable, 2, project: project) }
+
+ before do
+ project.variables << variables
+ end
+
+ it 'does not have any errors' do
+ subject
+
+ expect(project.errors.empty?).to be true
+ end
+
+ context 'with duplicates' do
+ before do
+ project.variables.build(key: variables.first.key, value: 'dummy_value')
+ end
+
+ it 'has a duplicate key error' do
+ subject
+
+ expect(project.errors).to have_key(:variables)
+ end
+ end
+ end
+
+ context 'with a scope attribute' do
+ let(:options) { { scope: :environment_scope } }
+ let(:first_variable) { build(:ci_variable, key: 'test_key', environment_scope: '*', project: project) }
+ let(:second_variable) { build(:ci_variable, key: 'test_key', environment_scope: 'prod', project: project) }
+
+ before do
+ project.variables << first_variable
+ project.variables << second_variable
+ end
+
+ it 'does not have any errors' do
+ subject
+
+ expect(project.errors.empty?).to be true
+ end
+
+ context 'with duplicates' do
+ before do
+ project.variables.build(key: second_variable.key, value: 'dummy_value', environment_scope: second_variable.environment_scope)
+ end
+
+ it 'has a duplicate key error' do
+ subject
+
+ expect(project.errors).to have_key(:variables)
+ end
+ end
+ end
+ end
+end