Commit e7cb01ae authored by Douwe Maan's avatar Douwe Maan

Merge branch 'bvl-expand-namespace-feature-checks' into 'master'

Improve Namespace feature check

Closes #2761

See merge request !2279
parents f582564c 5d381260
...@@ -33,11 +33,19 @@ module EE ...@@ -33,11 +33,19 @@ module EE
# for a given Namespace plan. This method should consider ancestor groups # for a given Namespace plan. This method should consider ancestor groups
# being licensed. # being licensed.
def feature_available?(feature) def feature_available?(feature)
@features_available ||= Hash.new do |h, feature| @feature_available ||= Hash.new do |h, feature|
h[feature] = load_feature_available(feature)
end
@feature_available[feature]
end
def feature_available_in_plan?(feature)
@features_available_in_plan ||= Hash.new do |h, feature|
h[feature] = plans.any? { |plan| License.plan_includes_feature?(EE_PLANS[plan], feature) } h[feature] = plans.any? { |plan| License.plan_includes_feature?(EE_PLANS[plan], feature) }
end end
@features_available[feature] @features_available_in_plan[feature]
end end
def actual_shared_runners_minutes_limit def actual_shared_runners_minutes_limit
...@@ -57,6 +65,16 @@ module EE ...@@ -57,6 +65,16 @@ module EE
private private
def load_feature_available(feature)
globally_available = License.feature_available?(feature)
if current_application_settings.should_check_namespace_plan?
globally_available && feature_available_in_plan?(feature)
else
globally_available
end
end
def plans def plans
@ancestors_plans ||= @ancestors_plans ||=
if parent_id if parent_id
......
...@@ -344,11 +344,19 @@ module EE ...@@ -344,11 +344,19 @@ module EE
private private
def licensed_feature_available?(feature) def licensed_feature_available?(feature)
@licensed_feature_available ||= Hash.new do |h, feature|
h[feature] = load_licensed_feature_available(feature)
end
@licensed_feature_available[feature]
end
def load_licensed_feature_available(feature)
globally_available = License.feature_available?(feature) globally_available = License.feature_available?(feature)
if current_application_settings.should_check_namespace_plan? if current_application_settings.should_check_namespace_plan?
globally_available && globally_available &&
(public? && namespace.public? || namespace.feature_available?(feature)) (public? && namespace.public? || namespace.feature_available_in_plan?(feature))
else else
globally_available globally_available
end end
......
...@@ -43,37 +43,64 @@ describe Namespace, models: true do ...@@ -43,37 +43,64 @@ describe Namespace, models: true do
end end
describe '#feature_available?' do describe '#feature_available?' do
let(:plan_license) { Namespace::BRONZE_PLAN }
let(:group) { create(:group, plan: plan_license) } let(:group) { create(:group, plan: plan_license) }
let(:feature) { :service_desk }
subject { group.feature_available?(feature) } subject { group.feature_available?(feature) }
context 'when feature available' do before do
let(:feature) { :deploy_board } stub_licensed_features(feature => true)
let(:plan_license) { Namespace::GOLD_PLAN } end
context 'when feature available for current group' do it 'uses the global setting when running on premise' do
it 'returns false' do stub_application_setting_on_object(group, should_check_namespace_plan: false)
is_expected.to eq(true)
end is_expected.to be_truthy
end
it 'only checks the plan once' do
expect(group).to receive(:load_feature_available).once.and_call_original
2.times { group.feature_available?(:service_desk) }
end
context 'when checking namespace plan' do
before do
stub_application_setting_on_object(group, should_check_namespace_plan: true)
end end
if Group.supports_nested_groups? it 'combines the global setting with the group setting when not running on premise' do
context 'when license is applied to parent group' do is_expected.to be_falsy
let(:child_group) { create :group, parent: group } end
context 'when feature available on the plan' do
let(:plan_license) { Namespace::GOLD_PLAN }
it 'child group has feature available' do context 'when feature available for current group' do
expect(child_group.feature_available?(feature)).to eq(true) it 'returns true' do
is_expected.to be_truthy
end
end
if Group.supports_nested_groups?
context 'when license is applied to parent group' do
let(:child_group) { create :group, parent: group }
it 'child group has feature available' do
expect(child_group.feature_available?(feature)).to be_truthy
end
end end
end end
end end
end
context 'when feature not available' do context 'when feature not available in the plan' do
let(:feature) { :deploy_board } let(:feature) { :deploy_board }
let(:plan_license) { Namespace::BRONZE_PLAN } let(:plan_license) { Namespace::BRONZE_PLAN }
it 'returns false' do it 'returns false' do
is_expected.to eq(false) is_expected.to be_falsy
end
end end
end end
end end
......
...@@ -22,7 +22,7 @@ describe Project, models: true do ...@@ -22,7 +22,7 @@ describe Project, models: true do
before do before do
stub_application_setting('check_namespace_plan?' => check_namespace_plan) stub_application_setting('check_namespace_plan?' => check_namespace_plan)
allow(Gitlab).to receive(:com?) { true } allow(Gitlab).to receive(:com?) { true }
expect(License).to receive(:feature_available?).with(feature) { allowed_on_global_license } stub_licensed_features(feature => allowed_on_global_license)
allow(namespace).to receive(:plan) { plan_license } allow(namespace).to receive(:plan) { plan_license }
end end
...@@ -99,6 +99,13 @@ describe Project, models: true do ...@@ -99,6 +99,13 @@ describe Project, models: true do
end end
end end
it 'only loads licensed availability once' do
expect(project).to receive(:load_licensed_feature_available)
.once.and_call_original
2.times { project.feature_available?(:service_desk) }
end
context 'when feature symbol is not included on Namespace features code' do context 'when feature symbol is not included on Namespace features code' do
let(:feature) { :issues } let(:feature) { :issues }
......
...@@ -9,6 +9,16 @@ module StubConfiguration ...@@ -9,6 +9,16 @@ module StubConfiguration
.to receive_messages(messages) .to receive_messages(messages)
end end
def stub_application_setting_on_object(object, messages)
add_predicates(messages)
allow(Gitlab::CurrentSettings.current_application_settings)
.to receive_messages(messages)
messages.each do |setting, value|
allow(object).to receive_message_chain(:current_application_settings, setting) { value }
end
end
def stub_config_setting(messages) def stub_config_setting(messages)
allow(Gitlab.config.gitlab).to receive_messages(messages) allow(Gitlab.config.gitlab).to receive_messages(messages)
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