diff --git a/ee/app/services/feature_flags/update_service.rb b/ee/app/services/feature_flags/update_service.rb index 5ad0dc004260dea8de21c54fff4318006d7dcbc8..eabed997bbddc9987770ec4de2c2cb28bb7fe1b1 100644 --- a/ee/app/services/feature_flags/update_service.rb +++ b/ee/app/services/feature_flags/update_service.rb @@ -21,7 +21,7 @@ module FeatureFlags success(feature_flag: feature_flag) else - error(feature_flag.errors.full_messages) + error(feature_flag.errors.full_messages, :bad_request) end end end diff --git a/ee/lib/api/feature_flag_scopes.rb b/ee/lib/api/feature_flag_scopes.rb index e367a77a5dd9b095f3f3704afdc3062cd31863df..d4f856526bb0188488234d9fe3aaa895330204d5 100644 --- a/ee/lib/api/feature_flag_scopes.rb +++ b/ee/lib/api/feature_flag_scopes.rb @@ -45,6 +45,29 @@ module API present paginate(feature_flag.scopes), with: EE::API::Entities::FeatureFlag::Scope end + desc 'Create a scope of a feature flag' do + detail 'This feature is going to be introduced in GitLab 12.5 if `feature_flag_api` feature flag is removed' + success EE::API::Entities::FeatureFlag::Scope + end + params do + requires :environment_scope, type: String, desc: 'The environment scope of the scope' + requires :active, type: Boolean, desc: 'Active/inactive of the scope' + requires :strategies, type: JSON, desc: 'The strategies of the scope' + end + post do + authorize_update_feature_flag! + + result = ::FeatureFlags::UpdateService + .new(user_project, current_user, scopes_attributes: [declared_params]) + .execute(feature_flag) + + if result[:status] == :success + present scope, with: EE::API::Entities::FeatureFlag::Scope + else + render_api_error!(result[:message], result[:http_status]) + end + end + params do requires :environment_scope, type: String, desc: 'URL-encoded environment scope' end @@ -57,6 +80,33 @@ module API present scope, with: EE::API::Entities::FeatureFlag::Scope end + desc 'Update a scope of a feature flag' do + detail 'This feature is going to be introduced in GitLab 12.5 if `feature_flag_api` feature flag is removed' + success EE::API::Entities::FeatureFlag::Scope + end + params do + optional :active, type: Boolean, desc: 'Active/inactive of the scope' + optional :strategies, type: JSON, desc: 'The strategies of the scope' + end + put do + authorize_update_feature_flag! + + scope_attributes = declared_params.merge(id: scope.id) + + result = ::FeatureFlags::UpdateService + .new(user_project, current_user, scopes_attributes: [scope_attributes]) + .execute(feature_flag) + + if result[:status] == :success + updated_scope = result[:feature_flag].scopes + .find { |scope| scope.environment_scope == params[:environment_scope] } + + present updated_scope, with: EE::API::Entities::FeatureFlag::Scope + else + render_api_error!(result[:message], result[:http_status]) + end + end + desc 'Delete a scope from a feature flag' do detail 'This feature is going to be introduced in GitLab 12.5 if `feature_flag_api` feature flag is removed' success EE::API::Entities::FeatureFlag::Scope diff --git a/ee/spec/requests/api/feature_flag_scopes_spec.rb b/ee/spec/requests/api/feature_flag_scopes_spec.rb index 2272c6db77a7675812089835e006085e0608cdc6..908e3114014b872a9a31cf89845cdb4bbdbb242d 100644 --- a/ee/spec/requests/api/feature_flag_scopes_spec.rb +++ b/ee/spec/requests/api/feature_flag_scopes_spec.rb @@ -144,6 +144,56 @@ describe API::FeatureFlagScopes do end end + describe 'POST /projects/:id/feature_flags/:name/scopes' do + subject do + post api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes", user), + params: params + end + + let(:params) do + { + environment_scope: 'staging', + active: true, + strategies: [{ name: 'userWithId', parameters: { 'userIds': 'a,b,c' } }].to_json + } + end + + context 'when there is a corresponding feature flag' do + let!(:feature_flag) { create(:operations_feature_flag, project: project) } + + it_behaves_like 'check user permission' + + it 'creates a new scope' do + subject + + expect(response).to have_gitlab_http_status(:created) + expect(response).to match_response_schema('public_api/v4/feature_flag_scope', dir: 'ee') + expect(json_response['environment_scope']).to eq(params[:environment_scope]) + expect(json_response['active']).to eq(params[:active]) + expect(json_response['strategies']).to eq(JSON.parse(params[:strategies])) + end + + context 'when the scope already exists' do + before do + create_scope(feature_flag, params[:environment_scope]) + end + + it 'returns error' do + subject + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to include('Scopes environment scope (staging) has already been taken') + end + end + end + + context 'when feature flag is not found' do + let(:feature_flag) { double(:feature_flag, name: 'test') } + + it_behaves_like 'not found' + end + end + describe 'GET /projects/:id/feature_flags/:name/scopes/:environment_scope' do subject do get api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes/#{environment_scope}", @@ -192,6 +242,52 @@ describe API::FeatureFlagScopes do end end + describe 'PUT /projects/:id/feature_flags/:name/scopes/:environment_scope' do + subject do + put api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes/#{environment_scope}", + user), params: params + end + + let(:environment_scope) { scope.environment_scope } + + let(:params) do + { + active: true, + strategies: [{ name: 'userWithId', parameters: { 'userIds': 'a,b,c' } }].to_json + } + end + + context 'when there is a corresponding feature flag' do + let!(:feature_flag) { create(:operations_feature_flag, project: project) } + let(:scope) { create_scope(feature_flag, 'staging', false, [{ name: "default", parameters: {} }]) } + + it_behaves_like 'check user permission' + + it 'returns the updated scope' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to match_response_schema('public_api/v4/feature_flag_scope', dir: 'ee') + expect(json_response['id']).to eq(scope.id) + expect(json_response['active']).to eq(params[:active]) + expect(json_response['strategies']).to eq(JSON.parse(params[:strategies])) + end + + context 'when there are no corresponding feature flag scopes' do + let(:scope) { double(:feature_flag_scope, environment_scope: 'prd') } + + it_behaves_like 'not found' + end + end + + context 'when there are no corresponding feature flags' do + let(:feature_flag) { double(:feature_flag, name: 'test') } + let(:scope) { double(:feature_flag_scope, environment_scope: 'prd') } + + it_behaves_like 'not found' + end + end + describe 'DELETE /projects/:id/feature_flags/:name/scopes/:environment_scope' do subject do delete api("/projects/#{project.id}/feature_flags/#{feature_flag.name}/scopes/#{environment_scope}", diff --git a/ee/spec/services/feature_flags/update_service_spec.rb b/ee/spec/services/feature_flags/update_service_spec.rb index 6953b7a8afe7ff956c3e52671ded79207d5c0744..4503d89ba9273d06db7fdcaa9c67e28c7a89fbc5 100644 --- a/ee/spec/services/feature_flags/update_service_spec.rb +++ b/ee/spec/services/feature_flags/update_service_spec.rb @@ -43,6 +43,7 @@ describe FeatureFlags::UpdateService do it 'returns error status' do expect(subject[:status]).to eq(:error) + expect(subject[:http_status]).to eq(:bad_request) end it 'returns error messages' do