Commit 46f66c7f authored by James Edwards-Jones's avatar James Edwards-Jones

Allow setting feature flags per GitLab group

Building on support for setting feature flags by project, this adds
support for setting them by GitLab group path.

This is different from setting them by Flipper feature_groups, which
are for batch updating pre-registered collections.
parent 82423ac3
---
title: Allow setting feature flags per GitLab group through the API
merge_request: 25022
author:
type: added
...@@ -60,10 +60,11 @@ POST /features/:name ...@@ -60,10 +60,11 @@ POST /features/:name
| `value` | integer/string | yes | `true` or `false` to enable/disable, or an integer for percentage of time | | `value` | integer/string | yes | `true` or `false` to enable/disable, or an integer for percentage of time |
| `feature_group` | string | no | A Feature group name | | `feature_group` | string | no | A Feature group name |
| `user` | string | no | A GitLab username | | `user` | string | no | A GitLab username |
| `group` | string | no | A GitLab group's path, for example 'gitlab-org' |
| `project` | string | no | A projects path, for example 'gitlab-org/gitlab-ce' | | `project` | string | no | A projects path, for example 'gitlab-org/gitlab-ce' |
Note that you can enable or disable a feature for a `feature_group`, a `user`, Note that you can enable or disable a feature for a `feature_group`, a `user`,
and a `project` in a single API call. a `group`, and a `project` in a single API call.
```bash ```bash
curl --data "value=30" --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/features/new_library curl --data "value=30" --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/features/new_library
......
...@@ -42,6 +42,7 @@ module API ...@@ -42,6 +42,7 @@ module API
requires :value, type: String, desc: '`true` or `false` to enable/disable, an integer for percentage of time' requires :value, type: String, desc: '`true` or `false` to enable/disable, an integer for percentage of time'
optional :feature_group, type: String, desc: 'A Feature group name' optional :feature_group, type: String, desc: 'A Feature group name'
optional :user, type: String, desc: 'A GitLab username' optional :user, type: String, desc: 'A GitLab username'
optional :group, type: String, desc: "A GitLab group's path, such as 'gitlab-org'"
optional :project, type: String, desc: 'A projects path, like gitlab-org/gitlab-ce' optional :project, type: String, desc: 'A projects path, like gitlab-org/gitlab-ce'
end end
post ':name' do post ':name' do
......
...@@ -111,11 +111,11 @@ class Feature ...@@ -111,11 +111,11 @@ class Feature
end end
def gate_specified? def gate_specified?
%i(user project feature_group).any? { |key| params.key?(key) } %i(user project group feature_group).any? { |key| params.key?(key) }
end end
def targets def targets
[feature_group, user, project].compact [feature_group, user, project, group].compact
end end
private private
...@@ -139,5 +139,11 @@ class Feature ...@@ -139,5 +139,11 @@ class Feature
Project.find_by_full_path(params[:project]) Project.find_by_full_path(params[:project])
end end
def group
return unless params.key?(:group)
Group.find_by_full_path(params[:group])
end
end end
end end
...@@ -186,13 +186,14 @@ describe Feature do ...@@ -186,13 +186,14 @@ describe Feature do
describe Feature::Target do describe Feature::Target do
describe '#targets' do describe '#targets' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:group) { create(:group) }
let(:user_name) { project.owner.username } let(:user_name) { project.owner.username }
subject { described_class.new(user: user_name, project: project.full_path) } subject { described_class.new(user: user_name, project: project.full_path, group: group.full_path) }
it 'returns all found targets' do it 'returns all found targets' do
expect(subject.targets).to be_an(Array) expect(subject.targets).to be_an(Array)
expect(subject.targets).to eq([project.owner, project]) expect(subject.targets).to eq([project.owner, project, group])
end end
end end
end end
......
...@@ -163,6 +163,40 @@ describe API::Features do ...@@ -163,6 +163,40 @@ describe API::Features do
end end
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(201)
expect(json_response).to eq(
'name' => 'my_feature',
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["Group:#{group.id}"] }
])
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(201)
expect(json_response).to eq(
"name" => "my_feature",
"state" => "off",
"gates" => [
{ "key" => "boolean", "value" => false }
]
)
end
end
end
it 'creates a feature with the given percentage if passed an integer' do it 'creates a feature with the given percentage if passed an integer' do
post api("/features/#{feature_name}", admin), params: { value: '50' } post api("/features/#{feature_name}", admin), params: { value: '50' }
......
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