Commit e24f1562 authored by Stan Hu's avatar Stan Hu

Merge branch 'ce-to-ee-2018-02-19' into 'master'

CE upstream - 2018-02-19 18:23 UTC

Closes #4984, #4864, gitlab-qa#190, and gitlab-ce#40854

See merge request gitlab-org/gitlab-ee!4605
parents a42996fb 186ac5fa
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.status-neutral, .status-neutral,
.status-red, { .status-red, {
height: 100%; height: 100%;
min-width: 25px; min-width: 30px;
padding: 0 5px; padding: 0 5px;
font-size: $tooltip-font-size; font-size: $tooltip-font-size;
font-weight: normal; font-weight: normal;
......
...@@ -7,18 +7,11 @@ module Emails ...@@ -7,18 +7,11 @@ module Emails
helper_method :member_source, :member helper_method :member_source, :member
end end
def member_access_requested_email(member_source_type, member_id) def member_access_requested_email(member_source_type, member_id, recipient_notification_email)
@member_source_type = member_source_type @member_source_type = member_source_type
@member_id = member_id @member_id = member_id
admins = member_source.members.owners_and_masters.pluck(:notification_email) mail(to: recipient_notification_email,
# A project in a group can have no explicit owners/masters, in that case
# we fallbacks to the group's owners/masters.
if admins.empty? && member_source.respond_to?(:group) && member_source.group
admins = member_source.group.members.owners_and_masters.pluck(:notification_email)
end
mail(to: admins,
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}")) subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end end
......
...@@ -231,7 +231,12 @@ class NotificationService ...@@ -231,7 +231,12 @@ class NotificationService
def new_access_request(member) def new_access_request(member)
return true unless member.notifiable?(:subscription) return true unless member.notifiable?(:subscription)
mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later recipients = member.source.members.owners_and_masters
if fallback_to_group_owners_masters?(recipients, member)
recipients = member.source.group.members.owners_and_masters
end
recipients.each { |recipient| deliver_access_request_email(recipient, member) }
end end
def decline_access_request(member) def decline_access_request(member)
...@@ -482,4 +487,14 @@ class NotificationService ...@@ -482,4 +487,14 @@ class NotificationService
def notifiable_users(*args) def notifiable_users(*args)
NotificationRecipientService.notifiable_users(*args) NotificationRecipientService.notifiable_users(*args)
end end
def deliver_access_request_email(recipient, member)
mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later
end
def fallback_to_group_owners_masters?(recipients, member)
return false if recipients.present?
member.source.respond_to?(:group) && member.source.group
end
end end
---
title: Fix long list of recipients on group request membership email
merge_request: 17121
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: "[GitHub Import] Create an empty wiki if wiki import failed"
merge_request:
author:
type: fixed
---
title: Increase feature flag cache TTL to one hour
merge_request:
author:
type: performance
---
title: Fix single digit value clipping for stacked progress bar
merge_request: 17217
author:
type: fixed
...@@ -8,7 +8,7 @@ Flipper.configure do |config| ...@@ -8,7 +8,7 @@ Flipper.configure do |config|
cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new( cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new(
adapter, adapter,
Rails.cache, Rails.cache,
expires_in: 10.seconds) expires_in: 1.hour)
Flipper.new(cached_adapter) Flipper.new(cached_adapter)
end end
......
...@@ -63,6 +63,7 @@ module Gitlab ...@@ -63,6 +63,7 @@ module Gitlab
true true
rescue Gitlab::Shell::Error => e rescue Gitlab::Shell::Error => e
if e.message !~ /repository not exported/ if e.message !~ /repository not exported/
project.create_wiki
fail_import("Failed to import the wiki: #{e.message}") fail_import("Failed to import the wiki: #{e.message}")
else else
true true
......
...@@ -4,18 +4,6 @@ module QA ...@@ -4,18 +4,6 @@ module QA
class SecretVariable < Factory::Base class SecretVariable < Factory::Base
attr_accessor :key, :value attr_accessor :key, :value
product :key do
Page::Project::Settings::CICD.act do
expand_secret_variables(&:variable_key)
end
end
product :value do
Page::Project::Settings::CICD.act do
expand_secret_variables(&:variable_value)
end
end
dependency Factory::Resource::Project, as: :project do |project| dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-secret-variables' project.name = 'project-with-secret-variables'
project.description = 'project for adding secret variable test' project.description = 'project for adding secret variable test'
......
...@@ -98,6 +98,10 @@ module QA ...@@ -98,6 +98,10 @@ module QA
views.map(&:errors).flatten views.map(&:errors).flatten
end end
def self.elements
views.map(&:elements).flatten
end
class DSL class DSL
attr_reader :views attr_reader :views
......
...@@ -6,39 +6,37 @@ module QA ...@@ -6,39 +6,37 @@ module QA
include Common include Common
view 'app/views/ci/variables/_variable_row.html.haml' do view 'app/views/ci/variables/_variable_row.html.haml' do
element :variable_row, '.ci-variable-row-body'
element :variable_key, '.js-ci-variable-input-key' element :variable_key, '.js-ci-variable-input-key'
element :variable_value, '.js-ci-variable-input-value' element :variable_value, '.js-ci-variable-input-value'
element :key_placeholder, 'Input variable key'
element :value_placeholder, 'Input variable value'
end end
view 'app/views/ci/variables/_index.html.haml' do view 'app/views/ci/variables/_index.html.haml' do
element :save_variables, '.js-secret-variables-save-button' element :save_variables, '.js-secret-variables-save-button'
element :reveal_values, '.js-secret-value-reveal-button'
end end
def fill_variable_key(key) def fill_variable_key(key)
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do fill_in('Input variable key', with: key, match: :first)
page.find('.js-ci-variable-input-key').set(key)
end
end end
def fill_variable_value(value) def fill_variable_value(value)
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do fill_in('Input variable value', with: value, match: :first)
page.find('.js-ci-variable-input-value').set(value)
end
end end
def save_variables def save_variables
click_button('Save variables') find('.js-secret-variables-save-button').click
end end
def variable_key def reveal_variables
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do find('.js-secret-value-reveal-button').click
page.find('.js-ci-variable-input-key').value
end
end end
def variable_value def variable_value(key)
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do within('.ci-variable-row-body', text: key) do
page.find('.js-ci-variable-input-value').value find('.js-ci-variable-input-value').value
end end
end end
end end
......
...@@ -4,16 +4,21 @@ module QA ...@@ -4,16 +4,21 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials } Page::Main::Login.act { sign_in_using_credentials }
variable_key = 'VARIABLE_KEY' Factory::Resource::SecretVariable.fabricate! do |resource|
variable_value = 'variable value' resource.key = 'VARIABLE_KEY'
resource.value = 'some secret variable'
variable = Factory::Resource::SecretVariable.fabricate! do |resource|
resource.key = variable_key
resource.value = variable_value
end end
expect(variable.key).to eq(variable_key) Page::Project::Settings::CICD.perform do |settings|
expect(variable.value).to eq(variable_value) settings.expand_secret_variables do |page|
expect(page).to have_field(with: 'VARIABLE_KEY')
expect(page).not_to have_field(with: 'some secret variable')
page.reveal_variables
expect(page).to have_field(with: 'some secret variable')
end
end
end end
end end
end end
...@@ -14,7 +14,7 @@ describe QA::Page::Base do ...@@ -14,7 +14,7 @@ describe QA::Page::Base do
end end
view 'path/to/some/_partial.html.haml' do view 'path/to/some/_partial.html.haml' do
element :something, 'string pattern' element :another_element, 'string pattern'
end end
end end
end end
...@@ -25,11 +25,10 @@ describe QA::Page::Base do ...@@ -25,11 +25,10 @@ describe QA::Page::Base do
end end
it 'populates views objects with data about elements' do it 'populates views objects with data about elements' do
subject.views.first.elements.tap do |elements| expect(subject.elements.size).to eq 3
expect(elements.size).to eq 2 expect(subject.elements).to all(be_an_instance_of QA::Page::Element)
expect(elements).to all(be_an_instance_of QA::Page::Element) expect(subject.elements.map(&:name))
expect(elements.map(&:name)).to eq [:something, :something_else] .to eq [:something, :something_else, :another_element]
end
end end
end end
......
...@@ -221,7 +221,7 @@ describe Gitlab::Checks::ChangeAccess do ...@@ -221,7 +221,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'with LFS not enabled' do context 'with LFS not enabled' do
it 'skips the validation' do it 'skips the validation' do
expect_any_instance_of(described_class).not_to receive(:lfs_file_locks_validation) expect_any_instance_of(Gitlab::Checks::CommitCheck).not_to receive(:validate)
subject.exec subject.exec
end end
...@@ -238,7 +238,7 @@ describe Gitlab::Checks::ChangeAccess do ...@@ -238,7 +238,7 @@ describe Gitlab::Checks::ChangeAccess do
end end
end end
context 'when change is sent by the author od the lock' do context 'when change is sent by the author of the lock' do
let(:user) { owner } let(:user) { owner }
it "doesn't raise any error" do it "doesn't raise any error" do
......
...@@ -11,7 +11,8 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -11,7 +11,8 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
import_source: 'foo/bar', import_source: 'foo/bar',
repository_storage_path: 'foo', repository_storage_path: 'foo',
disk_path: 'foo', disk_path: 'foo',
repository: repository repository: repository,
create_wiki: true
) )
end end
...@@ -192,7 +193,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -192,7 +193,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
expect(importer.import_wiki_repository).to eq(true) expect(importer.import_wiki_repository).to eq(true)
end end
it 'marks the import as failed if an error was raised' do it 'marks the import as failed and creates an empty repo if an error was raised' do
expect(importer.gitlab_shell) expect(importer.gitlab_shell)
.to receive(:import_repository) .to receive(:import_repository)
.and_raise(Gitlab::Shell::Error) .and_raise(Gitlab::Shell::Error)
...@@ -201,6 +202,9 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do ...@@ -201,6 +202,9 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
.to receive(:fail_import) .to receive(:fail_import)
.and_return(false) .and_return(false)
expect(project)
.to receive(:create_wiki)
expect(importer.import_wiki_repository).to eq(false) expect(importer.import_wiki_repository).to eq(false)
end end
end end
......
...@@ -578,59 +578,30 @@ describe Notify do ...@@ -578,59 +578,30 @@ describe Notify do
end end
describe 'project access requested' do describe 'project access requested' do
context 'for a project in a user namespace' do let(:project) do
let(:project) do create(:project, :public, :access_requestable) do |project|
create(:project, :public, :access_requestable) do |project| project.add_master(project.owner)
project.add_master(project.owner, current_user: project.owner)
end
end
let(:project_member) do
project.request_access(user)
project.requesters.find_by(user_id: user.id)
end
subject { described_class.member_access_requested_email('project', project_member.id) }
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
it 'contains all the useful information' do
to_emails = subject.header[:to].addrs
expect(to_emails.size).to eq(1)
expect(to_emails[0].address).to eq(project.members.owners_and_masters.first.user.notification_email)
is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
is_expected.to have_html_escaped_body_text project.name_with_namespace
is_expected.to have_body_text project_project_members_url(project)
is_expected.to have_body_text project_member.human_access
end end
end end
context 'for a project in a group' do let(:project_member) do
let(:group_owner) { create(:user) } project.request_access(user)
let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } } project.requesters.find_by(user_id: user.id)
let(:project) { create(:project, :public, :access_requestable, namespace: group) } end
let(:project_member) do subject { described_class.member_access_requested_email('project', project_member.id, recipient.notification_email) }
project.request_access(user)
project.requesters.find_by(user_id: user.id)
end
subject { described_class.member_access_requested_email('project', project_member.id) }
it_behaves_like 'an email sent from GitLab' it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like "a user cannot unsubscribe through footer link"
it 'contains all the useful information' do it 'contains all the useful information' do
to_emails = subject.header[:to].addrs to_emails = subject.header[:to].addrs.map(&:address)
expect(to_emails.size).to eq(1) expect(to_emails).to eq([recipient.notification_email])
expect(to_emails[0].address).to eq(group.members.owners_and_masters.first.user.notification_email)
is_expected.to have_subject "Request to join the #{project.name_with_namespace} project" is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
is_expected.to have_html_escaped_body_text project.name_with_namespace is_expected.to have_html_escaped_body_text project.name_with_namespace
is_expected.to have_body_text project_project_members_url(project) is_expected.to have_body_text project_project_members_url(project)
is_expected.to have_body_text project_member.human_access is_expected.to have_body_text project_member.human_access
end
end end
end end
...@@ -1074,13 +1045,16 @@ describe Notify do ...@@ -1074,13 +1045,16 @@ describe Notify do
group.request_access(user) group.request_access(user)
group.requesters.find_by(user_id: user.id) group.requesters.find_by(user_id: user.id)
end end
subject { described_class.member_access_requested_email('group', group_member.id) } subject { described_class.member_access_requested_email('group', group_member.id, recipient.notification_email) }
it_behaves_like 'an email sent from GitLab' it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links' it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like "a user cannot unsubscribe through footer link"
it 'contains all the useful information' do it 'contains all the useful information' do
to_emails = subject.header[:to].addrs.map(&:address)
expect(to_emails).to eq([recipient.notification_email])
is_expected.to have_subject "Request to join the #{group.name} group" is_expected.to have_subject "Request to join the #{group.name} group"
is_expected.to have_html_escaped_body_text group.name is_expected.to have_html_escaped_body_text group.name
is_expected.to have_body_text group_group_members_url(group) is_expected.to have_body_text group_group_members_url(group)
......
...@@ -1349,6 +1349,33 @@ describe NotificationService, :mailer do ...@@ -1349,6 +1349,33 @@ describe NotificationService, :mailer do
end end
describe 'GroupMember' do describe 'GroupMember' do
let(:added_user) { create(:user) }
describe '#new_access_request' do
let(:master) { create(:user) }
let(:owner) { create(:user) }
let(:developer) { create(:user) }
let!(:group) do
create(:group, :public, :access_requestable) do |group|
group.add_owner(owner)
group.add_master(master)
group.add_developer(developer)
end
end
before do
reset_delivered_emails!
end
it 'sends notification to group owners_and_masters' do
group.request_access(added_user)
should_email(owner)
should_email(master)
should_not_email(developer)
end
end
describe '#decline_group_invite' do describe '#decline_group_invite' do
let(:creator) { create(:user) } let(:creator) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
...@@ -1370,18 +1397,9 @@ describe NotificationService, :mailer do ...@@ -1370,18 +1397,9 @@ describe NotificationService, :mailer do
describe '#new_group_member' do describe '#new_group_member' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:added_user) { create(:user) }
def create_member!
GroupMember.create(
group: group,
user: added_user,
access_level: Gitlab::Access::GUEST
)
end
it 'sends a notification' do it 'sends a notification' do
create_member! group.add_guest(added_user)
should_only_email(added_user) should_only_email(added_user)
end end
...@@ -1391,7 +1409,7 @@ describe NotificationService, :mailer do ...@@ -1391,7 +1409,7 @@ describe NotificationService, :mailer do
end end
it 'does not send a notification' do it 'does not send a notification' do
create_member! group.add_guest(added_user)
should_not_email_anyone should_not_email_anyone
end end
end end
...@@ -1399,8 +1417,42 @@ describe NotificationService, :mailer do ...@@ -1399,8 +1417,42 @@ describe NotificationService, :mailer do
end end
describe 'ProjectMember' do describe 'ProjectMember' do
let(:project) { create(:project) }
set(:added_user) { create(:user) }
describe '#new_access_request' do
context 'for a project in a user namespace' do
let(:project) do
create(:project, :public, :access_requestable) do |project|
project.add_master(project.owner)
end
end
it 'sends notification to project owners_and_masters' do
project.request_access(added_user)
should_only_email(project.owner)
end
end
context 'for a project in a group' do
let(:group_owner) { create(:user) }
let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } }
let!(:project) { create(:project, :public, :access_requestable, namespace: group) }
before do
reset_delivered_emails!
end
it 'sends notification to group owners_and_masters' do
project.request_access(added_user)
should_only_email(group_owner)
end
end
end
describe '#decline_group_invite' do describe '#decline_group_invite' do
let(:project) { create(:project) }
let(:member) { create(:user) } let(:member) { create(:user) }
before do before do
...@@ -1417,19 +1469,12 @@ describe NotificationService, :mailer do ...@@ -1417,19 +1469,12 @@ describe NotificationService, :mailer do
end end
describe '#new_project_member' do describe '#new_project_member' do
let(:project) { create(:project) }
let(:added_user) { create(:user) }
def create_member!
create(:project_member, user: added_user, project: project)
end
it do it do
create_member! create_member!
should_only_email(added_user) should_only_email(added_user)
end end
describe 'when notifications are disabled' do context 'when notifications are disabled' do
before do before do
create_global_setting_for(added_user, :disabled) create_global_setting_for(added_user, :disabled)
end end
...@@ -1440,6 +1485,10 @@ describe NotificationService, :mailer do ...@@ -1440,6 +1485,10 @@ describe NotificationService, :mailer do
end end
end end
end end
def create_member!
create(:project_member, user: added_user, project: project)
end
end end
context 'guest user in private project' do context 'guest user in private project' do
......
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