diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 031910b1cdb39474692c98d9f25764982ecdac65..fd537e520c7b14cc730abc8bb295dc0a1cd75444 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -16,6 +16,7 @@ import {
featureAccessLevel,
CVE_ID_REQUEST_BUTTON_I18N,
featureAccessLevelDescriptions,
+ modelExperimentsHelpPath,
} from '../constants';
import { toggleHiddenClassBySelector } from '../external';
import ProjectFeatureSetting from './project_feature_setting.vue';
@@ -57,6 +58,10 @@ export default {
packageRegistryForEveryoneLabel: s__(
'ProjectSettings|Allow anyone to pull from Package Registry',
),
+ modelExperimentsLabel: s__('ProjectSettings|Model experiments'),
+ modelExperimentsHelpText: s__(
+ 'ProjectSettings|Track machine learning model experiments and artefacts.',
+ ),
pagesLabel: s__('ProjectSettings|Pages'),
ciCdLabel: __('CI/CD'),
repositoryLabel: s__('ProjectSettings|Repository'),
@@ -77,6 +82,7 @@ export default {
VISIBILITY_LEVEL_PRIVATE_INTEGER,
VISIBILITY_LEVEL_INTERNAL_INTEGER,
VISIBILITY_LEVEL_PUBLIC_INTEGER,
+ modelExperimentsHelpPath,
components: {
ProjectFeatureSetting,
@@ -246,6 +252,7 @@ export default {
forkingAccessLevel: featureAccessLevel.EVERYONE,
mergeRequestsAccessLevel: featureAccessLevel.EVERYONE,
packageRegistryAccessLevel: featureAccessLevel.EVERYONE,
+ modelExperimentsAccessLevel: featureAccessLevel.EVERYONE,
buildsAccessLevel: featureAccessLevel.EVERYONE,
wikiAccessLevel: featureAccessLevel.EVERYONE,
snippetsAccessLevel: featureAccessLevel.EVERYONE,
@@ -392,6 +399,10 @@ export default {
) {
this.packageRegistryAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
}
+ this.modelExperimentsAccessLevel = Math.min(
+ featureAccessLevel.PROJECT_MEMBERS,
+ this.wikiAccessLevel,
+ );
this.wikiAccessLevel = Math.min(featureAccessLevel.PROJECT_MEMBERS, this.wikiAccessLevel);
this.snippetsAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
@@ -458,6 +469,8 @@ export default {
this.buildsAccessLevel = featureAccessLevel.EVERYONE;
if (this.wikiAccessLevel > featureAccessLevel.NOT_ENABLED)
this.wikiAccessLevel = featureAccessLevel.EVERYONE;
+ if (this.modelExperimentsAccessLevel > featureAccessLevel.NOT_ENABLED)
+ this.modelExperimentsAccessLevel = featureAccessLevel.EVERYONE;
if (this.snippetsAccessLevel > featureAccessLevel.NOT_ENABLED)
this.snippetsAccessLevel = featureAccessLevel.EVERYONE;
if (this.pagesAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
@@ -898,6 +911,19 @@ export default {
name="project[project_feature_attributes][package_registry_access_level]"
/>
+
+
+
do
if ::Gitlab.config.packages.enabled
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 47d8d0eef3e30713b022329abf8819fe65cee767..12d177513536e6d895f08fbf19260ab8c66d3871 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -162,6 +162,11 @@ class ProjectPolicy < BasePolicy
with_scope :subject
condition(:service_desk_enabled) { @subject.service_desk_enabled? }
+ with_scope :subject
+ condition(:model_experiments_enabled) do
+ Feature.enabled?(:ml_experiment_tracking, @subject) && @subject.feature_available?(:model_experiments, @user)
+ end
+
with_scope :subject
condition(:resource_access_token_feature_available) do
resource_access_token_feature_available?
@@ -220,6 +225,7 @@ class ProjectPolicy < BasePolicy
feature_flags
releases
infrastructure
+ model_experiments
]
features.each do |f|
@@ -892,6 +898,10 @@ class ProjectPolicy < BasePolicy
enable :add_catalog_resource
end
+ rule { model_experiments_enabled }.policy do
+ enable :read_model_experiments
+ end
+
private
def user_is_user?
diff --git a/db/migrate/20230522132239_add_model_experiments_access_level_to_project_feature.rb b/db/migrate/20230522132239_add_model_experiments_access_level_to_project_feature.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a34b8a15521c0f3794453493d61f087960c1b34b
--- /dev/null
+++ b/db/migrate/20230522132239_add_model_experiments_access_level_to_project_feature.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddModelExperimentsAccessLevelToProjectFeature < Gitlab::Database::Migration[2.1]
+ OPERATIONS_DEFAULT_VALUE = 20
+
+ enable_lock_retries!
+
+ def change
+ add_column :project_features,
+ :model_experiments_access_level,
+ :integer,
+ null: false,
+ default: OPERATIONS_DEFAULT_VALUE
+ end
+end
diff --git a/db/schema_migrations/20230522132239 b/db/schema_migrations/20230522132239
new file mode 100644
index 0000000000000000000000000000000000000000..365eb1606cb4552a8335da5023b3eda92a25b01d
--- /dev/null
+++ b/db/schema_migrations/20230522132239
@@ -0,0 +1 @@
+7cda2cca39c53859e84bb7ecf3a2a9817f598486632a3cdd922dde6057b5c930
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 7fee4c30bcc80a8bf24344ae8721ba2ae4af751a..12eb34f06ffac11de9ac81acae1afcc534d2930b 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -20899,7 +20899,8 @@ CREATE TABLE project_features (
infrastructure_access_level integer DEFAULT 20 NOT NULL,
feature_flags_access_level integer DEFAULT 20 NOT NULL,
environments_access_level integer DEFAULT 20 NOT NULL,
- releases_access_level integer DEFAULT 20 NOT NULL
+ releases_access_level integer DEFAULT 20 NOT NULL,
+ model_experiments_access_level integer DEFAULT 20 NOT NULL
);
CREATE SEQUENCE project_features_id_seq
diff --git a/lib/api/ml/mlflow/entrypoint.rb b/lib/api/ml/mlflow/entrypoint.rb
index 880b1efeb5a763968b3cf3617a80e64944c8c4f3..048234eccd1d4fb4d40dd0d841ae29718b7c50fa 100644
--- a/lib/api/ml/mlflow/entrypoint.rb
+++ b/lib/api/ml/mlflow/entrypoint.rb
@@ -26,7 +26,7 @@ class Entrypoint < ::API::Base
authenticate!
- not_found! unless Feature.enabled?(:ml_experiment_tracking, user_project)
+ not_found! unless can?(current_user, :read_model_experiments, user_project)
end
rescue_from ActiveRecord::ActiveRecordError do |e|
diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb
index 39adbdce63b8bd22bb22449ae35be67f5da0ba8f..f41b7ce1a7307c364bbb47dc60fc5597ed031fa4 100644
--- a/lib/sidebars/projects/menus/packages_registries_menu.rb
+++ b/lib/sidebars/projects/menus/packages_registries_menu.rb
@@ -91,7 +91,7 @@ def harbor_registry_menu_item
end
def model_experiments_menu_item
- if Feature.disabled?(:ml_experiment_tracking, context.project)
+ unless can?(context.current_user, :read_model_experiments, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :model_experiments)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8df4d6eeffbe29eed6778fb40c662b710d40eae2..b7c9ae71552427fcc14786c0a8774ffc387c9f22 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -35608,6 +35608,9 @@ msgstr ""
msgid "ProjectSettings|Merging is only allowed when the source branch is up-to-date with its target."
msgstr ""
+msgid "ProjectSettings|Model experiments"
+msgstr ""
+
msgid "ProjectSettings|Monitor"
msgstr ""
@@ -35791,6 +35794,9 @@ msgstr ""
msgid "ProjectSettings|Topics are publicly visible even on private projects. Do not include sensitive information in topic names. %{linkStart}Learn more%{linkEnd}."
msgstr ""
+msgid "ProjectSettings|Track machine learning model experiments and artefacts."
+msgstr ""
+
msgid "ProjectSettings|Transfer project"
msgstr ""
diff --git a/rubocop/cop/gitlab/feature_available_usage.rb b/rubocop/cop/gitlab/feature_available_usage.rb
index df4409c27e051a5e18f4f7ff5148f52cee565bb6..cbfb89d3053c5ee794714d4ada32f8887707023c 100644
--- a/rubocop/cop/gitlab/feature_available_usage.rb
+++ b/rubocop/cop/gitlab/feature_available_usage.rb
@@ -28,6 +28,7 @@ class FeatureAvailableUsage < RuboCop::Cop::Base
feature_flags
releases
infrastructure
+ model_experiments
].freeze
EE_FEATURES = %i[requirements].freeze
ALL_FEATURES = (FEATURES + EE_FEATURES).freeze
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index d1c4cbbe59113e95109c58fe321fb6a31ae67c13..2d22cca463c5f86c94b4d7cd7f69b086e4d329d6 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -985,6 +985,7 @@ def update_project_feature
releases_access_level
monitor_access_level
infrastructure_access_level
+ model_experiments_access_level
]
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 1b485e47127a516353da483c261ad9e2e60042ea..6e3e119ddabae9b0a845247d39e7b54217f32c45 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -42,6 +42,7 @@
feature_flags_access_level { ProjectFeature::ENABLED }
releases_access_level { ProjectFeature::ENABLED }
infrastructure_access_level { ProjectFeature::ENABLED }
+ model_experiments_access_level { ProjectFeature::ENABLED }
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first
diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
index a7a1e649cd00ed04c3efa3b8cdcc69cb8ef3f191..28287d0d9c27f830e758e8e5ea32c4d19b777d78 100644
--- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
+++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
@@ -139,6 +139,8 @@ describe('Settings Panel', () => {
const findMonitorSettings = () => wrapper.findComponent({ ref: 'monitor-settings' });
const findMonitorVisibilityInput = () =>
findMonitorSettings().findComponent(ProjectFeatureSetting);
+ const findModelExperimentsSettings = () =>
+ wrapper.findComponent({ ref: 'model-experiments-settings' });
describe('Project Visibility', () => {
it('should set the project visibility help path', () => {
@@ -802,4 +804,11 @@ describe('Settings Panel', () => {
expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.EVERYONE);
});
});
+ describe('Model experiments', () => {
+ wrapper = mountComponent({});
+
+ it('shows model experiments toggle', () => {
+ expect(findModelExperimentsSettings().exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 3eb1090c9dcfed4a9f3384420ccb0f165099801a..04c8e79d7d6d84df7ae570b55c1ba176ebf2f615 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -1045,7 +1045,8 @@ def license_name
environmentsAccessLevel: project.project_feature.environments_access_level,
featureFlagsAccessLevel: project.project_feature.feature_flags_access_level,
releasesAccessLevel: project.project_feature.releases_access_level,
- infrastructureAccessLevel: project.project_feature.infrastructure_access_level
+ infrastructureAccessLevel: project.project_feature.infrastructure_access_level,
+ modelExperimentsAccessLevel: project.project_feature.model_experiments_access_level
)
end
diff --git a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
index 860206dc6af9f3d6879c8c5d62932d784c9413de..b917208bac1b5fd39576b3899bdaacfa5300e946 100644
--- a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
@@ -185,18 +185,25 @@
describe 'Model experiments' do
let(:item_id) { :model_experiments }
- context 'when :ml_experiment_tracking is enabled' do
- it 'shows the menu item' do
- stub_feature_flags(ml_experiment_tracking: true)
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_model_experiments, project)
+ .and_return(model_experiments_enabled)
+ end
+
+ context 'when user can access model experiments' do
+ let(:model_experiments_enabled) { true }
+ it 'shows the menu item' do
is_expected.not_to be_nil
end
end
- context 'when :ml_experiment_tracking is disabled' do
- it 'does not show the menu item' do
- stub_feature_flags(ml_experiment_tracking: false)
+ context 'when user does not have access model experiments' do
+ let(:model_experiments_enabled) { false }
+ it 'does not show the menu item' do
is_expected.to be_nil
end
end
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 87bfdd1577351441f5663ec4cd0649c53dfbdd48..03aee3ade8d65bc47a9264815938d45844bcce4a 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -30,6 +30,7 @@
specify { expect(subject.releases_access_level).to eq(ProjectFeature::ENABLED) }
specify { expect(subject.package_registry_access_level).to eq(ProjectFeature::ENABLED) }
specify { expect(subject.container_registry_access_level).to eq(ProjectFeature::ENABLED) }
+ specify { expect(subject.model_experiments_access_level).to eq(ProjectFeature::ENABLED) }
end
describe 'PRIVATE_FEATURES_MIN_ACCESS_LEVEL_FOR_PRIVATE_PROJECT' do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 32158ef9509199c60c78f9ea9c89a54b554ecd8f..7845c56538ef1988f5b837651d39160dd375f7ca 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1057,6 +1057,7 @@
it { is_expected.to delegate_method(:container_registry_enabled?).to(:project_feature) }
it { is_expected.to delegate_method(:container_registry_access_level).to(:project_feature) }
it { is_expected.to delegate_method(:environments_access_level).to(:project_feature) }
+ it { is_expected.to delegate_method(:model_experiments_access_level).to(:project_feature) }
it { is_expected.to delegate_method(:feature_flags_access_level).to(:project_feature) }
it { is_expected.to delegate_method(:releases_access_level).to(:project_feature) }
it { is_expected.to delegate_method(:infrastructure_access_level).to(:project_feature) }
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index ae2a11bdbf0a1b24d3f8cd1d9fb7f1295eba5306..ed78599caa11db198e68163bae8ec4b20ce8b94e 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -3263,6 +3263,32 @@ def permissions_abilities(role)
specify { is_expected.to be_disallowed(:read_namespace_catalog) }
end
+ describe ':read_model_experiments' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:ff_ml_experiment_tracking, :current_user, :access_level, :allowed) do
+ false | ref(:owner) | Featurable::ENABLED | false
+ true | ref(:guest) | Featurable::ENABLED | true
+ true | ref(:guest) | Featurable::PRIVATE | true
+ true | ref(:guest) | Featurable::DISABLED | false
+ true | ref(:non_member) | Featurable::ENABLED | true
+ true | ref(:non_member) | Featurable::PRIVATE | false
+ true | ref(:non_member) | Featurable::DISABLED | false
+ end
+ with_them do
+ before do
+ stub_feature_flags(ml_experiment_tracking: ff_ml_experiment_tracking)
+ project.project_feature.update!(model_experiments_access_level: access_level)
+ end
+
+ if params[:allowed]
+ it { is_expected.to be_allowed(:read_model_experiments) }
+ else
+ it { is_expected.not_to be_allowed(:read_model_experiments) }
+ end
+ end
+ end
+
private
def project_subject(project_type)
diff --git a/spec/requests/api/ml/mlflow/experiments_spec.rb b/spec/requests/api/ml/mlflow/experiments_spec.rb
index 1a2577e69e7775d18226f3cb10fc9c2f3a0e99f5..fc2e814752c09f40e418bd42a125f9e21756103e 100644
--- a/spec/requests/api/ml/mlflow/experiments_spec.rb
+++ b/spec/requests/api/ml/mlflow/experiments_spec.rb
@@ -20,7 +20,6 @@
end
let(:current_user) { developer }
- let(:ff_value) { true }
let(:access_token) { tokens[:write] }
let(:headers) { { 'Authorization' => "Bearer #{access_token.token}" } }
let(:project_id) { project.id }
@@ -52,10 +51,6 @@
response
end
- before do
- stub_feature_flags(ml_experiment_tracking: ff_value)
- end
-
describe 'GET /projects/:id/ml/mlflow/api/2.0/mlflow/experiments/get' do
let(:experiment_iid) { experiment.iid.to_s }
let(:route) { "/projects/#{project_id}/ml/mlflow/api/2.0/mlflow/experiments/get?experiment_id=#{experiment_iid}" }
diff --git a/spec/requests/api/ml/mlflow/runs_spec.rb b/spec/requests/api/ml/mlflow/runs_spec.rb
index 746372b79786bb7f1fcd9666417a7d0a06a4b120..a85fe4d867a434495b8cd3da1ad0fb2bc26a8ebd 100644
--- a/spec/requests/api/ml/mlflow/runs_spec.rb
+++ b/spec/requests/api/ml/mlflow/runs_spec.rb
@@ -26,7 +26,6 @@
end
let(:current_user) { developer }
- let(:ff_value) { true }
let(:access_token) { tokens[:write] }
let(:headers) { { 'Authorization' => "Bearer #{access_token.token}" } }
let(:project_id) { project.id }
@@ -40,10 +39,6 @@
response
end
- before do
- stub_feature_flags(ml_experiment_tracking: ff_value)
- end
-
RSpec.shared_examples 'MLflow|run_id param error cases' do
context 'when run id is not passed' do
let(:params) { {} }
diff --git a/spec/requests/projects/ml/candidates_controller_spec.rb b/spec/requests/projects/ml/candidates_controller_spec.rb
index 78c8e99e3f34fb0d7486516fb76de83f75ab4ee2..eec7af990637dc5f5f66984298f3a28f1e73f9f1 100644
--- a/spec/requests/projects/ml/candidates_controller_spec.rb
+++ b/spec/requests/projects/ml/candidates_controller_spec.rb
@@ -10,11 +10,13 @@
let(:ff_value) { true }
let(:candidate_iid) { candidate.iid }
+ let(:model_experiments_enabled) { true }
before do
- stub_feature_flags(ml_experiment_tracking: false)
- stub_feature_flags(ml_experiment_tracking: project) if ff_value
-
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_model_experiments, project)
+ .and_return(model_experiments_enabled)
sign_in(user)
end
@@ -32,9 +34,9 @@
end
end
- shared_examples '404 if feature flag disabled' do
- context 'when :ml_experiment_tracking disabled' do
- let(:ff_value) { false }
+ shared_examples '404 when model experiments is unavailable' do
+ context 'when user does not have access' do
+ let(:model_experiments_enabled) { false }
it_behaves_like 'renders 404'
end
@@ -59,7 +61,7 @@
end
it_behaves_like '404 if candidate does not exist'
- it_behaves_like '404 if feature flag disabled'
+ it_behaves_like '404 when model experiments is unavailable'
end
describe 'DELETE #destroy' do
@@ -81,7 +83,7 @@
end
it_behaves_like '404 if candidate does not exist'
- it_behaves_like '404 if feature flag disabled'
+ it_behaves_like '404 when model experiments is unavailable'
end
private
diff --git a/spec/requests/projects/ml/experiments_controller_spec.rb b/spec/requests/projects/ml/experiments_controller_spec.rb
index 5a8496a250a486698e9e292fd64065e1ee92fed3..e2d26e84f752d8e09b1da3b4bf509bf1de6167b9 100644
--- a/spec/requests/projects/ml/experiments_controller_spec.rb
+++ b/spec/requests/projects/ml/experiments_controller_spec.rb
@@ -3,27 +3,25 @@
require 'spec_helper'
RSpec.describe Projects::Ml::ExperimentsController, feature_category: :mlops do
- let_it_be(:project_with_feature) { create(:project, :repository) }
- let_it_be(:user) { project_with_feature.first_owner }
- let_it_be(:project_without_feature) do
- create(:project, :repository).tap { |p| p.add_developer(user) }
- end
-
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.first_owner }
let_it_be(:experiment) do
- create(:ml_experiments, project: project_with_feature, user: user).tap do |e|
+ create(:ml_experiments, project: project, user: user).tap do |e|
create(:ml_candidates, experiment: e, user: user)
end
end
let(:params) { basic_params }
let(:ff_value) { true }
- let(:project) { project_with_feature }
let(:basic_params) { { namespace_id: project.namespace.to_param, project_id: project } }
let(:experiment_iid) { experiment.iid }
+ let(:model_experiments_enabled) { true }
before do
- stub_feature_flags(ml_experiment_tracking: false)
- stub_feature_flags(ml_experiment_tracking: project_with_feature) if ff_value
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_model_experiments, project)
+ .and_return(model_experiments_enabled)
sign_in(user)
end
@@ -42,9 +40,9 @@
end
end
- shared_examples '404 if feature flag disabled' do
- context 'when :ml_experiment_tracking disabled' do
- let(:ff_value) { false }
+ shared_examples '404 when model experiments is unavailable' do
+ context 'when user does not have access' do
+ let(:model_experiments_enabled) { false }
it_behaves_like 'renders 404'
end
@@ -71,7 +69,7 @@
describe 'pagination' do
let_it_be(:experiments) do
- create_list(:ml_experiments, 3, project: project_with_feature)
+ create_list(:ml_experiments, 3, project: project)
end
let(:params) { basic_params.merge(id: experiment.iid) }
@@ -102,19 +100,7 @@
end
end
- context 'when :ml_experiment_tracking is disabled for the project' do
- let(:project) { project_without_feature }
-
- before do
- list_experiments
- end
-
- it 'responds with a 404' do
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- it_behaves_like '404 if feature flag disabled' do
+ it_behaves_like '404 when model experiments is unavailable' do
before do
list_experiments
end
@@ -225,7 +211,7 @@
end
it_behaves_like '404 if experiment does not exist'
- it_behaves_like '404 if feature flag disabled'
+ it_behaves_like '404 when model experiments is unavailable'
end
end
@@ -257,14 +243,14 @@
end
it_behaves_like '404 if experiment does not exist'
- it_behaves_like '404 if feature flag disabled'
+ it_behaves_like '404 when model experiments is unavailable'
end
end
end
describe 'DELETE #destroy' do
let_it_be(:experiment_for_deletion) do
- create(:ml_experiments, project: project_with_feature, user: user).tap do |e|
+ create(:ml_experiments, project: project, user: user).tap do |e|
create(:ml_candidates, experiment: e, user: user)
end
end
@@ -282,7 +268,7 @@
end
it_behaves_like '404 if experiment does not exist'
- it_behaves_like '404 if feature flag disabled'
+ it_behaves_like '404 when model experiments is unavailable'
end
private
diff --git a/spec/support/shared_examples/requests/api/ml/mlflow/mlflow_shared_examples.rb b/spec/support/shared_examples/requests/api/ml/mlflow/mlflow_shared_examples.rb
index 2ca62698dafd29f5aabd8d4ad4d94cd6c7b78d12..f2c38d70508cab49f9b0eace14e6bb07d89e705a 100644
--- a/spec/support/shared_examples/requests/api/ml/mlflow/mlflow_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/ml/mlflow/mlflow_shared_examples.rb
@@ -47,8 +47,13 @@
end
end
- context 'when ff is disabled' do
- let(:ff_value) { false }
+ context 'when model experiments is unavailable' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(current_user, :read_model_experiments, project)
+ .and_return(false)
+ end
it "is Not Found" do
is_expected.to have_gitlab_http_status(:not_found)