diff --git a/lib/feature.rb b/lib/feature.rb index 12b4ef07dd6a871eb0a308d44c236a58d0a89b60..47fee23c7ea7284d4f76d8e9f2fc14ca157de0f3 100644 --- a/lib/feature.rb +++ b/lib/feature.rb @@ -245,11 +245,11 @@ def initialize(params) end def gate_specified? - %i(user project group feature_group).any? { |key| params.key?(key) } + %i(user project group feature_group namespace).any? { |key| params.key?(key) } end def targets - [feature_group, user, project, group].compact + [feature_group, user, project, group, namespace].compact end private @@ -279,6 +279,13 @@ def group Group.find_by_full_path(params[:group]) end + + def namespace + return unless params.key?(:namespace) + + # We are interested in Group or UserNamespace + Namespace.without_project_namespaces.find_by_full_path(params[:namespace]) + end end end diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb index 35dba93b7669ab3c11133de754f97ef76d1f282e..a265f67115ab4dd91091bd9c0b9c996007c6c063 100644 --- a/spec/requests/api/features_spec.rb +++ b/spec/requests/api/features_spec.rb @@ -167,76 +167,85 @@ end end + shared_examples 'does not enable the flag' do |actor_type, actor_path| + it 'returns the current state of the flag without changes' do + post api("/features/#{feature_name}", admin), params: { value: 'true', actor_type => actor_path } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response).to match( + "name" => feature_name, + "state" => "off", + "gates" => [ + { "key" => "boolean", "value" => false } + ], + 'definition' => known_feature_flag_definition_hash + ) + end + end + + shared_examples 'enables the flag for the actor' do |actor_type| + it 'sets the feature gate' do + post api("/features/#{feature_name}", admin), params: { value: 'true', actor_type => actor.full_path } + + expect(response).to have_gitlab_http_status(:created) + expect(json_response).to match( + 'name' => feature_name, + 'state' => 'conditional', + 'gates' => [ + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'actors', 'value' => ["#{actor.class}:#{actor.id}"] } + ], + 'definition' => known_feature_flag_definition_hash + ) + end + end + context 'when enabling for a project by path' do context 'when the project exists' do - let!(:project) { create(:project) } - - it 'sets the feature gate' do - post api("/features/#{feature_name}", admin), params: { value: 'true', project: project.full_path } - - expect(response).to have_gitlab_http_status(:created) - expect(json_response).to match( - 'name' => feature_name, - 'state' => 'conditional', - 'gates' => [ - { 'key' => 'boolean', 'value' => false }, - { 'key' => 'actors', 'value' => ["Project:#{project.id}"] } - ], - 'definition' => known_feature_flag_definition_hash - ) + it_behaves_like 'enables the flag for the actor', :project do + let(:actor) { create(:project) } end end context 'when the project does not exist' do - it 'sets no new values' do - post api("/features/#{feature_name}", admin), params: { value: 'true', project: 'mep/to/the/mep/mep' } - - expect(response).to have_gitlab_http_status(:created) - expect(json_response).to match( - "name" => feature_name, - "state" => "off", - "gates" => [ - { "key" => "boolean", "value" => false } - ], - 'definition' => known_feature_flag_definition_hash - ) - end + it_behaves_like 'does not enable the flag', :project, 'mep/to/the/mep/mep' end end context 'when enabling for a group by path' do context 'when the group exists' do - it 'sets the feature gate' do - group = create(:group) - - post api("/features/#{feature_name}", admin), params: { value: 'true', group: group.full_path } - - expect(response).to have_gitlab_http_status(:created) - expect(json_response).to match( - 'name' => feature_name, - 'state' => 'conditional', - 'gates' => [ - { 'key' => 'boolean', 'value' => false }, - { 'key' => 'actors', 'value' => ["Group:#{group.id}"] } - ], - 'definition' => known_feature_flag_definition_hash - ) + it_behaves_like 'enables the flag for the actor', :group do + let(:actor) { create(:group) } end end context 'when the group does not exist' do - it 'sets no new values and keeps the feature disabled' do - post api("/features/#{feature_name}", admin), params: { value: 'true', group: 'not/a/group' } - - expect(response).to have_gitlab_http_status(:created) - expect(json_response).to match( - "name" => feature_name, - "state" => "off", - "gates" => [ - { "key" => "boolean", "value" => false } - ], - 'definition' => known_feature_flag_definition_hash - ) + it_behaves_like 'does not enable the flag', :group, 'not/a/group' + end + end + + context 'when enabling for a namespace by path' do + context 'when the user namespace exists' do + it_behaves_like 'enables the flag for the actor', :namespace do + let(:actor) { create(:namespace) } + end + end + + context 'when the group namespace exists' do + it_behaves_like 'enables the flag for the actor', :namespace do + let(:actor) { create(:group) } + end + end + + context 'when the user namespace does not exist' do + it_behaves_like 'does not enable the flag', :namespace, 'not/a/group' + end + + context 'when a project namespace exists' do + let(:project_namespace) { create(:project_namespace) } + + it_behaves_like 'does not enable the flag', :namespace do + let(:actor_path) { project_namespace.full_path } end end end