Skip to content
Snippets Groups Projects
Commit 9a7c3eec authored by Quang-Minh Nguyen's avatar Quang-Minh Nguyen
Browse files

Add repository actor support to Feature api

In gitaly#4459, we introduced
Repository actor for Gitaly. This actor targets a particular repository
managed by Gitaly. It uses repository relative path as unique identity
for flipper_id. This commit adds the support to enable/disable a flag
for a repository to admin Feature API.

Issue: gitaly#4549


Changelog: added
Signed-off-by: Quang-Minh Nguyen's avatarQuang-Minh Nguyen <qmnguyen@gitlab.com>
parent dbbb7680
No related branches found
No related tags found
No related merge requests found
......@@ -91,6 +91,10 @@ def gate_specified?(params)
optional :project,
type: String,
desc: "A projects path, for example `gitlab-org/gitlab-foss`, or comma-separated multiple project paths"
optional :repository,
type: String,
desc: "A repository path, for example `gitlab-org/gitlab-test.wiki.git`, `gitlab-org/gitlab-test.wiki.git` " \
", `snippets/21.git`, to name a few. Use comma to separate multiple repository paths"
optional :force, type: Boolean, desc: 'Skip feature flag validation checks, such as a YAML definition'
mutually_exclusive :key, :feature_group
......@@ -98,6 +102,7 @@ def gate_specified?(params)
mutually_exclusive :key, :group
mutually_exclusive :key, :namespace
mutually_exclusive :key, :project
mutually_exclusive :key, :repository
end
post ':name' do
if Feature.enabled?(:set_feature_flag_service)
......
......@@ -301,11 +301,11 @@ def initialize(params)
end
def gate_specified?
%i(user project group feature_group namespace).any? { |key| params.key?(key) }
%i(user project group feature_group namespace repository).any? { |key| params.key?(key) }
end
def targets
[feature_group, users, projects, groups, namespaces].flatten.compact
[feature_group, users, projects, groups, namespaces, repositories].flatten.compact
end
private
......@@ -350,6 +350,17 @@ def namespaces
Namespace.without_project_namespaces.find_by_full_path(arg) || (raise UnknowTargetError, "#{arg} is not found!")
end
end
def repositories
return unless params.key?(:repository)
params[:repository].split(',').map do |arg|
container, _project, _type, _path = Gitlab::RepoPath.parse(arg)
raise UnknowTargetError, "#{arg} is not found!" if container.nil?
container.repository
end
end
end
end
......
......@@ -790,11 +790,48 @@
let(:group) { create(:group) }
let(:user_name) { project.first_owner.username }
subject { described_class.new(user: user_name, project: project.full_path, group: group.full_path) }
subject do
described_class.new(
user: user_name,
project: project.full_path,
group: group.full_path,
repository: project.repository.full_path
)
end
it 'returns all found targets' do
expect(subject.targets).to be_an(Array)
expect(subject.targets).to eq([project.first_owner, project, group])
expect(subject.targets).to eq([project.first_owner, project, group, project.repository])
end
context 'repository target works with different types of repositories' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :wiki_repo, group: group) }
let_it_be(:project_in_user_namespace) { create(:project, namespace: create(:user).namespace) }
let(:personal_snippet) { create(:personal_snippet) }
let(:project_snippet) { create(:project_snippet, project: project) }
let(:targets) do
[
project,
project.wiki,
group.wiki,
project_in_user_namespace,
personal_snippet,
project_snippet
]
end
subject do
described_class.new(
repository: targets.map { |t| t.repository.full_path }.join(",")
)
end
it 'returns all found targets' do
expect(subject.targets).to be_an(Array)
expect(subject.targets).to eq(targets.map(&:repository))
end
end
end
end
......
......@@ -187,13 +187,14 @@
it 'sets the feature gate' do
post api("/features/#{feature_name}", admin), params: { value: 'true', actor_type => actor.full_path }
actor_id = actor_type == :repository ? actor.relative_path : actor.id
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}"] }
{ 'key' => 'actors', 'value' => ["#{actor.class}:#{actor_id}"] }
],
'definition' => known_feature_flag_definition_hash
)
......@@ -269,6 +270,20 @@
end
end
context 'when enabling for a repository by path' do
context 'when the repository exists' do
it_behaves_like 'enables the flag for the actor', :repository do
let_it_be(:actor) { create(:project).repository }
end
end
context 'when the repository does not exist' do
it_behaves_like 'does not enable the flag', :repository do
let(:actor_path) { 'not/a/repository' }
end
end
end
context 'with multiple users' do
let_it_be(:users) { create_list(:user, 3) }
......@@ -361,6 +376,29 @@
end
end
context 'with multiple repository' do
let_it_be(:projects) { create_list(:project, 3) }
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { repository: projects.map { |p| p.repository.full_path }.join(',') } }
let(:expected_gate_params) { projects.map { |p| p.repository.flipper_id } }
end
context 'when empty value exists between comma' do
it_behaves_like 'creates an enabled feature for the specified entries' do
let(:gate_params) { { repository: "#{projects.first.repository.full_path},,,," } }
let(:expected_gate_params) { projects.first.repository.flipper_id }
end
end
context 'when one of the projects does not exist' do
it_behaves_like 'does not enable the flag', :project do
let(:actor_path) { "#{projects.first.repository.full_path},inexistent-entry" }
let(:expected_inexistent_path) { "inexistent-entry" }
end
end
end
it 'creates a feature with the given percentage of time if passed an integer' do
post api("/features/#{feature_name}", admin), params: { value: '50' }
......
......@@ -130,6 +130,15 @@
end
end
context 'when enabling for a repository' do
let(:params) { { value: 'true', repository: project.repository.full_path } }
it 'enables the feature flag' do
expect(Feature).to receive(:enable).with(feature_name, project.repository)
expect(subject).to be_success
end
end
context 'when enabling for a user actor and a feature group' do
let(:params) { { value: 'true', user: user.username, feature_group: 'perf_team' } }
let(:feature_group) { Feature.group('perf_team') }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment