Commit 9fdf1a74 authored by Shinya Maeda's avatar Shinya Maeda Committed by Kamil Trzciński

Add JSON responder at `#index` endpoint in feature flag controller

parent 28573a38
......@@ -10,9 +10,23 @@ class Projects::FeatureFlagsController < Projects::ApplicationController
before_action :feature_flag, only: [:edit, :update, :destroy]
def index
@feature_flags = project.operations_feature_flags
.ordered
.page(params[:page]).per(30)
@feature_flags = FeatureFlagsFinder
.new(project, current_user, scope: params[:scope])
.execute
.page(params[:page])
.per(30)
respond_to do |format|
format.html
format.json do
Gitlab::PollingInterval.set_header(response, interval: 10_000)
render json: FeatureFlagSerializer
.new(project: @project, current_user: @current_user)
.with_pagination(request, response)
.represent(@feature_flags)
end
end
end
def new
......
# frozen_string_literal: true
class FeatureFlagsFinder
attr_reader :project, :feature_flags, :params, :current_user
def initialize(project, current_user, params = {})
@project = project
@current_user = current_user
@feature_flags = project.operations_feature_flags
@params = params
end
def execute
unless Ability.allowed?(current_user, :read_feature_flag, project)
return Operations::FeatureFlag.none
end
items = feature_flags
items = by_scope(items)
items.ordered
end
private
def by_scope(items)
case params[:scope]
when 'enabled'
items.enabled
when 'disabled'
items.disabled
else
items
end
end
end
......@@ -18,6 +18,8 @@ module Operations
validates :description, allow_blank: true, length: 0..255
scope :ordered, -> { order(:name) }
scope :enabled, -> { where(active: true) }
scope :disabled, -> { where(active: false) }
def strategies
[
......
# frozen_string_literal: true
module Operations
class FeatureFlagPolicy < BasePolicy
delegate { @subject.project }
end
end
# frozen_string_literal: true
class FeatureFlagEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :active
expose :created_at
expose :updated_at
expose :name
expose :description
expose :edit_path, if: -> (feature_flag, _) { can_update?(feature_flag) } do |feature_flag|
edit_project_feature_flag_path(feature_flag.project, feature_flag)
end
expose :destroy_path, if: -> (feature_flag, _) { can_destroy?(feature_flag) } do |feature_flag|
project_feature_flag_path(feature_flag.project, feature_flag)
end
private
def can_update?(feature_flag)
can?(current_user, :update_feature_flag, feature_flag)
end
def can_destroy?(feature_flag)
can?(current_user, :destroy_feature_flag, feature_flag)
end
def current_user
request.current_user
end
end
# frozen_string_literal: true
class FeatureFlagSerializer < BaseSerializer
include WithPagination
entity FeatureFlagEntity
end
......@@ -60,6 +60,77 @@ describe Projects::FeatureFlagsController do
end
end
describe 'GET #index json' do
subject { get(:index, params: view_params, format: :json) }
let!(:feature_flag_active) do
create(:operations_feature_flag, project: project, active: true)
end
let!(:feature_flag_inactive) do
create(:operations_feature_flag, project: project, active: false)
end
it 'returns all feature flags as json response' do
subject
expect(json_response.count).to eq(2)
expect(json_response.first['name']).to eq(feature_flag_active.name)
expect(json_response.second['name']).to eq(feature_flag_inactive.name)
end
it 'returns edit path and destroy path' do
subject
expect(json_response.first['edit_path']).not_to be_nil
expect(json_response.first['destroy_path']).not_to be_nil
end
it 'matches json schema' do
subject
expect(response).to match_response_schema('feature_flags', dir: 'ee')
end
context 'when scope is specified' do
let(:view_params) do
{ namespace_id: project.namespace, project_id: project, scope: scope }
end
context 'when scope is all' do
let(:scope) { 'all' }
it 'returns all feature flags' do
subject
expect(json_response.count).to eq(2)
end
end
context 'when scope is enabled' do
let(:scope) { 'enabled' }
it 'returns enabled feature flags' do
subject
expect(json_response.count).to eq(1)
expect(json_response.first['active']).to be_truthy
end
end
context 'when scope is disabled' do
let(:scope) { 'disabled' }
it 'returns disabled feature flags' do
subject
expect(json_response.count).to eq(1)
expect(json_response.first['active']).to be_falsy
end
end
end
end
describe 'GET new' do
render_views
......
# frozen_string_literal: true
require 'spec_helper'
describe FeatureFlagsFinder do
let(:finder) { described_class.new(project, user, params) }
let(:project) { create(:project) }
let(:user) { developer }
let(:developer) { create(:user) }
let(:reporter) { create(:user) }
let(:params) { {} }
before do
project.add_developer(developer)
project.add_reporter(reporter)
stub_licensed_features(feature_flags: true)
end
describe '#execute' do
subject { finder.execute }
let!(:feature_flag_1) { create(:operations_feature_flag, name: 'flag-a', project: project) }
let!(:feature_flag_2) { create(:operations_feature_flag, name: 'flag-b', project: project) }
it 'returns feature flags ordered by name' do
is_expected.to eq([feature_flag_1, feature_flag_2])
end
context 'when user is a reporter' do
let(:user) { reporter }
it 'returns an empty list' do
is_expected.to be_empty
end
end
context 'when scope is given' do
let!(:feature_flag_1) { create(:operations_feature_flag, project: project, active: true) }
let!(:feature_flag_2) { create(:operations_feature_flag, project: project, active: false) }
context 'when scope is enabled' do
let(:params) { { scope: 'enabled' } }
it 'returns active feature flag' do
is_expected.to eq([feature_flag_1])
end
end
context 'when scope is disabled' do
let(:params) { { scope: 'disabled' } }
it 'returns inactive feature flag' do
is_expected.to eq([feature_flag_2])
end
end
end
end
end
{
"type": "object",
"required" : [
"id",
"name"
],
"properties" : {
"id": { "type": "integer" },
"created_at": { "type": "date" },
"updated_at": { "type": "date" },
"name": { "type": "string" },
"active": { "type": "boolean" },
"description": { "type": ["string", "null"] },
"edit_path": { "type": ["string", "null"] },
"destroy_path": { "type": ["string", "null"] }
},
"additionalProperties": false
}
{
"type": "array",
"items": { "$ref": "feature_flag.json" }
}
# frozen_string_literal: true
require 'spec_helper'
describe FeatureFlagEntity do
let(:feature_flag) { create(:operations_feature_flag, project: project) }
let(:project) { create(:project) }
let(:request) { double('request', current_user: user) }
let(:user) { create(:user) }
let(:entity) { described_class.new(feature_flag, request: request) }
before do
project.add_developer(user)
stub_licensed_features(feature_flags: true)
end
subject { entity.as_json }
it 'has feature flag attributes' do
expect(subject).to include(:id, :active, :created_at, :updated_at,
:description, :name, :edit_path, :destroy_path)
end
end
# frozen_string_literal: true
require 'spec_helper'
describe FeatureFlagSerializer do
let(:serializer) { described_class.new(project: project, current_user: user) }
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:feature_flags) { create_list(:operations_feature_flag, 3) }
before do
stub_licensed_features(feature_flags: true)
project.add_developer(user)
end
describe '#represent' do
subject { serializer.represent(feature_flags) }
it 'includes feature flag attributes' do
is_expected.to all(include(:id, :active, :created_at, :updated_at,
:description, :name))
end
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